swagger-to-rbs 0.1.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 +7 -0
- data/README.md +53 -0
- data/bin/swagger-to-rbs +7 -0
- data/lib/index.rb +7 -0
- data/lib/swagger2_rbs/cli.rb +42 -0
- data/lib/swagger2_rbs/rest_endpoint.rb +122 -0
- data/lib/swagger2_rbs.rb +50 -0
- data/lib/templates/http_client.rb.erb +24 -0
- data/lib/templates/http_client.rbs.erb +17 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 74b61b73586f11db01b0d710db9d9d37ba73ff814100cee23ed8be6be6f844bb
|
4
|
+
data.tar.gz: '0649e4db09841b3681a93cb749ed43f8562271e49ac8e1e9ab4af30018404edc'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dcb476b66268b52abbfb5b25746755618d4bae2af3a28a2a8ee5d2bf0c1a32c937eaf4418887bc9e77b981afe6a4a1231ba418ffd7c1bfe52e7a6b87e5083596
|
7
|
+
data.tar.gz: c52a43103f947a24a7e839b3f59638a81677e850b159bd85290196c59f232424b99833fb1f5c8450523f8b3a77865562b66a49c7efedadf5f42d6a605452505e
|
data/README.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
### Usage
|
2
|
+
|
3
|
+
Dowload swagger spec
|
4
|
+
|
5
|
+
Example:
|
6
|
+
|
7
|
+
```
|
8
|
+
curl https://petstore3.swagger.io/api/v3/openapi.json > swagger.json
|
9
|
+
```
|
10
|
+
|
11
|
+
Generate http client based on [Httparty gem](https://github.com/jnunemaker/httparty)
|
12
|
+
|
13
|
+
```
|
14
|
+
ruby lib/index.rb MyApi
|
15
|
+
```
|
16
|
+
|
17
|
+
Result:
|
18
|
+
|
19
|
+
```rb
|
20
|
+
require 'httparty'
|
21
|
+
|
22
|
+
class PetApi
|
23
|
+
include HTTParty
|
24
|
+
base_uri "/api/v3"
|
25
|
+
#...
|
26
|
+
|
27
|
+
def getPetById(petId, params, options = {})
|
28
|
+
self.class.get("/pet/#{petId}", params: params.to_json, headers: { 'Content-Type' => 'application/json' }.merge(options))
|
29
|
+
end
|
30
|
+
|
31
|
+
def updatePet(body, options = {})
|
32
|
+
self.class.put("/pet", body: body.to_json, headers: { 'Content-Type' => 'application/json' }.merge(options))
|
33
|
+
end
|
34
|
+
|
35
|
+
def addPet(body, options = {})
|
36
|
+
self.class.post("/pet", body: body.to_json, headers: { 'Content-Type' => 'application/json' }.merge(options))
|
37
|
+
end
|
38
|
+
#...
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
### Send default header in all request
|
43
|
+
|
44
|
+
Authorization example:
|
45
|
+
|
46
|
+
```
|
47
|
+
api = MyApi.new
|
48
|
+
|
49
|
+
MyApi.headers(Authorization: "Bearer #{access_token}")
|
50
|
+
MyApi.base_uri("http://otherurl.test/api/v1)
|
51
|
+
|
52
|
+
api.getPetById(1)
|
53
|
+
```
|
data/bin/swagger-to-rbs
ADDED
data/lib/index.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'json'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Swagger2Rbs
|
6
|
+
class Cli < Thor
|
7
|
+
attr_reader :name, :swagger_path, :rest_api_path, :debug
|
8
|
+
|
9
|
+
desc 'genearte', 'generate http client and rbs'
|
10
|
+
option :name, desc: 'Name'
|
11
|
+
option :spec, desc: 'Swagger file path'
|
12
|
+
option :debug, desc: 'Generate debug file'
|
13
|
+
def generate
|
14
|
+
@name = options[:name]
|
15
|
+
@swagger_path = options[:spec]
|
16
|
+
@debug = options[:debug]
|
17
|
+
data = fetch_data
|
18
|
+
File.write(".rest-api.json", JSON.pretty_generate(data)) if debug
|
19
|
+
|
20
|
+
file_name = to_underscore(name)
|
21
|
+
File.write("#{file_name}.rb", ::Swagger2Rbs.generate(name, data.dup))
|
22
|
+
File.write("#{file_name}.rbs", ::Swagger2Rbs.generate_rbs(name, data.dup))
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def fetch_data
|
27
|
+
if swagger_path
|
28
|
+
swagger_spec = JSON.parse(File.read(swagger_path))
|
29
|
+
::Swagger2Rbs.swagger_to_rest_api(swagger_spec)
|
30
|
+
elsif rest_api_path
|
31
|
+
data = YAML.load_file(rest_api_path, symbolize_names: true)
|
32
|
+
::Swagger2Rbs.rest_api_all(data)
|
33
|
+
else
|
34
|
+
raise StandardError, "Missing swagger_path or rest_api_path"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_underscore(string)
|
39
|
+
string.gsub(/(.)([A-Z])/,'\1_\2').downcase
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'slugify'
|
2
|
+
|
3
|
+
module Swagger2Rbs
|
4
|
+
class RestEndpoint
|
5
|
+
attr_reader :path, :method, :props
|
6
|
+
|
7
|
+
def initialize(path, method, props)
|
8
|
+
@path = path
|
9
|
+
@method = method
|
10
|
+
@props = props || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_h
|
14
|
+
{
|
15
|
+
path: path_with_parameters,
|
16
|
+
method: method,
|
17
|
+
parameters: parameters,
|
18
|
+
parameters_for_method: parameters_for_method,
|
19
|
+
parameters_typed: parameters_typed,
|
20
|
+
method_name: method_name,
|
21
|
+
body: body,
|
22
|
+
body_typed: body_typed,
|
23
|
+
response_typed: response_typed,
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_name
|
28
|
+
props["operationId"] || path.slugify.gsub("-", "_")
|
29
|
+
end
|
30
|
+
|
31
|
+
def path_with_parameters
|
32
|
+
path.gsub("{", '#{')
|
33
|
+
end
|
34
|
+
|
35
|
+
def parameters
|
36
|
+
return [] unless props["parameters"]
|
37
|
+
|
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
|
71
|
+
end
|
72
|
+
|
73
|
+
def body
|
74
|
+
body_schema = resolve_of(props.dig("requestBody", "content", "application/json", "schema"))
|
75
|
+
return {} unless body_schema
|
76
|
+
|
77
|
+
schema_to_typed(body_schema)
|
78
|
+
end
|
79
|
+
|
80
|
+
def parameters_for_method
|
81
|
+
return parameters.push("options = {}").join(", ") if (method == "get")
|
82
|
+
|
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
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def resolve_of(data)
|
105
|
+
resolve_all_of(resolve_one_of(data))
|
106
|
+
end
|
107
|
+
|
108
|
+
def resolve_one_of(data)
|
109
|
+
return data unless data
|
110
|
+
return data unless data["oneOf"]
|
111
|
+
|
112
|
+
data["oneOf"].reduce(&:merge)
|
113
|
+
end
|
114
|
+
|
115
|
+
def resolve_all_of(data)
|
116
|
+
return data unless data
|
117
|
+
return data unless data["allOf"]
|
118
|
+
|
119
|
+
data["allOf"].reduce(&:merge)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/swagger2_rbs.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'erb'
|
3
|
+
require_relative 'swagger2_rbs/rest_endpoint'
|
4
|
+
|
5
|
+
module Swagger2Rbs
|
6
|
+
|
7
|
+
def self.swagger_to_rest_api(swagger_spec)
|
8
|
+
result = []
|
9
|
+
swagger_spec["paths"].each do |path, data|
|
10
|
+
data.each do |method, props|
|
11
|
+
rest_data = RestEndpoint.new(path, method, props)
|
12
|
+
result << rest_data.to_h
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
{ base_uri: swagger_spec["servers"].first["url"], endpoints: result }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.rest_api_all(spec)
|
20
|
+
result = []
|
21
|
+
spec[:endpoints].each do |endpoint|
|
22
|
+
rest_data = RestEndpoint.new(endpoint[:path], endpoint[:method], endpoint[:props])
|
23
|
+
result << rest_data.to_h
|
24
|
+
end
|
25
|
+
|
26
|
+
{ base_uri: spec[:base_uri], endpoints: result }
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.mock_rest_api_data
|
30
|
+
data = YAML.load_file('spec/fixtures/rest-api.yaml').transform_keys(&:to_sym)
|
31
|
+
data[:endpoints].map!{|it| it.transform_keys(&:to_sym) }
|
32
|
+
data
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.generate(name, data)
|
36
|
+
@module_name = name
|
37
|
+
@data = data
|
38
|
+
template = File.read("#{File.dirname(__dir__ )}/lib/templates/http_client.rb.erb")
|
39
|
+
|
40
|
+
ERB.new(template, nil, '-').result(binding)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.generate_rbs(name, data)
|
44
|
+
@module_name = name
|
45
|
+
@data = data
|
46
|
+
template = File.read("#{File.dirname(__dir__ )}/lib/templates/http_client.rbs.erb")
|
47
|
+
|
48
|
+
ERB.new(template, nil, '-').result(binding)
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
require 'httparty'
|
3
|
+
|
4
|
+
class <%= @module_name %>
|
5
|
+
include HTTParty
|
6
|
+
|
7
|
+
base_uri "<%= @data[:base_uri] %>"
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self.class.headers({ 'Content-Type' => 'application/json' })
|
11
|
+
end
|
12
|
+
<%- @data[:endpoints].each do |endpoint| -%>
|
13
|
+
|
14
|
+
<%- if endpoint[:method] == 'get' -%>
|
15
|
+
def <%= endpoint[:method_name] %>(<%= endpoint[:parameters_for_method] %>)
|
16
|
+
self.class.<%= endpoint[:method] %>("<%= endpoint[:path] %>", options)
|
17
|
+
end
|
18
|
+
<%- else -%>
|
19
|
+
def <%= endpoint[:method_name] %>(<%= endpoint[:parameters_for_method] %>)
|
20
|
+
self.class.<%= endpoint[:method] %>("<%= endpoint[:path] %>", { body: body.to_json }.merge(options))
|
21
|
+
end
|
22
|
+
<%- end -%>
|
23
|
+
<%- end -%>
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
# Classes
|
3
|
+
|
4
|
+
class <%= @module_name %>
|
5
|
+
include HTTParty
|
6
|
+
attr_accessor default_headers: bot
|
7
|
+
|
8
|
+
def initialize: -> void
|
9
|
+
<%- @data[:endpoints].each do |endpoint| -%>
|
10
|
+
|
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
|
+
<%- end -%>
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: swagger-to-rbs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Miguel Savignano
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-01-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: slugify
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Generate Http client and rbs files
|
42
|
+
email: migue.masx@gmail.com
|
43
|
+
executables:
|
44
|
+
- swagger-to-rbs
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- README.md
|
49
|
+
- bin/swagger-to-rbs
|
50
|
+
- lib/index.rb
|
51
|
+
- lib/swagger2_rbs.rb
|
52
|
+
- lib/swagger2_rbs/cli.rb
|
53
|
+
- lib/swagger2_rbs/rest_endpoint.rb
|
54
|
+
- lib/templates/http_client.rb.erb
|
55
|
+
- lib/templates/http_client.rbs.erb
|
56
|
+
homepage: https://github.com/MiguelSavignano/swagger-to-rbs
|
57
|
+
licenses:
|
58
|
+
- MIT
|
59
|
+
metadata: {}
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubygems_version: 3.3.3
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: Swagger to rbs
|
79
|
+
test_files: []
|