OpenAPITools / OpenAPITools/openapi-generator

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

Open
#14,820 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Enhancement: Feature
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

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:

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with modules/openapi-generator/src/main/resources/ruby-client/api_client_faraday_partial.mustache and trace generated Api::Client#call_api calls from methods such as CoreV1Api#list_core_v1_namespace. Review how Faraday exposes streaming callbacks and how generated methods could propagate a block. Done means Ruby clients can receive streamed response data through the generated API while preserving existing calls.

Written by the indexing model from the issue text.

Assessment

Tech stack
ruby
Domain
api
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.