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 +4 -4
- data/README.md +17 -7
- data/lib/swagger2_rbs/cli.rb +13 -0
- data/lib/swagger2_rbs/hash_helper.rb +41 -0
- data/lib/swagger2_rbs/rest_endpoint.rb +30 -57
- data/lib/swagger2_rbs/rest_endpoint_typed.rb +74 -0
- data/lib/swagger2_rbs.rb +30 -3
- data/lib/templates/http_client.rb.erb +5 -5
- data/lib/templates/http_client.rbs.erb +15 -6
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8ac0ad5697156071c8422296d55ea3eb34bc39e8359b8eb3b6d796a2be4d298
|
4
|
+
data.tar.gz: 2b0172153de41682a387d9909b0db5bec00cbf4f0708b4b5cdf6728289ca8d16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae3b412fa2e72e7af7d66740b68ac9ecd107a479719e80354cfb1a6896e2fa693688c65017c6c9bb9dd4492c01171d4c6ee9a5cdbe9687078abcac036e58f83e
|
7
|
+
data.tar.gz: 458ed2649c08a5019d2491887996869b3d6f7d1f701193aff13264a0aec782035ff10a049609d2a8ab15d32973ce50c8d10c3db979c9b9b37bf93367bfc40590
|
data/README.md
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
|
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
|
-
|
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
|
28
|
-
self.class.
|
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
|
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
|
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 =
|
57
|
+
api = PetApi.new
|
48
58
|
|
49
59
|
MyApi.headers(Authorization: "Bearer #{access_token}")
|
50
60
|
MyApi.base_uri("http://otherurl.test/api/v1)
|
data/lib/swagger2_rbs/cli.rb
CHANGED
@@ -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
|
-
|
17
|
-
parameters: parameters,
|
18
|
+
http_method: method,
|
18
19
|
parameters_for_method: parameters_for_method,
|
19
|
-
|
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"]
|
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
|
68
|
+
return parameters.push("options = {}").join(", ") if method == "get"
|
82
69
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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.
|
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.
|
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 '
|
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
|
-
<%-
|
14
|
+
<%- unless endpoint[:has_body] -%>
|
15
15
|
def <%= endpoint[:method_name] %>(<%= endpoint[:parameters_for_method] %>)
|
16
|
-
self.class.<%= endpoint[:
|
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[:
|
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
|
-
|
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.
|
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
|