open_api_import 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 378a413d85facc074f8b7e8f1231c4fb34fa22b59485ab68be86a9634ef6152c
4
+ data.tar.gz: e7f07d0d064edf63db793934c300e4476244533d9efc7b28db65be72de720aa9
5
+ SHA512:
6
+ metadata.gz: bbb86c25b23282f190f62973f012d12b8c0323b2c3a925b70912ecc1b1219514d79dee7a43bb1d4978d5fdb5bef3ba86908e23c675118b95c8fd30c32873f6f0
7
+ data.tar.gz: b3312d3571d8d95fcbaf97d535e4090345cc94877f61e8878ccd21a3f8958499b3b51be44f1bcac866f654e9ccf6d1eb938ecfe2624e8b3a9d6865ac61f4af08
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --readme README.md
2
+ --title 'open_api_import - OpenApiImport -- Import a swagger or Open API file and create a Ruby Request Hash file including all requests and responses'
3
+ --charset utf-8
4
+ --markup markdown
5
+ 'lib/*.rb' - '*.md' - 'LICENSE'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Mario Ruiz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,405 @@
1
+ # OpenApiImport
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/open_api_import.svg)](https://rubygems.org/gems/open_api_import)
4
+ [![Build Status](https://travis-ci.com/MarioRuiz/open_api_import.svg?branch=master)](https://github.com/MarioRuiz/open_api_import)
5
+ [![Coverage Status](https://coveralls.io/repos/github/MarioRuiz/open_api_import/badge.svg?branch=master)](https://coveralls.io/github/MarioRuiz/open_api_import?branch=master)
6
+
7
+ Import a Swagger or Open API file and create a Ruby Request Hash file including all requests and responses with all the examples. The file can be in JSON or YAML.
8
+
9
+ The Request Hash will include also the pattern (regular expressions) of the fields, parameters, default values...
10
+
11
+ On this first preliminary version we only fully support Open API v2. Open API v3 is not fully supported yet.
12
+
13
+ The output of this gem will be following the specification of Request Hashes: https://github.com/MarioRuiz/Request-Hash
14
+
15
+ The Request Hashes generated will be able to be used with any Ruby Http Client and it is adapted even better with nice_http gem: https://github.com/MarioRuiz/nice_http
16
+
17
+ To be able to generate random requests take a look at the documentation for nice_hash gem: https://github.com/MarioRuiz/nice_hash
18
+
19
+ This is an example of a generated request hash:
20
+
21
+ ```ruby
22
+
23
+ # operationId: addPet, method: post
24
+ # summary: Example
25
+ # description: Creates a new pet in the store. Duplicates are allowed
26
+ # required data: name
27
+ def self.add_pet()
28
+ {
29
+ path: "/api/pets",
30
+ data_required: [
31
+ :name,
32
+ ],
33
+ data_examples: [
34
+ {
35
+ name: "",
36
+ tag: "",
37
+ },
38
+ ],
39
+ responses: {
40
+ '200': {message: "pet response"},
41
+ 'default': {message: "unexpected error"},
42
+ },
43
+ }
44
+ end
45
+
46
+
47
+ ```
48
+
49
+ ## Installation
50
+
51
+ Install it yourself as:
52
+
53
+ $ gem install open_api_import
54
+
55
+
56
+ Take in consideration open_api_import gem is using the 'rufo' gem that executes in command line the `rufo` command. In case you experience any trouble with it, visit: https://github.com/ruby-formatter/rufo
57
+
58
+ ## Usage
59
+
60
+ You have all the json and yaml examples that the Open API project supplies on /spec/fixtures/ folder. You can use any of those ones or your own Swagger or Open API file. To convert the Swagger or Open API file into a Request Hash:
61
+
62
+ ```ruby
63
+ require 'open_api_import'
64
+
65
+ OpenApiImport.from "./spec/fixtures/v2.0/yaml/uber.yaml"
66
+
67
+ OpenApiImport.from "my_file.json"
68
+
69
+ ```
70
+
71
+ The supported HTTP methods are: `GET`, `POST`, `PUT`, `DELETE` and `PATCH`
72
+
73
+ The requests will be organized by modules generated from the content in the Swagger file.
74
+ For example this would be generated when run this: `OpenApiImport.from "./spec/fixtures/v2.0/yaml/petstore-simple.yaml"`
75
+
76
+ ```ruby
77
+ ##################################################
78
+ # Swagger Petstore
79
+ # version: 1.0.0
80
+ # description:
81
+ # A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification
82
+ ##################################################
83
+ module Swagger
84
+ module SwaggerPetstore
85
+ module V1_0_0
86
+ module Pets
87
+ # operationId: findPetById, method: get
88
+ # summary:
89
+ # description:
90
+ # Returns a user based on a single ID, if the user does not have access to the pet
91
+ # parameters description:
92
+ # id: (integer) ID of pet to fetch
93
+ def self.find_pet_by_id(id)
94
+ {
95
+ path: "/api/pets/#{id}",
96
+ method: :get,
97
+ responses: {
98
+ ...
99
+ ...
100
+
101
+ ```
102
+
103
+ ## Parameters
104
+
105
+ The parameters can be supplied alone or with other parameters. In case a parameter is not supplied then it will be used the default value.
106
+
107
+ ### create_method_name
108
+
109
+ How the name of the methods will be generated.
110
+
111
+ Accepts three different options: :path, :operation_id and :operationId. By default :operation_id.
112
+
113
+ path: it will be used the path and http method, for example for a GET on path: /users/list, the method name will be get_users_list
114
+
115
+ operation_id: it will be used the operationId field but using the snake_case version, for example for listUsers: list_users
116
+
117
+ operationId: it will be used the operationId field like it is, for example: listUsers
118
+
119
+ ```ruby
120
+ require 'open_api_import'
121
+
122
+ OpenApiImport.from "./spec/fixtures/v2.0/yaml/petstore-simple.yaml", create_method_name: :path
123
+
124
+ ```
125
+
126
+ The output will generate methods like this:
127
+
128
+ ```ruby
129
+ # operationId: findPets, method: get
130
+ # summary:
131
+ # description:
132
+ # Returns all pets from the system that the user has access to
133
+ # parameters description:
134
+ # tags: (array) tags to filter by
135
+ # limit: (integer) maximum number of results to return
136
+ def self.get_pets(tags: "", limit: "")
137
+ {
138
+ path: "/api/pets?tags=#{tags}&limit=#{limit}&",
139
+ ...
140
+ ...
141
+ ```
142
+
143
+ if create_method_name is :operation_id
144
+
145
+ ```ruby
146
+ require 'open_api_import'
147
+
148
+ OpenApiImport.from "./spec/fixtures/v2.0/yaml/petstore-simple.yaml", create_method_name: :operation_id
149
+
150
+ ```
151
+
152
+ The output will generate methods like this:
153
+
154
+ ```ruby
155
+ # operationId: findPets, method: get
156
+ # summary:
157
+ # description:
158
+ # Returns all pets from the system that the user has access to
159
+ # parameters description:
160
+ # tags: (array) tags to filter by
161
+ # limit: (integer) maximum number of results to return
162
+ def self.find_pets(tags: "", limit: "")
163
+ {
164
+ path: "/api/pets?tags=#{tags}&limit=#{limit}&",
165
+ ...
166
+ ...
167
+ ```
168
+
169
+ if create_method_name is :operationId
170
+
171
+ ```ruby
172
+ require 'open_api_import'
173
+
174
+ OpenApiImport.from "./spec/fixtures/v2.0/yaml/petstore-simple.yaml", create_method_name: :operationId
175
+
176
+ ```
177
+
178
+ The output will generate methods like this:
179
+
180
+ ```ruby
181
+ # operationId: findPets, method: get
182
+ # summary:
183
+ # description:
184
+ # Returns all pets from the system that the user has access to
185
+ # parameters description:
186
+ # tags: (array) tags to filter by
187
+ # limit: (integer) maximum number of results to return
188
+ def self.findPets(tags: "", limit: "")
189
+ {
190
+ path: "/api/pets?tags=#{tags}&limit=#{limit}&",
191
+ ...
192
+ ...
193
+ ```
194
+
195
+ ### name_for_module
196
+
197
+ How the module names will be created.
198
+
199
+ Accepts three different options: :path, :path_file and :fixed. By default :path.
200
+
201
+ path: It will be used the first folder of the path to create the module name, for example the path /users/list will be in the module Users and all the requests from all modules in the same file.
202
+
203
+ path_file: It will be used the first folder of the path to create the module name, for example the path /users/list will be in the module Users and each module will be in a new requests file.
204
+
205
+ fixed: all the requests will be under the module Requests
206
+
207
+ ```ruby
208
+ require 'open_api_import'
209
+
210
+ OpenApiImport.from "./spec/fixtures/v2.0/yaml/petstore-simple.yaml", name_for_module: :fixed
211
+
212
+ ```
213
+
214
+ It will generate just one file including all requests under the Requests module
215
+
216
+ ```ruby
217
+ module Swagger
218
+ module SwaggerPetstore
219
+ module V1_0_0
220
+ module Requests
221
+
222
+ # operationId: findPets, method: get
223
+ # summary:
224
+ # description:
225
+ # Returns all pets from the system that the user has access to
226
+ # parameters description:
227
+ # tags: (array) tags to filter by
228
+ # limit: (integer) maximum number of results to return
229
+ def self.find_pets(tags: "", limit: "")
230
+ ...
231
+ ...
232
+ ```
233
+
234
+ In case using :path
235
+
236
+ ```ruby
237
+ require 'open_api_import'
238
+
239
+ OpenApiImport.from "./spec/fixtures/v2.0/yaml/petstore-simple.yaml", name_for_module: :path
240
+
241
+ ```
242
+
243
+ It will generate just one file including every request under the module generated from the first folder of the path
244
+
245
+ ```ruby
246
+ module Swagger
247
+ module SwaggerPetstore
248
+ module V1_0_0
249
+ module Pets
250
+
251
+ # operationId: findPetById, method: get
252
+ # summary:
253
+ # description:
254
+ # Returns a user based on a single ID, if the user does not have access to the pet
255
+ # parameters description:
256
+ # id: (integer) ID of pet to fetch
257
+ def self.find_pet_by_id(id)
258
+ ...
259
+ ...
260
+ ```
261
+
262
+ In case using :path_file
263
+
264
+ ```ruby
265
+ require 'open_api_import'
266
+
267
+ OpenApiImport.from "./spec/fixtures/v2.0/yaml/petstore-simple.yaml", name_for_module: :path_file
268
+
269
+ ```
270
+
271
+ It will generate one file per module including every request under the module generated from the first folder of the path. Also it will be generated one file that will have all the `require_relative` for the generated request files.
272
+
273
+ This is the output of the run:
274
+
275
+ ```
276
+ ** Generated files that contain the code of the requests after importing the Swagger file:
277
+ - /petstore-simple.yaml_Root.rb
278
+ - /petstore-simple.yaml_Pets.rb
279
+ ** File that contains all the requires for all Request files:
280
+ - /petstore-simple.yaml.rb
281
+ ```
282
+
283
+ ### include_responses
284
+
285
+ If you want to add the examples of responses in the resultant file.
286
+
287
+ Accepts true or false, by default is true.
288
+
289
+ ```ruby
290
+ require 'open_api_import'
291
+
292
+ OpenApiImport.from "./spec/fixtures/v2.0/yaml/petstore-simple.yaml", include_responses: false
293
+
294
+ ```
295
+
296
+ A method that will be included in the output:
297
+
298
+ ```ruby
299
+ # operationId: findPetById, method: get
300
+ # summary:
301
+ # description:
302
+ # Returns a user based on a single ID, if the user does not have access to the pet
303
+ # parameters description:
304
+ # id: (integer) ID of pet to fetch
305
+ def self.find_pet_by_id(id)
306
+ {
307
+ path: "/api/pets/#{id}",
308
+ method: :get,
309
+ }
310
+ end
311
+ ```
312
+
313
+ In case we run this
314
+
315
+ ```ruby
316
+ require 'open_api_import'
317
+
318
+ OpenApiImport.from "./spec/fixtures/v2.0/yaml/petstore-simple.yaml", include_responses: true
319
+
320
+ ```
321
+
322
+ A method that will be included in the output:
323
+
324
+ ```ruby
325
+ # operationId: findPetById, method: get
326
+ # summary:
327
+ # description:
328
+ # Returns a user based on a single ID, if the user does not have access to the pet
329
+ # parameters description:
330
+ # id: (integer) ID of pet to fetch
331
+ def self.find_pet_by_id(id)
332
+ {
333
+ path: "/api/pets/#{id}",
334
+ method: :get,
335
+ responses: {
336
+ '200': {
337
+ message: "pet response",
338
+ data: {
339
+ name: "string",
340
+ tag: "string",
341
+ id: 0,
342
+ },
343
+ },
344
+ 'default': {
345
+ message: "unexpected error",
346
+ data: {
347
+ code: 0,
348
+ message: "string",
349
+ },
350
+ },
351
+ },
352
+ }
353
+ end
354
+ ```
355
+
356
+ ### mock_response
357
+
358
+ Add the first response on the request as mock_response to be used.
359
+
360
+ Admits true or false. By default false.
361
+
362
+ In case using nice_http gem: if NiceHttp.use_mocks = true will use it instead of getting the real response from the WS.
363
+
364
+ ```ruby
365
+ require 'open_api_import'
366
+
367
+ OpenApiImport.from "./spec/fixtures/v2.0/yaml/petstore.yaml", mock_response: true
368
+
369
+ ```
370
+
371
+ It will include this on the output file:
372
+
373
+ ```ruby
374
+ # operationId: listPets, method: get
375
+ # summary: List all pets
376
+ # description:
377
+ # parameters description:
378
+ # limit: (integer) How many items to return at one time (max 100)
379
+ def self.list_pets(limit: "")
380
+ {
381
+ path: "/v1/pets?limit=#{limit}&",
382
+ method: :get,
383
+ mock_response: {
384
+ code: "200",
385
+ message: "A paged array of pets",
386
+ data: [
387
+ {
388
+ id: 0,
389
+ name: "string",
390
+ tag: "string",
391
+ },
392
+ ],
393
+ },
394
+ ...
395
+ ...
396
+ ```
397
+
398
+ ## Contributing
399
+
400
+ Bug reports and pull requests are welcome on GitHub at https://github.com/marioruiz/open_api_import.
401
+
402
+
403
+ ## License
404
+
405
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,552 @@
1
+ require_relative "open_api_import/utils"
2
+
3
+ require "oas_parser"
4
+ require "rufo"
5
+ require "nice_hash"
6
+ require "logger"
7
+
8
+ class OpenApiImport
9
+ ##############################################################################################
10
+ # Import a Swagger or Open API file and create a Ruby Request Hash file including all requests and responses.
11
+ # The http methods that will be treated are: 'get','post','put','delete', 'patch'.
12
+ # @param swagger_file [String]. Path and file name. Could be absolute or relative to project root folder.
13
+ # @param include_responses [Boolean]. (default: true) if you want to add the examples of responses in the resultant file.
14
+ # @param mock_response [Boolean]. (default:false) Add the first response on the request as mock_response to be used.
15
+ # In case using nice_http gem: if NiceHttp.use_mocks = true will use it instead of getting the real response from the WS.
16
+ # @param create_method_name [Symbol]. (:path, :operation_id, :operationId) (default: operation_id). How the name of the methods will be generated.
17
+ # path: it will be used the path and http method, for example for a GET on path: /users/list, the method name will be get_users_list
18
+ # operation_id: it will be used the operationId field but using the snake_case version, for example for listUsers: list_users
19
+ # operationId: it will be used the operationId field like it is, for example: listUsers
20
+ # @param name_for_module [Symbol]. (:path, :path_file, :fixed) (default: :path). How the module names will be created.
21
+ # path: It will be used the first folder of the path to create the module name, for example the path /users/list will be in the module Users and all the requests from all modules in the same file.
22
+ # path_file: It will be used the first folder of the path to create the module name, for example the path /users/list will be in the module Users and each module will be in a new requests file.
23
+ # fixed: all the requests will be under the module Requests
24
+ ##############################################################################################
25
+ def self.from(swagger_file, create_method_name: :operation_id, include_responses: true, mock_response: false, name_for_module: :path)
26
+ begin
27
+ f = File.new("#{swagger_file}_open_api_import.log", "w")
28
+ f.sync = true
29
+ @logger = Logger.new f
30
+ rescue StandardError => e
31
+ warn "Not possible to create the Logger file"
32
+ warn e
33
+ @logger = Logger.new nil
34
+ end
35
+
36
+ begin
37
+ @logger.info "swagger_file: #{swagger_file}, include_responses: #{include_responses}, mock_response: #{mock_response}\n"
38
+ @logger.info "create_method_name: #{create_method_name}, name_for_module: #{name_for_module}\n"
39
+
40
+ file_to_convert = if swagger_file["./"].nil?
41
+ swagger_file
42
+ else
43
+ Dir.pwd.to_s + "/" + swagger_file.gsub("./", "")
44
+ end
45
+ unless File.exist?(file_to_convert)
46
+ raise "The file #{file_to_convert} doesn't exist"
47
+ end
48
+
49
+ file_errors = file_to_convert + ".errors.log"
50
+ File.delete(file_errors) if File.exist?(file_errors)
51
+ import_errors = ""
52
+
53
+ definition = OasParser::Definition.resolve(swagger_file)
54
+ raw = definition.raw.deep_symbolize_keys
55
+
56
+ if raw.key?(:openapi) && (raw[:openapi].to_f > 0)
57
+ raw[:swagger] = raw[:openapi]
58
+ end
59
+ if raw[:swagger].to_f < 2.0
60
+ raise "Unsupported Swagger version. Only versions >= 2.0 are valid."
61
+ elsif raw[:swagger].to_f >= 3.0
62
+ message = "Take in consideration Open API #{raw[:swagger]} is not fully supported for the moment while we are in preliminary version of open_api_import."
63
+ warn message
64
+ @logger.warn message
65
+ end
66
+
67
+ base_host = ""
68
+ base_path = ""
69
+
70
+ base_host = raw[:host] if raw.key?(:host)
71
+ base_path = raw[:basePath] if raw.key?(:basePath)
72
+ module_name = raw[:info][:title].camel_case
73
+ module_version = "V#{raw[:info][:version].to_s.snake_case}"
74
+
75
+ output = []
76
+ output_header = []
77
+ output_header << "#" * 50
78
+ output_header << "# #{raw[:info][:title]}"
79
+ output_header << "# version: #{raw[:info][:version]}"
80
+ output_header << "# description: "
81
+ raw[:info][:description].to_s.split("\n").each do |d|
82
+ output_header << "# #{d}" unless d == ""
83
+ end
84
+ output_header << "#" * 50
85
+
86
+ output_header << "module Swagger"
87
+ output_header << "module #{module_name}"
88
+ output_header << "module #{module_version}"
89
+ output_header << "module Requests" if name_for_module == :fixed
90
+
91
+ files = {}
92
+
93
+ module_requests = ""
94
+
95
+ definition.paths.each do |path|
96
+ raw = path.raw.deep_symbolize_keys
97
+
98
+ if raw.key?(:parameters)
99
+ raw.each do |met, cont|
100
+ if met != :parameters
101
+ if raw[met].key?(:parameters)
102
+ raw[met][:parameters] = raw[met][:parameters] & raw[:parameters]
103
+ else
104
+ raw[met][:parameters] = raw[:parameters]
105
+ end
106
+ end
107
+ end
108
+ raw.delete(:parameters)
109
+ end
110
+
111
+ raw.each do |met, cont|
112
+ if %w[get post put delete patch].include?(met.to_s.downcase)
113
+ params = []
114
+ params_path = []
115
+ params_query = []
116
+ description_parameters = []
117
+ data_required = []
118
+ data_read_only = []
119
+ data_default = []
120
+ data_examples = []
121
+ data_pattern = []
122
+ responses = []
123
+
124
+ # for the case operationId is missing
125
+ cont[:operationId] = "unknown" unless cont.key?(:operationId)
126
+
127
+ if create_method_name == :path
128
+ method_name = (met.to_s + "_" + path.path.to_s).snake_case
129
+ method_name.chop! if method_name[-1] == "_"
130
+ elsif create_method_name == :operation_id
131
+ method_name = (cont[:operationId]).to_s.snake_case
132
+ else
133
+ method_name = cont[:operationId]
134
+ end
135
+
136
+ path_txt = path.path.dup.to_s
137
+ if name_for_module == :path or name_for_module == :path_file
138
+ old_module_requests = module_requests
139
+ # to remove version from path fex: /v1/Customer
140
+ path_requests = path_txt.gsub(/^\/v[\d\.]*\//i, "")
141
+ # to remove version from path fex: /1.0/Customer
142
+ path_requests = path_requests.gsub(/^\/[\d\.]*\//i, "")
143
+ if (path_requests == path_txt) && (path_txt.scan("/").size == 1)
144
+ # no folder in path
145
+ module_requests = "Root"
146
+ else
147
+ res_path = path_requests.scan(/(\w+)/)
148
+ module_requests = res_path[0][0].camel_case
149
+ end
150
+ if old_module_requests != module_requests
151
+ output << "end" unless old_module_requests == "" or name_for_module == :path_file
152
+ if name_for_module == :path
153
+ # to add the end for the previous module unless is the first one
154
+ output << "module #{module_requests}"
155
+ else #:path_file
156
+ if old_module_requests != ""
157
+ unless files.key?(old_module_requests)
158
+ files[old_module_requests] = Array.new
159
+ end
160
+ files[old_module_requests].concat(output)
161
+ output = Array.new
162
+ end
163
+ output << "module #{module_requests}" unless files.key?(module_requests) # dont add in case already existed
164
+ end
165
+ end
166
+ end
167
+
168
+ output << ""
169
+ output << "# operationId: #{cont[:operationId]}, method: #{met}"
170
+ output << "# summary: #{cont[:summary]}"
171
+ if !cont[:description].to_s.split("\n").empty?
172
+ output << "# description: "
173
+ cont[:description].to_s.split("\n").each do |d|
174
+ output << "# #{d}" unless d == ""
175
+ end
176
+ else
177
+ output << "# description: #{cont[:description]}"
178
+ end
179
+
180
+ mock_example = []
181
+ if include_responses && cont.key?(:responses) && cont[:responses].is_a?(Hash)
182
+ cont[:responses].each do |k, v|
183
+ response_example = []
184
+
185
+ response_example = get_response_examples(v)
186
+
187
+
188
+ if !response_example.empty?
189
+ responses << "'#{k}': { "
190
+ responses << "message: '#{v[:description]}', "
191
+ responses << "data: "
192
+ responses << response_example
193
+ responses << "},"
194
+
195
+ if mock_response and mock_example.size==0
196
+ mock_example << "code: '#{k}',"
197
+ mock_example << "message: '#{v[:description]}',"
198
+ mock_example << "data: "
199
+ mock_example << response_example
200
+ end
201
+
202
+ else
203
+ responses << "'#{k}': { message: '#{v[:description]}'}, "
204
+ end
205
+
206
+ end
207
+ end
208
+
209
+ # todo: for open api 3.0 is not getting the required params
210
+ if cont.key?(:parameters) && cont[:parameters].is_a?(Array)
211
+ cont[:parameters].each do |p|
212
+ if p[:in] == "path"
213
+ if create_method_name == :operationId
214
+ params_path << p[:name]
215
+ path_txt.gsub!("{#{p[:name]}}", "\#{#{p[:name]}}")
216
+ else
217
+ params_path << p[:name].to_s.snake_case
218
+ path_txt.gsub!("{#{p[:name]}}", "\#{#{p[:name].to_s.snake_case}}")
219
+ end
220
+ if p.keys.include?(:description)
221
+ description_parameters << "# #{p[:name]}: (#{p[:type]}) #{p[:description]}"
222
+ end
223
+ elsif p[:in] == "query"
224
+ params_query << p[:name]
225
+ if p.keys.include?(:description)
226
+ description_parameters << "# #{p[:name]}: (#{p[:type]}) #{p[:description]}"
227
+ end
228
+ elsif p[:in] == "body"
229
+ if p.keys.include?(:schema)
230
+ #jal
231
+ if p[:schema].key?(:oneOf)
232
+ bodies = p[:schema][:oneOf]
233
+ elsif p[:schema].key?(:anyOf)
234
+ bodies = p[:schema][:anyOf]
235
+ elsif p[:schema].key?(:allOf)
236
+ bodies = p[:schema][:allOf]
237
+ else
238
+ bodies = [p[:schema]]
239
+ end
240
+
241
+ params_data = []
242
+
243
+ bodies.each do |body|
244
+ if body.keys.include?(:required) and body[:required].size > 0
245
+ output << "# required data: #{body[:required].join(", ")}"
246
+ data_required += body[:required]
247
+ end
248
+
249
+ if body.keys.include?(:properties) and body[:properties].size > 0
250
+ body[:properties].each { |dpk, dpv|
251
+ if dpv.keys.include?(:example)
252
+ valv = dpv[:example]
253
+ else
254
+ valv = ""
255
+ end
256
+ if dpv.keys.include?(:description)
257
+ description_parameters << "# #{dpk}: (#{dpv[:type]}) #{dpv[:description]}"
258
+ end
259
+ if dpv.keys.include?(:pattern)
260
+ data_pattern << "#{dpk}: /#{dpv[:pattern].to_s.gsub("\\/", "\/")}/"
261
+ end
262
+ if dpv.keys.include?(:readOnly) and dpv[:readOnly] == true
263
+ data_read_only << dpk
264
+ end
265
+ if dpv.keys.include?(:default)
266
+ if dpv.type != "string"
267
+ data_default << "#{dpk}: #{dpv[:default]}"
268
+ else
269
+ data_default << "#{dpk}: '#{dpv[:default]}'"
270
+ end
271
+ end
272
+
273
+ if dpv[:type].downcase == "string"
274
+ valv = '"' + valv + '"'
275
+ else
276
+ #todo: consider check default and insert it
277
+ if valv.to_s == ""
278
+ valv = '"' + valv + '"'
279
+ end
280
+ end
281
+ params_data << "#{dpk}: #{valv}"
282
+ }
283
+ if params_data.size > 0
284
+ data_examples << params_data
285
+ params_data = []
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end
291
+ end
292
+
293
+ params = params_path
294
+
295
+ unless params_query.empty?
296
+ path_txt += "?"
297
+ params_query.each do |pq|
298
+ if create_method_name == :operationId
299
+ path_txt += "#{pq}=\#{#{pq}}&"
300
+ params << "#{pq}: ''"
301
+ else
302
+ path_txt += "#{pq}=\#{#{pq.to_s.snake_case}}&"
303
+ params << "#{pq.to_s.snake_case}: ''"
304
+ end
305
+ end
306
+ end
307
+ end
308
+
309
+ if description_parameters.size > 0
310
+ output << "# parameters description: "
311
+ output << description_parameters
312
+ end
313
+
314
+ output << "def self.#{method_name} (#{params.join(", ")})"
315
+
316
+ output << "{"
317
+
318
+ output << "path: \"#{base_path}#{path_txt}\","
319
+
320
+ output << "method: :#{met}," if met.to_s != ""
321
+
322
+ unless data_required.empty?
323
+ output << "data_required: ["
324
+ output << ":#{data_required.uniq.join(", :")}"
325
+ output << "],"
326
+ end
327
+ unless data_read_only.empty?
328
+ output << "data_read_only: ["
329
+ output << ":#{data_read_only.uniq.join(", :")}"
330
+ output << "],"
331
+ end
332
+ unless data_default.empty?
333
+ output << "data_default: {"
334
+ output << data_default.join(", \n")
335
+ output << "},"
336
+ end
337
+
338
+ unless data_pattern.empty?
339
+ output << "data_pattern: {"
340
+ output << data_pattern.uniq.join(", \n")
341
+ output << "},"
342
+ end
343
+
344
+ unless data_examples.empty?
345
+ output << "data_examples: ["
346
+ data_examples.each do |data|
347
+ output << "{"
348
+ output << data.join(", \n")
349
+ output << "}, "
350
+ end
351
+ output << "],"
352
+ end
353
+
354
+ unless mock_example.empty?
355
+ output << "mock_response: {"
356
+ output << mock_example
357
+ output << "},"
358
+ end
359
+
360
+ unless responses.empty?
361
+ output << "responses: {"
362
+ output << responses
363
+ output << "},"
364
+ end
365
+
366
+ output << "}"
367
+ output << "end"
368
+ else
369
+ @logger.warn "Not imported method: #{met} for path: #{path.path} since it is not supported by OpenApiImport"
370
+ end
371
+ end
372
+ end
373
+
374
+ output_footer = []
375
+
376
+ output_footer << "end" unless (module_requests == "") && (name_for_module == :path or name_for_module == :path_file)
377
+ output_footer << "end" << "end" << "end"
378
+
379
+ if files.size == 0
380
+ output = output_header + output + output_footer
381
+ output_txt = output.join("\n")
382
+ requests_file_path = file_to_convert + ".rb"
383
+ File.open(requests_file_path, "w") { |file| file.write(output_txt) }
384
+ `rufo #{requests_file_path}`
385
+ message = "** Requests file: #{swagger_file}.rb that contains the code of the requests after importing the Swagger file"
386
+ puts message
387
+ @logger.info message
388
+ else
389
+ files[module_requests] = output #for the last one
390
+ requires_txt = ""
391
+ message = "** Generated files that contain the code of the requests after importing the Swagger file: "
392
+ puts message
393
+ @logger.info message
394
+ files.each do |mod, out_mod|
395
+ output = output_header + out_mod + output_footer
396
+ output_txt = output.join("\n")
397
+ requests_file_path = file_to_convert + "_" + mod + ".rb"
398
+ requires_txt += "require_relative '#{File.basename(swagger_file)}_#{mod}'\n"
399
+ File.open(requests_file_path, "w") { |file| file.write(output_txt) }
400
+ `rufo #{requests_file_path}`
401
+ message = " - #{requests_file_path}"
402
+ puts message
403
+ @logger.info message
404
+ end
405
+
406
+ requests_file_path = file_to_convert + ".rb"
407
+ File.open(requests_file_path, "w") { |file| file.write(requires_txt) }
408
+ `rufo #{requests_file_path}`
409
+ message = "** File that contains all the requires for all Request files: \n"
410
+ message += " - #{requests_file_path} "
411
+ puts message
412
+ @logger.info message
413
+ end
414
+
415
+ begin
416
+ res = eval(output_txt)
417
+ rescue Exception => stack
418
+ import_errors += "\n\nResult evaluating the ruby file generated: \n" + stack.to_s
419
+ end
420
+
421
+ if import_errors.to_s != ""
422
+ File.open(file_errors, "w") { |file| file.write(import_errors) }
423
+ message = "* It seems there was a problem importing the Swagger file #{file_to_convert}\n"
424
+ message += "* Take a look at the detected errors at #{file_errors}\n"
425
+ warn message
426
+ @logger.fatal message
427
+ return false
428
+ else
429
+ return true
430
+ end
431
+ rescue StandardError => stack
432
+ puts stack.message
433
+ @logger.fatal stack.message
434
+ @logger.fatal stack.backtrace
435
+ puts stack.backtrace
436
+ end
437
+ end
438
+
439
+ class << self
440
+ # Retrieve the examples from the properties hash
441
+ private def get_examples(properties)
442
+ #todo: consider using this method also to get data examples
443
+ example = []
444
+ example << "{" unless properties.empty?
445
+ properties.each do |prop, val|
446
+ if val.key?(:example)
447
+ example << if val[:example].is_a?(String)
448
+ " #{prop.to_sym}: \"#{val[:example]}\", "
449
+ else
450
+ " #{prop.to_sym}: #{val[:example]}, "
451
+ end
452
+ elsif val.key?(:type)
453
+ format = val[:format]
454
+ format = val[:type] if format.to_s == ""
455
+ case val[:type].downcase
456
+ when "string"
457
+ example << " #{prop.to_sym}: \"#{format}\", "
458
+ when "integer", "number"
459
+ example << " #{prop.to_sym}: 0, "
460
+ when "boolean"
461
+ example << " #{prop.to_sym}: true, "
462
+ when "array"
463
+ if val.key?(:items) and val[:items].key?(:enum)
464
+ if val[:items][:enum][0].is_a?(String)
465
+ example << " #{prop.to_sym}: \"" + val[:items][:enum].sample + "\", "
466
+ else
467
+ example << " #{prop.to_sym}: " + val[:items][:enum].sample + ", "
468
+ end
469
+ else
470
+ #todo: differ between response examples and data examples
471
+ example << " #{prop.to_sym}: " + get_response_examples({schema: val}).join("\n") + ", "
472
+ end
473
+ when "object"
474
+ #todo: differ between response examples and data examples
475
+ res_ex = get_response_examples({schema: val})
476
+ if res_ex.size == 0
477
+ res_ex = "{ }"
478
+ else
479
+ res_ex = res_ex.join("\n")
480
+ end
481
+ example << " #{prop.to_sym}: " + res_ex + ", "
482
+ else
483
+ example << " #{prop.to_sym}: \"#{format}\", "
484
+ end
485
+ end
486
+ end
487
+ example << "}" unless properties.empty?
488
+ example
489
+ end
490
+
491
+ # Retrieve the response examples from the hash
492
+ private def get_response_examples(v)
493
+ # TODO: take in consideration the case allOf, oneOf... schema.items.allOf[0].properties schema.items.allOf[1].properties
494
+ # example on https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v2.0/yaml/petstore-expanded.yaml
495
+ response_example = Array.new()
496
+ if v.key?(:examples) && v[:examples].is_a?(Hash) && v[:examples].key?(:'application/json')
497
+ if v[:examples][:'application/json'].is_a?(String)
498
+ response_example << v[:examples][:'application/json']
499
+ elsif v[:examples][:'application/json'].is_a?(Hash)
500
+ exs = v[:examples][:'application/json'].to_s
501
+ exs.gsub!(/:(\w+)=>/, "\n\\1: ")
502
+ response_example << exs
503
+ elsif v[:examples][:'application/json'].is_a?(Array)
504
+ response_example << "["
505
+ v[:examples][:'application/json'].each do |ex|
506
+ exs = ex.to_s
507
+ if ex.is_a?(Hash)
508
+ exs.gsub!(/:(\w+)=>/, "\n\\1: ")
509
+ end
510
+ response_example << (exs + ", ")
511
+ end
512
+ response_example << "]"
513
+ end
514
+ elsif v.key?(:schema) && v[:schema].is_a?(Hash) &&
515
+ (v[:schema].key?(:properties) ||
516
+ (v[:schema].key?(:items) && v[:schema][:items].key?(:properties)) ||
517
+ (v[:schema].key?(:items) && v[:schema][:items].key?(:allOf)) ||
518
+ v[:schema].key?(:allOf))
519
+ properties = {}
520
+ if v[:schema].key?(:properties)
521
+ properties = v[:schema][:properties]
522
+ elsif v[:schema].key?(:allOf)
523
+ v[:schema][:allOf].each do |pr|
524
+ properties.merge!(pr[:properties]) if pr.key?(:properties)
525
+ end
526
+ elsif v[:schema][:items].key?(:properties)
527
+ properties = v[:schema][:items][:properties]
528
+ response_example << "["
529
+ elsif v[:schema][:items].key?(:allOf)
530
+ v[:schema][:items][:allOf].each do |pr|
531
+ properties.merge!(pr[:properties]) if pr.key?(:properties)
532
+ end
533
+ response_example << "["
534
+ end
535
+
536
+ response_example += get_examples(properties) unless properties.empty?
537
+
538
+ unless response_example.empty?
539
+ if v[:schema].key?(:properties) || v[:schema].key?(:allOf)
540
+ #
541
+ else # array, items
542
+ response_example << "]"
543
+ end
544
+ end
545
+ elsif v.key?(:schema) and v[:schema].key?(:items) and v[:schema][:items].key?(:type)
546
+ # for the case only type supplied but nothing else for the array
547
+ response_example << "[\"#{v[:schema][:items][:type]}\"]"
548
+ end
549
+ return response_example
550
+ end
551
+ end
552
+ end
@@ -0,0 +1,22 @@
1
+ class String
2
+ ########################################################
3
+ # Convert to snake_case a string
4
+ ########################################################
5
+ def snake_case
6
+ gsub(/\W/, '_')
7
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
8
+ .gsub(/([a-z])([A-Z])/, '\1_\2')
9
+ .downcase
10
+ .gsub(/_+/, '_')
11
+ end
12
+
13
+ ########################################################
14
+ # Convert to CamelCase a string
15
+ ########################################################
16
+ def camel_case
17
+ return self if self !~ /_/ && self !~ /\s/ && self =~ /[A-Z]+.*/
18
+
19
+ gsub(/\W/, '_')
20
+ .split('_').map(&:capitalize).join
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: open_api_import
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mario Ruiz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-01-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: oas_parser
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.15'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.15.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.15'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.15.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: rufo
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.4'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.4.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.4'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.4.1
53
+ - !ruby/object:Gem::Dependency
54
+ name: nice_hash
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 1.8.1
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 1.8.1
67
+ - !ruby/object:Gem::Dependency
68
+ name: rspec
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '3.8'
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 3.8.0
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '3.8'
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 3.8.0
87
+ - !ruby/object:Gem::Dependency
88
+ name: coveralls
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: '0.8'
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 0.8.22
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.8'
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 0.8.22
107
+ description: OpenApiImport -- Import a Swagger or Open API file and create a Ruby
108
+ Request Hash file including all requests and responses with all the examples. The
109
+ file can be in JSON or YAML
110
+ email: marioruizs@gmail.com
111
+ executables: []
112
+ extensions: []
113
+ extra_rdoc_files:
114
+ - LICENSE
115
+ - README.md
116
+ files:
117
+ - ".yardopts"
118
+ - LICENSE
119
+ - README.md
120
+ - lib/open_api_import.rb
121
+ - lib/open_api_import/utils.rb
122
+ homepage: https://github.com/MarioRuiz/open_api_import
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message: Thanks for installing! Visit us on https://github.com/MarioRuiz/open_api_import
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '2.3'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.7.6
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: OpenApiImport -- Import a Swagger or Open API file and create a Ruby Request
146
+ Hash file including all requests and responses with all the examples. The file can
147
+ be in JSON or YAML
148
+ test_files: []