eazypi 0.0.1 → 0.0.2
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/CHANGELOG.md +7 -1
- data/eazypi.gemspec +35 -0
- data/lib/eazypi/api.rb +30 -6
- data/lib/eazypi/components.rb +13 -1
- data/lib/eazypi/media_type.rb +7 -0
- data/lib/eazypi/operation.rb +5 -0
- data/lib/eazypi/request_body.rb +6 -0
- data/lib/eazypi/response.rb +9 -1
- data/lib/eazypi/responses.rb +6 -0
- data/lib/eazypi/schema/array.rb +4 -0
- data/lib/eazypi/schema/object.rb +20 -2
- data/lib/eazypi/schema/primitive.rb +2 -0
- data/lib/eazypi/schema.rb +4 -2
- data/lib/eazypi/serializer.rb +16 -4
- data/lib/eazypi/server.rb +18 -0
- data/lib/eazypi/version.rb +1 -1
- data/lib/eazypi.rb +1 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 344baf3f0f1dacd826dae2caf2aeda6172c78dcd72c4b5a3a8e05f691ccf1a4e
|
4
|
+
data.tar.gz: 5ac7965a5e48ad5962dfbce582a61c54d72c19f5ac0740d2c5cea4e8020f905f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bf75cf26c5890e8749311e3a65b32d2bd6d8834bbc54e8da77b828b8bbeb32d0e49aa33deda42cb31b733472d4def0044f3a4ef6f69aafa9fae0584952efd39
|
7
|
+
data.tar.gz: a41d5f22d742e653409a362d58cdf82684b1ded4b671400adf7f184b768eec3acb44f2ec4d9d7f2049c918d9806d8eab355012907576fc4ea470f56c4d62b90a
|
data/CHANGELOG.md
CHANGED
data/eazypi.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/eazypi/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "eazypi"
|
7
|
+
spec.version = Eazypi::VERSION
|
8
|
+
spec.authors = ["Nathan Samson"]
|
9
|
+
spec.email = ["nathan@nathansamson.be"]
|
10
|
+
spec.license = "MIT"
|
11
|
+
|
12
|
+
spec.summary = "An opinionated framework to generate OpenAPI API's for Rails"
|
13
|
+
spec.homepage = "https://gitlab.com/nathansamson/eazypi/-/blob/main/README.md"
|
14
|
+
spec.required_ruby_version = ">= 3.0.0"
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
+
spec.metadata["source_code_uri"] = "https://gitlab.com/nathansamson/eazypi"
|
18
|
+
spec.metadata["changelog_uri"] = "https://gitlab.com/nathansamson/eazypi/-/blob/main/CHANGELOG.md"
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
+
spec.files = Dir.chdir(__dir__) do
|
23
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
24
|
+
(File.expand_path(f) == __FILE__) ||
|
25
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ petstore-example/ .git .gitlab-ci.yml appveyor Gemfile])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
spec.add_dependency "actionpack", "~> 7.0"
|
33
|
+
spec.add_dependency "activesupport", "~> 7.0"
|
34
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
35
|
+
end
|
data/lib/eazypi/api.rb
CHANGED
@@ -26,6 +26,10 @@ module Eazypi
|
|
26
26
|
paths[operation.path] ||= PathItem.new
|
27
27
|
|
28
28
|
paths[operation.path].send(operation.method, operation)
|
29
|
+
|
30
|
+
operation.collect_components(
|
31
|
+
schemas: method(:reference_schema)
|
32
|
+
)
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
@@ -38,13 +42,19 @@ module Eazypi
|
|
38
42
|
end
|
39
43
|
|
40
44
|
def to_openapi_spec
|
45
|
+
to_openapi_spec_with_servers(servers)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_openapi_spec_with_servers(override_servers)
|
49
|
+
actual_servers = override_servers || servers
|
50
|
+
|
41
51
|
{
|
42
52
|
"openapi" => "3.0.3", # Future improvement allow different version
|
43
53
|
"info" => info.to_openapi_spec,
|
44
54
|
"paths" => paths.transform_keys do |path|
|
45
55
|
Operation.normalized_path(path)
|
46
56
|
end.transform_values(&:to_openapi_spec),
|
47
|
-
"servers" =>
|
57
|
+
"servers" => actual_servers.map(&:to_openapi_spec),
|
48
58
|
"components" => components.to_openapi_spec
|
49
59
|
}
|
50
60
|
end
|
@@ -71,22 +81,36 @@ module Eazypi
|
|
71
81
|
|
72
82
|
private
|
73
83
|
|
84
|
+
def reference_schema(schema)
|
85
|
+
return unless schema.is_a?(Eazypi::Schema::Object) && !schema.object_name.nil?
|
86
|
+
|
87
|
+
reference = components.add_schema(schema.object_name, schema)
|
88
|
+
schema.reference!(reference)
|
89
|
+
end
|
90
|
+
|
74
91
|
def prepare_controller_class
|
75
92
|
ancestors[0].const_set("EazypiController", controller_class)
|
76
93
|
end
|
77
94
|
|
78
|
-
def controller_class # rubocop:todo Metrics/MethodLength
|
95
|
+
def controller_class # rubocop:todo Metrics/MethodLength, Metrics/AbcSize
|
79
96
|
@controller_class ||= begin
|
80
97
|
api_instance = self
|
81
98
|
|
82
99
|
klass = Class.new(ActionController::Base)
|
83
|
-
klass.define_method(:show) do
|
84
|
-
|
100
|
+
klass.define_method(:show) do # rubocop:todo Metrics/MethodLength
|
101
|
+
override_servers = if api_instance.servers.empty?
|
102
|
+
s = Server.new
|
103
|
+
s.url request.url.split("/")[0..-2].join("/") # Probably a better way to do this
|
104
|
+
[
|
105
|
+
s
|
106
|
+
]
|
107
|
+
end
|
85
108
|
|
86
109
|
if params[:format] == "json"
|
87
|
-
render json: api_instance.
|
110
|
+
render json: api_instance.to_openapi_spec_with_servers(override_servers)
|
88
111
|
elsif params[:format] == "yaml"
|
89
|
-
render plain: api_instance.
|
112
|
+
render plain: api_instance.to_openapi_spec_with_servers(override_servers).to_yaml,
|
113
|
+
content_type: "text/yaml"
|
90
114
|
else
|
91
115
|
raise "#{params[:format]} not supported"
|
92
116
|
end
|
data/lib/eazypi/components.rb
CHANGED
@@ -3,8 +3,20 @@
|
|
3
3
|
module Eazypi
|
4
4
|
# OpenAPI spec ComponentsObject
|
5
5
|
class Components
|
6
|
+
def initialize
|
7
|
+
@schemas = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_schema(name, schema)
|
11
|
+
@schemas[name] = schema.dup # Make sure it never gets a reference
|
12
|
+
|
13
|
+
"#/components/schemas/#{name}"
|
14
|
+
end
|
15
|
+
|
6
16
|
def to_openapi_spec
|
7
|
-
{
|
17
|
+
{
|
18
|
+
"schemas" => @schemas.empty? ? nil : @schemas.transform_values(&:to_openapi_spec)
|
19
|
+
}.compact
|
8
20
|
end
|
9
21
|
end
|
10
22
|
end
|
data/lib/eazypi/media_type.rb
CHANGED
@@ -7,6 +7,13 @@ module Eazypi
|
|
7
7
|
|
8
8
|
spec_attribute :schema
|
9
9
|
|
10
|
+
def collect_components(schemas: nil, **kwargs)
|
11
|
+
return unless schema
|
12
|
+
|
13
|
+
schema.collect_components(schemas: schemas, **kwargs)
|
14
|
+
schemas&.call(schema)
|
15
|
+
end
|
16
|
+
|
10
17
|
def to_openapi_spec
|
11
18
|
{
|
12
19
|
"schema" => schema.to_openapi_spec
|
data/lib/eazypi/operation.rb
CHANGED
data/lib/eazypi/request_body.rb
CHANGED
data/lib/eazypi/response.rb
CHANGED
@@ -14,6 +14,8 @@ module Eazypi
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def content(schema, content_type = "application/json")
|
17
|
+
return if schema.nil?
|
18
|
+
|
17
19
|
media_type = MediaType.new
|
18
20
|
|
19
21
|
media_type.load do
|
@@ -23,10 +25,16 @@ module Eazypi
|
|
23
25
|
@content[content_type] = media_type
|
24
26
|
end
|
25
27
|
|
28
|
+
def collect_components(**kwargs)
|
29
|
+
@content.each_value do |media_type|
|
30
|
+
media_type.collect_components(**kwargs)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
26
34
|
def to_openapi_spec
|
27
35
|
{
|
28
36
|
"description" => description,
|
29
|
-
"content" => @content.transform_values(&:to_openapi_spec)
|
37
|
+
"content" => @content.empty? ? nil : @content.transform_values(&:to_openapi_spec)
|
30
38
|
}.compact
|
31
39
|
end
|
32
40
|
end
|
data/lib/eazypi/responses.rb
CHANGED
@@ -14,6 +14,12 @@ module Eazypi
|
|
14
14
|
@responses[status_code.to_s] = response
|
15
15
|
end
|
16
16
|
|
17
|
+
def collect_components(**kwargs)
|
18
|
+
@responses.each_value do |response|
|
19
|
+
response.collect_components(**kwargs)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
17
23
|
def to_openapi_spec
|
18
24
|
@responses.transform_values(&:to_openapi_spec)
|
19
25
|
end
|
data/lib/eazypi/schema/array.rb
CHANGED
data/lib/eazypi/schema/object.rb
CHANGED
@@ -4,9 +4,17 @@ module Eazypi
|
|
4
4
|
module Schema
|
5
5
|
# Object schema for Json
|
6
6
|
class Object
|
7
|
-
|
7
|
+
attr_reader :object_name
|
8
|
+
|
9
|
+
def initialize(object_name = nil)
|
8
10
|
@properties = {}
|
9
11
|
@required = []
|
12
|
+
@object_name = object_name
|
13
|
+
@reference = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def reference!(reference)
|
17
|
+
@reference = reference
|
10
18
|
end
|
11
19
|
|
12
20
|
def property(name, schema, required: false)
|
@@ -14,7 +22,16 @@ module Eazypi
|
|
14
22
|
@required << name if required
|
15
23
|
end
|
16
24
|
|
25
|
+
def collect_components(schemas: nil, **kwargs)
|
26
|
+
@properties.each_value do |property_schema|
|
27
|
+
property_schema.collect_components(schemas: schemas, **kwargs)
|
28
|
+
schemas&.call(property_schema)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
17
32
|
def to_openapi_spec
|
33
|
+
return { "$ref" => @reference } if @reference
|
34
|
+
|
18
35
|
{
|
19
36
|
"type" => "object",
|
20
37
|
"required" => @required.empty? ? nil : @required,
|
@@ -26,7 +43,8 @@ module Eazypi
|
|
26
43
|
return false unless other.is_a?(Object)
|
27
44
|
|
28
45
|
other.instance_variable_get(:@properties) == @properties &&
|
29
|
-
other.instance_variable_get(:@required) == @required
|
46
|
+
other.instance_variable_get(:@required) == @required &&
|
47
|
+
other.object_name == object_name
|
30
48
|
end
|
31
49
|
end
|
32
50
|
end
|
data/lib/eazypi/schema.rb
CHANGED
@@ -3,8 +3,10 @@
|
|
3
3
|
module Eazypi
|
4
4
|
# JSON schema
|
5
5
|
module Schema
|
6
|
-
def self.from_object(object) # rubocop:todo Metrics/MethodLength
|
7
|
-
if object
|
6
|
+
def self.from_object(object) # rubocop:todo Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
|
7
|
+
if object == :boolean
|
8
|
+
Schema::Primitive.new(type: "boolean")
|
9
|
+
elsif object.is_a?(::Array)
|
8
10
|
raise "Array needs to have one element" if object.length != 1
|
9
11
|
|
10
12
|
Schema::Array.new(Schema.from_object(object[0]))
|
data/lib/eazypi/serializer.rb
CHANGED
@@ -9,12 +9,23 @@ module Eazypi
|
|
9
9
|
class Attribute
|
10
10
|
attr_reader :name, :type, :required
|
11
11
|
|
12
|
-
def initialize(name, type:,
|
12
|
+
def initialize(name, type:, required:, method:, method_name:)
|
13
13
|
@name = name
|
14
14
|
@type = type
|
15
|
+
@method = method
|
15
16
|
@method_name = method_name
|
16
17
|
@required = required
|
17
18
|
end
|
19
|
+
|
20
|
+
def serialize(object)
|
21
|
+
if @method
|
22
|
+
@method.call(object)
|
23
|
+
elsif @method_name
|
24
|
+
object.send(@method_name)
|
25
|
+
else
|
26
|
+
object.send(@name)
|
27
|
+
end
|
28
|
+
end
|
18
29
|
end
|
19
30
|
|
20
31
|
def initialize(object)
|
@@ -23,17 +34,18 @@ module Eazypi
|
|
23
34
|
|
24
35
|
def to_json(*_args)
|
25
36
|
self.class.attributes.to_h do |attribute|
|
26
|
-
[attribute.name, @object
|
37
|
+
[attribute.name, attribute.serialize(@object)]
|
27
38
|
end
|
28
39
|
end
|
29
40
|
|
30
41
|
included do
|
31
42
|
@attributes = []
|
32
43
|
|
33
|
-
def self.attribute(attribute_name, type:, method_name: nil, required: false)
|
44
|
+
def self.attribute(attribute_name, type:, method: nil, method_name: nil, required: false)
|
34
45
|
@attributes << Attribute.new(
|
35
46
|
attribute_name,
|
36
47
|
type: type,
|
48
|
+
method: method,
|
37
49
|
method_name: method_name,
|
38
50
|
required: required
|
39
51
|
)
|
@@ -44,7 +56,7 @@ module Eazypi
|
|
44
56
|
end
|
45
57
|
|
46
58
|
def self.to_schema
|
47
|
-
schema = Schema::Object.new
|
59
|
+
schema = Schema::Object.new(name.split("::").last)
|
48
60
|
|
49
61
|
@attributes.each do |attribute|
|
50
62
|
schema.property attribute.name.to_s, Schema.from_object(attribute.type), required: attribute.required
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eazypi
|
4
|
+
# OpenAPI spec ServerObject
|
5
|
+
class Server
|
6
|
+
include SpecObject
|
7
|
+
|
8
|
+
spec_attribute :url
|
9
|
+
spec_attribute :description
|
10
|
+
|
11
|
+
def to_openapi_spec
|
12
|
+
{
|
13
|
+
"url" => url,
|
14
|
+
"description" => description
|
15
|
+
}.compact
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/eazypi/version.rb
CHANGED
data/lib/eazypi.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eazypi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Samson
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- CHANGELOG.md
|
51
51
|
- README.md
|
52
52
|
- Rakefile
|
53
|
+
- eazypi.gemspec
|
53
54
|
- lib/eazypi.rb
|
54
55
|
- lib/eazypi/api.rb
|
55
56
|
- lib/eazypi/api_controller.rb
|
@@ -67,10 +68,12 @@ files:
|
|
67
68
|
- lib/eazypi/schema/object.rb
|
68
69
|
- lib/eazypi/schema/primitive.rb
|
69
70
|
- lib/eazypi/serializer.rb
|
71
|
+
- lib/eazypi/server.rb
|
70
72
|
- lib/eazypi/spec_object.rb
|
71
73
|
- lib/eazypi/version.rb
|
72
74
|
homepage: https://gitlab.com/nathansamson/eazypi/-/blob/main/README.md
|
73
|
-
licenses:
|
75
|
+
licenses:
|
76
|
+
- MIT
|
74
77
|
metadata:
|
75
78
|
homepage_uri: https://gitlab.com/nathansamson/eazypi/-/blob/main/README.md
|
76
79
|
source_code_uri: https://gitlab.com/nathansamson/eazypi
|
@@ -91,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
94
|
- !ruby/object:Gem::Version
|
92
95
|
version: '0'
|
93
96
|
requirements: []
|
94
|
-
rubygems_version: 3.
|
97
|
+
rubygems_version: 3.5.3
|
95
98
|
signing_key:
|
96
99
|
specification_version: 4
|
97
100
|
summary: An opinionated framework to generate OpenAPI API's for Rails
|