swagger-to-rbs 0.1.1 → 0.4.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: 54cc8e6fb12e53ddc9dfbbafaa1628cdb1059fcfef1eaadaf1457a8ee113eaf9
4
- data.tar.gz: 063253b30a32967fe2a20766885ffb32da17a2b98bc0a86846f55425b7db80b9
3
+ metadata.gz: f8ac0ad5697156071c8422296d55ea3eb34bc39e8359b8eb3b6d796a2be4d298
4
+ data.tar.gz: 2b0172153de41682a387d9909b0db5bec00cbf4f0708b4b5cdf6728289ca8d16
5
5
  SHA512:
6
- metadata.gz: e5923cfe74e6ebd320e3cf9695d8f11186cb5470fabaf301ae57d2ee1fcd129012401332728e60edfed772c3de658f503e1f64c530b2f82d191d4f2afc6324fe
7
- data.tar.gz: 733c15163c84675309f9850f503bc1dffdb251148c9de14ea3e494d5406315e6f743d49d575d71c9df6a1f05d6b2463446fbf91424d11e8ba8e4e6d32e976ca0
6
+ metadata.gz: ae3b412fa2e72e7af7d66740b68ac9ecd107a479719e80354cfb1a6896e2fa693688c65017c6c9bb9dd4492c01171d4c6ee9a5cdbe9687078abcac036e58f83e
7
+ data.tar.gz: 458ed2649c08a5019d2491887996869b3d6f7d1f701193aff13264a0aec782035ff10a049609d2a8ab15d32973ce50c8d10c3db979c9b9b37bf93367bfc40590
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)
@@ -22,6 +22,19 @@ module Swagger2Rbs
22
22
  File.write("#{file_name}.rbs", ::Swagger2Rbs.generate_rbs(name, data.dup))
23
23
  end
24
24
 
25
+ desc 'genearte-yaml', 'generate yaml file'
26
+ option :name, desc: 'Name'
27
+ option :spec, desc: 'Swagger file path'
28
+ def generate_yaml
29
+ @name = options[:name]
30
+ @swagger_path = options[:spec]
31
+ @debug = options[:debug]
32
+ swagger_spec = JSON.parse(File.read(@swagger_path))
33
+
34
+ file_name = to_underscore(name)
35
+ File.write("#{file_name}.yaml", ::Swagger2Rbs.swagger_to_rest_api_yaml(swagger_spec))
36
+ end
37
+
25
38
  private
26
39
  def fetch_data
27
40
  if swagger_path
@@ -0,0 +1,41 @@
1
+ module Swagger2Rbs
2
+ class HashHelper
3
+
4
+ # https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/hash/keys.rb#L129
5
+ def self.deep_transform_keys_in_object!(object, &block)
6
+ case object
7
+ when Hash
8
+ object.keys.each do |key|
9
+ value = object.delete(key)
10
+ object[yield(key)] = deep_transform_keys_in_object!(value, &block)
11
+ end
12
+ object
13
+ when Array
14
+ object.map! { |e| deep_transform_keys_in_object!(e, &block) }
15
+ else
16
+ object
17
+ end
18
+ end
19
+
20
+ def self.set_value(hash, key, value)
21
+ arr = key.split(".")
22
+ last_key = arr.pop()
23
+ hash.dig(*arr)[last_key] = value
24
+ hash
25
+ rescue => e
26
+ hash
27
+ end
28
+
29
+ def self.walk(hash, &block)
30
+ hash.each do |k, v|
31
+ if v.is_a?(Hash)
32
+ walk(v) do |k2, v2|
33
+ yield "#{k}.#{k2}", v2
34
+ end
35
+ else
36
+ yield k, v
37
+ end
38
+ end
39
+ end
40
+ end
41
+ 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,32 @@ module Swagger2Rbs
13
15
  def to_h
14
16
  {
15
17
  path: path_with_parameters,
16
- method: method,
17
- parameters: parameters,
18
+ http_method: method,
18
19
  parameters_for_method: parameters_for_method,
19
- parameters_typed: parameters_typed,
20
+ typed_parameters_for_method: typed_parameters_for_method,
21
+ has_body: body?,
20
22
  method_name: method_name,
21
- body: body,
22
- body_typed: body_typed,
23
23
  response_typed: response_typed,
24
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,
36
+ method: method,
37
+ path_parameters: parameters,
38
+ method_name: method_name,
39
+ body: body,
40
+ response: response_typed,
41
+ }
42
+ rescue => e
43
+ raise e, "Context: #{path} #{method} Message: #{e.message}"
25
44
  end
