swagger-to-rbs 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74b61b73586f11db01b0d710db9d9d37ba73ff814100cee23ed8be6be6f844bb
4
- data.tar.gz: '0649e4db09841b3681a93cb749ed43f8562271e49ac8e1e9ab4af30018404edc'
3
+ metadata.gz: 4609330c97363e8fc3f14d202f55904f026124a77a13c9e9bfcf8728a75aab42
4
+ data.tar.gz: 4ebb0337d6d72947aa3ce27ce4e2d8a99cbc63eb7f33a8015a3dccacb668696a
5
5
  SHA512:
6
- metadata.gz: dcb476b66268b52abbfb5b25746755618d4bae2af3a28a2a8ee5d2bf0c1a32c937eaf4418887bc9e77b981afe6a4a1231ba418ffd7c1bfe52e7a6b87e5083596
7
- data.tar.gz: c52a43103f947a24a7e839b3f59638a81677e850b159bd85290196c59f232424b99833fb1f5c8450523f8b3a77865562b66a49c7efedadf5f42d6a605452505e
6
+ metadata.gz: 7899e345942d1014a68dccf6a866295a2afafefabc80d4a2abc439a15ab4f6bb35d7ab708f3ef4eb7b548602902b66c850d015fae56b3b4efdc7ab3c4dca5781
7
+ data.tar.gz: 58a8e1db7ae6b58d0e3e0611c4e4b6346e3871ee5556f3f01018d812eb2027703d2bfa3733f63c346304cfbd322b68ec316c8ecb031dfdb356be8a3075a9157a
data/README.md CHANGED
@@ -1,4 +1,10 @@
1
- ### Usage
1
+ ## Install
2
+
3
+ ```
4
+ gem install swagger-to-rbs
5
+ ```
6
+
7
+ ## Usage
2
8
 
3
9
  Dowload swagger spec
4
10
 
