zero-rails_openapi 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,36 @@
1
+ class Api::V1::ExamplesController < Api::V1::BaseController
2
+ apis_set 'ExamplesController\'s APIs' do
3
+ schema :Dog => [ { id!: Integer, name: String }, dft: { id: 1, name: 'pet' } ]
4
+ path! :PathCompId => [ :id, Integer, desc: 'user id' ]
5
+ query! :QueryCompUuid => [ :product_uuid, { uuid: String, time: 'int32' }, desc: 'product uuid' ]
6
+ body! :RqBodyComp => [ :form ]
7
+ resp :RespComp => [ 'bad request', :json ]
8
+ end
9
+
10
+ open_api_set %i[index show], 'common response' do
11
+ response '567', 'query result export', :pdf, type: File
12
+ end
13
+
14
+ open_api :index, '(SUMMARY) this api blah blah ...' do
15
+ this_api_is_invalid! 'this api is expired!'
16
+ desc 'Optional multiline or single-line Markdown-formatted description',
17
+ id: 'user id',
18
+ email_addr: 'email_addr\'s desc'
19
+ email = 'git@github.com'
20
+
21
+ query! :id, Integer, enum: 0..5, length: [1, 2], pattern: /^[0-9]$/, range: {gt:0, le:5}
22
+ query! :done, Boolean, must_be: false, default: true, desc: 'must be false'
23
+ query :email_addr, String, lth: :ge_3, default: email # is_a: :email
24
+ # form! 'form', type: { id!: Integer, name: String }
25
+ file :pdf, 'desc: the media type is application/pdf'
26
+
27
+ response :success, 'success response', :json, type: :Dog
28
+
29
+ security :ApiKeyAuth
30
+ end
31
+
32
+ open_api :show do
33
+ param_ref :PathCompId, :QueryCompUuid
34
+ response_ref '123' => :RespComp, '223' => :RespComp
35
+ end
36
+ end
@@ -0,0 +1,87 @@
1
+ require 'open_api'
2
+
3
+ OpenApi.configure do |c|
4
+ # [REQUIRED] The location where .json doc file will be output.
5
+ c.file_output_path = 'public/open_api'
6
+
7
+ # Everything about OAS3 is on https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md
8
+ # Getting started: https://swagger.io/docs/specification/basic-structure/
9
+ c.register_apis = {
10
+ homepage_api: {
11
+ # [REQUIRED] ZRO will scan all the descendants of the root_controller, then generate their docs.
12
+ root_controller: Api::V1::BaseController,
13
+
14
+ # [REQUIRED] Info Object: The info section contains API information
15
+ info: {
16
+ # [REQUIRED] The title of the application.
17
+ title: 'Zero Rails APIs',
18
+ # Description of the application.
19
+ description: 'API documentation of Zero-Rails Application. <br/>' \
20
+ 'Optional multiline or single-line Markdown-formatted description ' \
21
+ 'in [CommonMark](http://spec.commonmark.org/) or `HTML`.',
22
+ # A URL to the Terms of Service for the API. MUST be in the format of a URL.
23
+ # termsOfService: 'http://example.com/terms/',
24
+ # Contact Object: The contact information for the exposed API.
25
+ contact: {
26
+ # The identifying name of the contact person/organization.
27
+ name: 'API Support',
28
+ # The URL pointing to the contact information. MUST be in the format of a URL.
29
+ url: 'http://www.github.com',
30
+ # The email address of the contact person/organization. MUST be in the format of an email address.
31
+ email: 'git@gtihub.com'
32
+ },
33
+ # License Object: The license information for the exposed API.
34
+ license: {
35
+ # [REQUIRED] The license name used for the API.
36
+ name: 'Apache 2.0',
37
+ # A URL to the license used for the API. MUST be in the format of a URL.
38
+ url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
39
+ },
40
+ # [REQUIRED] The version of the OpenAPI document
41
+ # (which is distinct from the OpenAPI Specification version or the API implementation version).
42
+ version: '1.0.0'
43
+ },
44
+
45
+ # An array of Server Objects, which provide connectivity information to a target server.
46
+ # If the servers property is not provided, or is an empty array,
47
+ # the default value would be a Server Object with a url value of /.
48
+ # https://swagger.io/docs/specification/api-host-and-base-path/
49
+ # The servers section specifies the API server and base URL.
50
+ # You can define one or several servers, such as production and sandbox.
51
+ servers: [{
52
+ # [REQUIRED] A URL to the target host.
53
+ # This URL supports Server Variables and MAY be relative,
54
+ # to indicate that the host location is relative to the location where
55
+ # the OpenAPI document is being served.
56
+ url: 'http://localhost:2333/api/v1',
57
+ # An optional string describing the host designated by the URL.
58
+ description: 'Optional server description, e.g. Main (production) server'
59
+ },{
60
+ url: 'http://localhost:3332/api/v1',
61
+ description: 'Optional server description, e.g. Internal staging server for testing'
62
+ }],
63
+
64
+ # Authentication
65
+ # The securitySchemes and security keywords are used to describe the authentication methods used in your API.
66
+ # Security Scheme Object: An object to hold reusable Security Scheme Objects.
67
+ global_security_schemes: {
68
+ ApiKeyAuth: {
69
+ type: 'apiKey',
70
+ name: 'server_token',
71
+ in: 'query'
72
+ }
73
+ },
74
+ # Security Requirement Object
75
+ # A declaration of which security mechanisms can be used across the API.
76
+ # The list of values includes alternative security requirement objects that can be used.
77
+ # Only one of the security requirement objects need to be satisfied to authorize a request.
78
+ # Individual operations can override this definition.
79
+ # see: https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#securityRequirementObject
80
+ global_security: [{ ApiKeyAuth: [] }],
81
+ }
82
+ }
83
+ end
84
+
85
+ Object.const_set('Boolean', 'boolean') # Support `Boolean` writing in DSL
86
+
87
+ OpenApi.write_docs # Generate doc when Rails initializing
@@ -0,0 +1,41 @@
1
+ module OpenApi
2
+ module Helpers
3
+
4
+ # TODO: comment-block doc
5
+ def truly_present?(obj)
6
+ obj == false || obj.present?
7
+ end
8
+
9
+ def value_present
10
+ Proc.new { |_, v| truly_present? v }
11
+ end
12
+
13
+ def assign(value)
14
+ @assign = value.is_a?(Symbol)? self.send("_#{value}") : value
15
+ self
16
+ end
17
+
18
+ def all(*values)
19
+ @assign = values.compact.reduce({ }, :merge).keep_if &value_present
20
+ self
21
+ end
22
+
23
+ def to_processed(who)
24
+ if who.is_a?(Symbol)
25
+ self.send("#{who}=", @assign)
26
+ else
27
+ processed[who.to_sym] = @assign
28
+ end if truly_present?(@assign)
29
+
30
+ processed
31
+ end
32
+
33
+ def to(who)
34
+ self[who.to_sym] = @assign if truly_present?(@assign)
35
+ end
36
+
37
+ def for_merge # to_processed
38
+ processed.tap { |it| it.merge! @assign if truly_present?(@assign) }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,86 @@
1
+ require 'oas_objs/schema_obj'
2
+
3
+ module OpenApi
4
+ module DSL
5
+ # https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#media-type-object
6
+ class MediaTypeObj < Hash
7
+ attr_accessor :media_type, :schema
8
+ def initialize(media_type, schema_hash)
9
+ self.media_type = media_type_mapping media_type
10
+ self.schema = SchemaObj.new(schema_hash[:type], schema_hash)
11
+ end
12
+
13
+ def process
14
+ schema_processed = self.schema.process
15
+ schema = schema_processed.values.join.blank? ? { } : { schema: schema_processed }
16
+ media_type.nil? ? { } : { media_type => schema }
17
+ end
18
+
19
+ # https://swagger.io/docs/specification/media-types/
20
+ # https://en.wikipedia.org/wiki/Media_type
21
+ # https://zh.wikipedia.org/wiki/%E4%BA%92%E8%81%94%E7%BD%91%E5%AA%92%E4%BD%93%E7%B1%BB%E5%9E%8B
22
+ # https://www.iana.org/assignments/media-types/media-types.xhtml
23
+ def media_type_mapping(media_type)
24
+ return media_type if media_type.is_a? String
25
+ case media_type
26
+ when :app; 'application/*'
27
+ when :json; 'application/json'
28
+ when :xml; 'application/xml'
29
+ when :xwww; 'application/x-www-form-urlencoded'
30
+ when :pdf; 'application/pdf'
31
+ when :zip; 'application/zip'
32
+ when :gzip; 'application/gzip'
33
+ when :doc; 'application/msword'
34
+ when :docx; 'application/application/vnd.openxmlformats-officedocument.wordprocessingml.document'
35
+ when :xls; 'application/vnd.ms-excel'
36
+ when :xlsx; 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
37
+ when :ppt; 'application/vnd.ms-powerpoint'
38
+ when :pptx; 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
39
+ # when :pdf; 'application/pdf'
40
+ when :form; 'multipart/form-data'; when :form_data; 'multipart/form-data'
41
+ when :text; 'text/*'
42
+ when :plain; 'text/plain; charset=utf-8'
43
+ when :html; 'text/html'
44
+ when :csv; 'text/csv'
45
+ when :image; 'image/*'
46
+ when :png; 'image/png'
47
+ when :jpeg; 'image/jpeg'
48
+ when :gif; 'image/gif'
49
+ when :audio; 'audio/*'
50
+ when :video; 'video/*'
51
+ else; nil
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ __END__
60
+
61
+ Media Type Examples
62
+
63
+ {
64
+ "application/json": {
65
+ "schema": {
66
+ "$ref": "#/components/schemas/Pet"
67
+ },
68
+ "examples": {
69
+ "cat" : {
70
+ "summary": "An example of a cat",
71
+ "value":
72
+ {
73
+ "name": "Fluffy",
74
+ "petType": "Cat",
75
+ "color": "White",
76
+ "gender": "male",
77
+ "breed": "Persian"
78
+ }
79
+ },
80
+ "frog": {
81
+ "$ref": "#/components/examples/frog-example"
82
+ }
83
+ }
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,83 @@
1
+ require 'oas_objs/helpers'
2
+ require 'oas_objs/schema_obj'
3
+
4
+ module OpenApi
5
+ module DSL
6
+ class ParamObj < Hash
7
+ include Helpers
8
+
9
+ attr_accessor :processed, :schema
10
+ def initialize(name, param_type, type, required, schema_hash)
11
+ self.processed = {
12
+ name: name,
13
+ in: param_type,
14
+ required: "#{required}".match?(/req/),
15
+ }
16
+ self.schema = SchemaObj.new(type, schema_hash)
17
+ self.merge! schema_hash
18
+ end
19
+
20
+ def process
21
+ assign(_desc).to_processed 'description'
22
+ processed.tap { |it| it[:schema] = schema.process_for name }
23
+ end
24
+
25
+
26
+ # Getters and Setters of the original values that was passed to param()
27
+ # This mapping allows user to select the aliases in DSL writing,
28
+ # without increasing the complexity of the implementation.
29
+ { # SELF_MAPPING
30
+ _range: %i[range number_range],
31
+ _length: %i[length lth ],
32
+ _desc: %i[desc description ],
33
+ }.each do |key, aliases|
34
+ define_method key do
35
+ aliases.each { |alias_name| self[key] ||= self[alias_name] } if self[key].nil?
36
+ self[key]
37
+ end
38
+ end
39
+
40
+
41
+ # Interfaces for directly taking the info what you focus on,
42
+ # The next step you may want to verify the parameters based on these infos.
43
+ # The implementation of the parameters validator, see:
44
+ # TODO
45
+ alias_method :range, :_range
46
+ alias_method :length, :_length
47
+ { # INTERFACE_MAPPING
48
+ name: %i[name ],
49
+ required: %i[required ],
50
+ in: %i[in ],
51
+ enum: %i[schema enum ],
52
+ pattern: %i[schema pattern],
53
+ regexp: %i[schema pattern],
54
+ type: %i[schema type ],
55
+ is: %i[schema format ],
56
+ }.each do |method, path|
57
+ define_method method do path.inject(processed, &:[]) end # Get value from hash by key path
58
+ end
59
+ alias_method :required?, :required
60
+ end
61
+ end
62
+ end
63
+
64
+
65
+ __END__
66
+
67
+ Parameter Object Examples
68
+ A header parameter with an array of 64 bit integer numbers:
69
+
70
+ {
71
+ "name": "token",
72
+ "in": "header",
73
+ "description": "token to be passed as a header",
74
+ "required": true,
75
+ "schema": {
76
+ "type": "array",
77
+ "items": {
78
+ "type": "integer",
79
+ "format": "int64"
80
+ }
81
+ },
82
+ "style": "simple"
83
+ }
@@ -0,0 +1,29 @@
1
+ require 'oas_objs/media_type_obj'
2
+ require 'oas_objs/helpers'
3
+
4
+ module OpenApi
5
+ module DSL
6
+ # https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#referenceObject
7
+ class RefObj < Hash
8
+ include Helpers
9
+
10
+ attr_accessor :processed
11
+ def initialize(ref_to, component_key)
12
+ self.processed = {
13
+ '$ref': "#components/#{ref_to}s/#{component_key}"
14
+ }
15
+ end
16
+
17
+ def process; processed; end
18
+ end
19
+ end
20
+ end
21
+
22
+
23
+ __END__
24
+
25
+ Reference Object Example
26
+
27
+ {
28
+ "$ref": "#/components/schemas/Pet"
29
+ }
@@ -0,0 +1,54 @@
1
+ require 'oas_objs/media_type_obj'
2
+ require 'oas_objs/helpers'
3
+
4
+ module OpenApi
5
+ module DSL
6
+ # https://swagger.io/docs/specification/describing-request-body/
7
+ # https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#requestBodyObject
8
+ class RequestBodyObj < Hash
9
+ include Helpers
10
+
11
+ attr_accessor :processed, :media_type
12
+ def initialize(required, media_type, desc, schema_hash)
13
+ self.media_type = MediaTypeObj.new(media_type, schema_hash)
14
+ self.processed = { required: "#{required}".match?(/req/), description: desc }
15
+ end
16
+
17
+ def process
18
+ assign(media_type.process).to_processed 'content'
19
+ processed
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+
26
+ __END__
27
+
28
+ Request Body Examples
29
+ A request body with a referenced model definition.
30
+
31
+ {
32
+ "description": "user to add to the system",
33
+ "content": {
34
+ "application/json": {
35
+ "schema": {
36
+ "$ref": "#/components/schemas/User"
37
+ },
38
+ "examples": {
39
+ "user" : {
40
+ "summary": "User Example",
41
+ "externalValue": "http://foo.bar/examples/user-example.json"
42
+ }
43
+ }
44
+ },
45
+ "*/*": {
46
+ "examples": {
47
+ "user" : {
48
+ "summary": "User example in other format",
49
+ "externalValue": "http://foo.bar/examples/user-example.whatever"
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,44 @@
1
+ require 'oas_objs/media_type_obj'
2
+ require 'oas_objs/helpers'
3
+
4
+ module OpenApi
5
+ module DSL
6
+ # https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#responseObject
7
+ class ResponseObj < Hash
8
+ include Helpers
9
+
10
+ attr_accessor :processed, :code, :media_type
11
+ def initialize(code, desc, media_type, schema_hash)
12
+ self.code = "#{code}"
13
+ self.media_type = MediaTypeObj.new(media_type, schema_hash)
14
+ self.processed = { description: desc }
15
+ end
16
+
17
+ def process
18
+ assign(media_type.process).to_processed 'content'
19
+ { code => processed }
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+
26
+ __END__
27
+
28
+ Response Object Examples
29
+
30
+ Response of an array of a complex type:
31
+
32
+ {
33
+ "description": "A complex object array response",
34
+ "content": {
35
+ "application/json": {
36
+ "schema": {
37
+ "type": "array",
38
+ "items": {
39
+ "$ref": "#/components/schemas/VeryComplexType"
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }