Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • O openapi-generator
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 3,476
    • Issues 3,476
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 402
    • Merge requests 402
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • OpenAPI Tools
  • openapi-generator
  • Issues
  • #14820
Closed
Open
Issue created Feb 26, 2023 by Administrator@rootContributor

[REQ] [RUBY] Add support for streaming responses to ruby clients

Created by: zhulik

Is your feature request related to a problem? Please describe.

I'm currently working on a kubernetes API client for ruby using Faraday and I'd like to implement support for watching for changes. On kubernetes side this is basically implemented via streaming.

Both supported by openapi-generator http clients have support for streaming:

  • Faraday, the default adapter net_http supports streaming
  • Typhoeus

Describe the solution you'd like

Python's client for kubernetes which is also created with help of openapi-generator provides an API to make it possible:

from kubernetes import client, config, watch

# Configs can be set in Configuration class directly or using helper utility
config.load_kube_config()

v1 = client.CoreV1Api()
count = 10
w = watch.Watch()
for event in w.stream(v1.list_namespace, _request_timeout=60):
    print("Event: %s %s" % (event['type'], event['object'].metadata.name))
    count -= 1
    if not count:
        w.stop()

print("Ended.")

I've checked the generated ruby source code and couldn't find a way to hook into the request process and define a custom options.on_data callback (in case of Faraday). I assume it can be done somewhere here. The Api::Client#call_api method could receive a block and call it right after build_request. In this case all generated methods that call Api::Client#call_api should also receive a block and pass it to Api::Client#call_api. It will make it possible to hook into every single request made by the client and give enough flexibility to implement streaming. It will also be fully backwards compatible. Something like

Generated Api client:

def call_api(http_method, path, opts = {}, &block)
  begin
    response = connection(opts).public_send(http_method.to_sym.downcase) do |req|
      build_request(http_method, path, req, opts)
      block.call(http_method, path, req, opts)
    end
   ...
end

Generated client for CoreV1Api:

def list_core_v1_namespace(opts = {}, &block)
  data, _status_code, _headers = list_core_v1_namespace_with_http_info(opts, &block)
  data
end

def list_core_v1_namespace_with_http_info(opts = {}, &block)
  ...
  data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options, &block)
  ...
end

Client code, somewhat similar to python's example

v1 = CoreV1Api.new
w = Watcher.new
w.stream(v1, :list_core_v1_namespace) do |event|
  pp(event)
end

Watcher under the hood will call CoreV1Api#list_core_v1_namespace with modified arguments, pass a block into it and hook into the request process to receive the streamed response, parse it, wrap it into objects and yield it to the client.

Describe alternatives you've considered

With raw Faraday this can be achieved this way

conn = Faraday.new("http://127.0.0.1:8001") # kubectl proxy for simplicity

# inspired by https://github.com/kubernetes-client/python/blob/release-26.0/kubernetes/base/watch/watch.py#L54
prev = ""
conn.get("/api/v1/namespaces", { watch: 1 }) do |req|
  req.options.on_data = proc do |seg, _overall_received_bytes, _env|
    prev += seg
    lines = prev.split("\n")
    if seg.end_with?("\n")
      prev = ""
    else
      *lines, prev = lines
    end
    lines.each do |line|
      pp(JSON.parse(line, symbolize_names: true))
    end
  end
end

Additional context

Assignee
Assign to
Time tracking