@@ -11,7 +17,7 @@ curl https://petstore3.swagger.io/api/v3/openapi.json > swagger.json
11
17
  Generate http client based on [Httparty gem](https://github.com/jnunemaker/httparty)
12
18
 
13
19
  ```
14
- ruby lib/index.rb MyApi
20
+ swagger-to-rbs generate --name PetApi --spec swagger.json
15
21
  ```
16
22
 
17
23
  Result:
@@ -24,16 +30,20 @@ class PetApi
24
30
  base_uri "/api/v3"
25
31
  #...
26
32
 
27
- def getPetById(petId, params, options = {})
28
- self.class.get("/pet/#{petId}", params: params.to_json, headers: { 'Content-Type' => 'application/json' }.merge(options))
33
+ def initialize
34
+ self.class.headers({ 'Content-Type' => 'application/json' })
35
+ end
36
+
37
+ def getPetById(petId, options = {})
38
+ self.class.get("/pet/#{petId}", options)
29
39
  end
30
40
 
31
41
  def updatePet(body, options = {})
32
- self.class.put("/pet", body: body.to_json, headers: { 'Content-Type' => 'application/json' }.merge(options))
42
+ self.class.put("/pet", { body: body.to_json }.merge(options))
33
43
  end
34
44
 
35
45
  def addPet(body, options = {})
36
- self.class.post("/pet", body: body.to_json, headers: { 'Content-Type' => 'application/json' }.merge(options))
46
+ self.class.post("/pet", { body: body.to_json }.merge(options))
37
47
  end
38
48
  #...
39
49
  end
@@ -44,7 +54,7 @@ end
44
54
  Authorization example:
45
55
 
46
56
  ```
47
- api = MyApi.new
57
+ api = PetApi.new
48
58
 
49
59
  MyApi.headers(Authorization: "Bearer #{access_token}")
50
60
  MyApi.base_uri("http://otherurl.test/api/v1)
@@ -0,0 +1,24 @@
1
+ module Swagger2Rbs
2
+ class HashHelper
3
+ def self.set_value(hash, key, value)
4
+ arr = key.split(".")
5
+ last_key = arr.pop()
6
+ hash.dig(*arr)[last_key] = value
7
+ hash
8
+ rescue => e
9
+ hash
10
+ end
11
+
12
+ def self.walk(hash, &block)
13
+ hash.each do |k, v|
14
+ if v.is_a?(Hash)
15
+ walk(v) do |k2, v2|
16
+ yield "#{k}.#{k2}", v2
17
+ end
18
+ else
19
+ yield k, v
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,8 +1,10 @@
1
1
  require 'slugify'
2
+ require_relative 'rest_endpoint_typed'
2
3
 
3
4
  module Swagger2Rbs
4
5
  class RestEndpoint
5
6
  attr_reader :path, :method, :props
7
+ include RestEndpointTyped
6
8
 
7
9
  def initialize(path, method, props)
8
10
  @path = path
@@ -13,15 +15,31 @@ module Swagger2Rbs
13
15
  def to_h
14
16
  {
15
17
  path: path_with_parameters,
18
+ http_method: method,
19
+ parameters_for_method: parameters_for_method,
20
+ typed_parameters_for_method: typed_parameters_for_method,
21
+ has_body: body?,
22
+ method_name: method_name,
23
+ response_typed: response_typed,
24
+ }
25
+ rescue => e
26
+ raise e, "Context: #{path} #{method} Message: #{e.message}"
27
+ end
28
+
29
+ def body?
30
+ body && !body.empty?
31
+ end
32
+
33
+ def to_yaml
34
+ {
35
+ path: path,
16
36
  method: method,
17
37
  parameters: parameters,
18
- parameters_for_method: parameters_for_method,
19
- parameters_typed: parameters_typed,
20
38
  method_name: method_name,
21
39
  body: body,
22
- body_typed: body_typed,
23
- response_typed: response_typed,
24
40
  }
41
+ rescue => e
42
+ raise e, "Context: #{path} #{method} Message: #{e.message}"
25
43
  end
26
44
 
27
45
  def method_name
@@ -35,39 +53,7 @@ module Swagger2Rbs
35
53
  def parameters
36
54
  return [] unless props["parameters"]
37
55
 
38
- props["parameters"].select{|it| it["in"] == "path"}.map{|it| it["name"]}
39
- end
40
-
41
- def response_typed
42
- schema = resolve_all_of(@props.dig("responses", "200", "content", "application/json", "schema"))
43
- schema_to_typed(schema, {})
44
- end
45
-
46
- def parameters_typed
47
- return nil if method != "get"
48
- parameters.map{|it| "String #{it}" }
49
- .push("?Hash[untyped, untyped] options")
50
- .join(", ")
51
- end
52
-
53
- def body_typed
54
- return nil if method != "post"
55
-
56
- return "Hash[String, untyped]" unless body
57
- return "Hash[String, untyped]" if body.empty?
58
-
59
- "{" + body.map{ |k, v| to_typed(k, v) }.join(", ") + "}" + " body, ?Hash[untyped, untyped] options"
60
- end
61
-
62
- def to_typed(k, v)
63
- return "#{k}: #{v&.capitalize}" unless v.is_a?(Array) || v.is_a?(Hash)
64
- return "#{k}: {#{v.map{ |k2, v2| to_typed(k2, v2) }.join(", ")}}" if v.is_a?(Hash)
65
-
66
- if v[0]&.is_a?(Hash)
67
- "#{k}: Array[{" + v[0].map{ |k, v| "#{k}: #{v&.capitalize}" }.join(", ") + "}]"
68
- else
69
- "#{k}: Array[#{v[0]&.capitalize}]"
70
- end
56
+ props["parameters"]&.select{|it| it["in"] == "path"}&.map{|it| it["name"]}
71
57
  end
72
58
 
73
59
  def body
@@ -78,26 +64,12 @@ module Swagger2Rbs
78
64
  end
79
65
 
80
66
  def parameters_for_method
81
- return parameters.push("options = {}").join(", ") if (method == "get")
67
+ return parameters.push("options = {}").join(", ") if method == "get"
82
68
 
83
- parameters.push("body").push("options = {}").join(", ")
84
- end
85
-
86
- def schema_to_typed(schema, memo = {})
87
- return nil unless schema
88
-
89
- schema["properties"]&.reduce(memo)do |memo, (k,v)|
90
- if v["type"] == "object"
91
- memo.merge({k => schema_to_typed(v, {})})
92
- elsif v["type"] == "array"
93
- if v.dig("items", "type") == "object"
94
- memo.merge({k => [schema_to_typed(v["items"], {})] })
95
- else
96
- memo.merge({k => [v.dig("items", "type")] })
97
- end
98
- else
99
- memo.merge({k => v["type"] })
100
- end
69
+ if body&.empty?
70
+ parameters.push("options = {}").join(", ")
71
+ else
72
+ parameters.push("body").push("options = {}").join(", ")
101
73
  end
102
74
  end
103
75
 
@@ -0,0 +1,74 @@
1
+ module Swagger2Rbs
2
+
3
+ module RestEndpointTyped
4
+ def response_typed
5
+ schema = resolve_all_of(@props.dig("responses", "200", "content", "application/json", "schema"))
6
+ schema_to_typed(schema, {})
7
+ end
8
+
9
+ def typed_parameters_for_method
10
+ options_typed = "?Hash[untyped, untyped] options"
11
+ return "(#{options_typed})" unless body && parameters
12
+ return "(#{options_typed})" if body.empty? && parameters.empty?
13
+
14
+ typed_parameters = parameters&.map{|it| "String #{it}" } || []
15
+
16
+ typed_parameters.push("#{body_typed} body") if body?
17
+
18
+ "(#{typed_parameters.push(options_typed).join(', ')})"
19
+ end
20
+
21
+ def body_typed
22
+ return nil unless body?
23
+ typed = (body.is_a?(Array) ? body[0] : body)&.map{ |k, v| to_typed(k, v) }.join(', ')
24
+ if body.is_a?(Array)
25
+ "Array[{ #{typed} }]"
26
+ else
27
+ "{ #{typed} }"
28
+ end
29
+ end
30
+
31
+ def type_case(str)
32
+ case str
33
+ when "boolean"
34
+ "bool"
35
+ else
36
+ str&.capitalize
37
+ end
38
+ end
39
+
40
+ def to_typed(k, v)
41
+ return "#{k}: #{type_case(v)}" unless v.is_a?(Array) || v.is_a?(Hash)
42
+ return "#{k}: {#{v.map{ |k2, v2| to_typed(k2, v2) }.join(", ")}}" if v.is_a?(Hash)
43
+
44
+ if v[0]&.is_a?(Hash)
45
+ "#{k}: Array[{" + v[0].map{ |k, v| to_typed(k, v) }.join(", ") + "}]"
46
+ else
47
+ "#{k}: Array[#{type_case(v[0])}]"
48
+ end
49
+ end
50
+
51
+ def schema_to_typed(schema, memo = {})
52
+ return nil unless schema
53
+
54
+ properties = schema["type"]["array"] ? schema["items"]["properties"] : schema["properties"]
55
+
56
+ result = properties&.reduce(memo)do |memo, (k,v)|
57
+ if v["type"] == "object"
58
+ memo.merge({k => schema_to_typed(v, {})})
59
+ elsif v["type"] == "array"
60
+ if v.dig("items", "type") == "object"
61
+ memo.merge({k => [schema_to_typed(v["items"], {})] })
62
+ else
63
+ memo.merge({k => [v.dig("items", "type")] })
64
+ end
65
+ else
66
+ memo.merge({k => v["type"] })
67
+ end
68
+ end
69
+ return [result] if schema["type"]["array"]
70
+
71
+ result
72
+ end
73
+ end
74
+ end
data/lib/swagger2_rbs.rb CHANGED
@@ -1,12 +1,32 @@
1
1
  require 'json'
2
2
  require 'erb'
3
3
  require_relative 'swagger2_rbs/rest_endpoint'
4
+ require_relative 'swagger2_rbs/hash_helper'
4
5
 
5
6
  module Swagger2Rbs
6
7
 
8
+ def self.resolve_ref(hash, key, value)
9
+ ref_key = value.gsub("#/", "").split("/")
10
+ data = hash.dig(*ref_key)
11
+ update_key = key.split(".").reject{|k| k == "$ref"}.join(".")
12
+
13
+ [update_key, data]
14
+ end
15
+
16
+ def self.resolve_all_ref(swagger_spec)
17
+ new_swagger_spec = swagger_spec.dup
18
+ HashHelper.walk(swagger_spec) do |key, value|
19
+ if key.split(".").last == "$ref"
20
+ update_key, data = resolve_ref(swagger_spec, key, value)
21
+ HashHelper.set_value(new_swagger_spec, update_key, data)
22
+ end
23
+ end
24
+ new_swagger_spec
25
+ end
26
+
7
27
  def self.swagger_to_rest_api(swagger_spec)
8
28
  result = []
9
- swagger_spec["paths"].each do |path, data|
29
+ resolve_all_ref(swagger_spec)["paths"].each do |path, data|
10
30
  data.each do |method, props|
11
31
  rest_data = RestEndpoint.new(path, method, props)
12
32
  result << rest_data.to_h
@@ -1,5 +1,6 @@
1
1
 
2
2
  require 'httparty'
3
+ require 'json'
3
4
 
4
5
  class <%= @module_name %>
5
6
  include HTTParty
@@ -11,13 +12,13 @@ class <%= @module_name %>
11
12
  end
12
13
  <%- @data[:endpoints].each do |endpoint| -%>
13
14
 
14
- <%- if endpoint[:method] == 'get' -%>
15
+ <%- unless endpoint[:has_body] -%>
15
16
  def <%= endpoint[:method_name] %>(<%= endpoint[:parameters_for_method] %>)
16
- self.class.<%= endpoint[:method] %>("<%= endpoint[:path] %>", options)
17
+ self.class.<%= endpoint[:http_method] %>("<%= endpoint[:path] %>", options)
17
18
  end
18
19
  <%- else -%>
19
20
  def <%= endpoint[:method_name] %>(<%= endpoint[:parameters_for_method] %>)
20
- self.class.<%= endpoint[:method] %>("<%= endpoint[:path] %>", { body: body.to_json }.merge(options))
21
+ self.class.<%= endpoint[:http_method] %>("<%= endpoint[:path] %>", { body: body.to_json }.merge(options))
21
22
  end
22
23
  <%- end -%>
23
24
  <%- end -%>
@@ -1,17 +1,18 @@
1
-
2
1
  # Classes
3
2
 
3
+ module HTTParty
4
+ class Response
5
+ def body: -> untyped
6
+ def success?: -> bool
7
+ end
8
+ end
9
+
4
10
  class <%= @module_name %>
5
11
  include HTTParty
6
- attr_accessor default_headers: bot
7
12
 
8
13
  def initialize: -> void
9
14
  <%- @data[:endpoints].each do |endpoint| -%>
10
15
 
11
- <%- if endpoint[:method] == 'get' -%>
12
- def <%= endpoint[:method_name] %>(<%= endpoint[:parameters_typed] %>) -> HTTParty::Response
13
- <%- else -%>
14
- def <%= endpoint[:method_name] %>(<%= endpoint[:body_typed] %>) -> HTTParty::Response
15
- <%- end -%>
16
+ def <%= endpoint[:method_name] %>: <%= endpoint[:typed_parameters_for_method] %> -> HTTParty::Response
16
17
  <%- end -%>
17
18
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swagger-to-rbs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Savignano
@@ -14,14 +14,14 @@ dependencies:
14
14
  name: thor
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
@@ -50,7 +50,9 @@ files:
50
50
  - lib/index.rb
51
51
  - lib/swagger2_rbs.rb
52
52
  - lib/swagger2_rbs/cli.rb
53
+ - lib/swagger2_rbs/hash_helper.rb
53
54
  - lib/swagger2_rbs/rest_endpoint.rb
55
+ - lib/swagger2_rbs/rest_endpoint_typed.rb
54
56
  - lib/templates/http_client.rb.erb
55
57
  - lib/templates/http_client.rbs.erb
56
58
  homepage: https://github.com/MiguelSavignano/swagger-to-rbs