26
45
 
27
46
  def method_name
@@ -35,39 +54,7 @@ module Swagger2Rbs
35
54
  def parameters
36
55
  return [] unless props["parameters"]
37
56
 
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
57
+ props["parameters"]&.select{|it| it["in"] == "path"}&.map{|it| it["name"]}
71
58
  end
72
59
 
73
60
  def body
@@ -78,26 +65,12 @@ module Swagger2Rbs
78
65
  end
79
66
 
80
67
  def parameters_for_method
81
- return parameters.push("options = {}").join(", ") if (method == "get")
68
+ return parameters.push("options = {}").join(", ") if method == "get"
82
69
 
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
70
+ if body&.empty?
71
+ parameters.push("options = {}").join(", ")
72
+ else
73
+ parameters.push("body").push("options = {}").join(", ")
101
74
  end
102
75
  end
103
76
 
@@ -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,21 +1,48 @@
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
 
7
- def self.swagger_to_rest_api(swagger_spec)
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
+
27
+ def self.swagger_to_rest_api(swagger_spec, parse_method = :to_h)
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
- result << rest_data.to_h
32
+ result << rest_data.send(parse_method)
13
33
  end
14
34
  end
15
35
 
16
36
  { base_uri: swagger_spec["servers"].first["url"], endpoints: result }
17
37
  end
18
38
 
39
+ def self.swagger_to_rest_api_yaml(swagger_spec)
40
+ response = swagger_to_rest_api(swagger_spec, :to_yaml)
41
+ YAML.dump(
42
+ HashHelper.deep_transform_keys_in_object!(response, &:to_s)
43
+ ).gsub("---\n", "")
44
+ end
45
+
19
46
  def self.rest_api_all(spec)
20
47
  result = []
21
48
  spec[:endpoints].each do |endpoint|
@@ -1,5 +1,5 @@
1
-
2
- require 'httparty'
1
+ # require 'httparty'
2
+ require 'json'
3
3
 
4
4
  class <%= @module_name %>
5
5
  include HTTParty
@@ -11,13 +11,13 @@ class <%= @module_name %>
11
11
  end
12
12
  <%- @data[:endpoints].each do |endpoint| -%>
13
13
 
14
- <%- if endpoint[:method] == 'get' -%>
14
+ <%- unless endpoint[:has_body] -%>
15
15
  def <%= endpoint[:method_name] %>(<%= endpoint[:parameters_for_method] %>)
16
- self.class.<%= endpoint[:method] %>("<%= endpoint[:path] %>", options)
16
+ self.class.<%= endpoint[:http_method] %>("<%= endpoint[:path] %>", options)
17
17
  end
18
18
  <%- else -%>
19
19
  def <%= endpoint[:method_name] %>(<%= endpoint[:parameters_for_method] %>)
20
- self.class.<%= endpoint[:method] %>("<%= endpoint[:path] %>", { body: body.to_json }.merge(options))
20
+ self.class.<%= endpoint[:http_method] %>("<%= endpoint[:path] %>", { body: body.to_json }.merge(options))
21
21
  end
22
22
  <%- end -%>
23
23
  <%- end -%>
@@ -1,16 +1,25 @@
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
12
 
13
+ def self.headers: (untyped headers) -> void
14
+ def self.base_uri: (String uri) -> void
15
+ def self.get: (String path, untyped options) -> void
16
+ def self.post: (String path, untyped options) -> void
17
+ def self.put: (String path, untyped options) -> void
18
+ def self.delete: (String path, untyped options) -> void
19
+
7
20
  def initialize: -> void
8
21
  <%- @data[:endpoints].each do |endpoint| -%>
9
22
 
10
- <%- if endpoint[:method] == 'get' -%>
11
- def <%= endpoint[:method_name] %>(<%= endpoint[:parameters_typed] %>) -> HTTParty::Response
12
- <%- else -%>
13
- def <%= endpoint[:method_name] %>(<%= endpoint[:body_typed] %>) -> HTTParty::Response
14
- <%- end -%>
23
+ def <%= endpoint[:method_name] %>: <%= endpoint[:typed_parameters_for_method] %> -> HTTParty::Response
15
24
  <%- end -%>
16
25
  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.1
4
+ version: 0.4.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