zero-rails_openapi 1.0.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.
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
+ }