Class: Bluepine::Generators::OpenAPI::Generator

Inherits:
Generator
  • Object
show all
Defined in:
lib/bluepine/generators/open_api/generator.rb

Overview

Generate Open API v3 Specifications

Examples:

resolver  = Resolver.new(schemas: [])
generator = OpenApi::Generator.new(resolver, {
  title: "Awesome API",
  version: "1.0.0",
  # other infos ...
})

generator = OpenApi::Generator.new(resolver) do
  title "Awesome API"
  version "1.0.0"
  servers []
end

generator.generate # => {  }

Constant Summary collapse

OPEN_API_VERSION =
"3.0.0".freeze
OPEN_API_ID_REGEX =
/:([\w]+)/.freeze
EMPTY_RESPONSE =
"Response".freeze
OPTIONS =
{
  version: nil,
  title: nil,
  description: nil,
  servers: []
}

Constants included from Resolvable

Resolvable::ResolverRequired

Constants included from Assertions

Assertions::Error, Assertions::KeyError, Assertions::SubsetError

Instance Method Summary collapse

Methods included from Resolvable

#resolver

Methods included from Assertions

#assert, #assert_in, #assert_kind_of, #assert_not, #assert_subset_of, included

Constructor Details

#initialize(resolver = nil, options = {}) ⇒ Generator

Returns a new instance of Generator.



35
36
37
38
39
# File 'lib/bluepine/generators/open_api/generator.rb', line 35

def initialize(resolver = nil, options = {})
  super(resolver)

  @options = OPTIONS.merge(options)
end

Instance Method Details

#generateObject



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/bluepine/generators/open_api/generator.rb', line 41

def generate
  {
    openapi: OPEN_API_VERSION,
    info: {
      version: @options[:version],
      title: @options[:title],
      description: @options[:description],
    },
    servers: generate_server_urls(@options[:servers]),
    paths: generate_paths(resolver.endpoints.keys),
    components: {
      schemas: generate_schemas(resolver.schemas.keys),
    },
  }
end

#generate_json_response(method) ⇒ Object



163
164
165
166
167
168
169
# File 'lib/bluepine/generators/open_api/generator.rb', line 163

def generate_json_response(method)
  {
    "application/json": {
      schema: PropertyGenerator.generate(method.schema, as: method.as),
    },
  }
end

#generate_operation(method, base_url = nil) ⇒ Object



128
129
130
131
132
133
134
135
136
137
# File 'lib/bluepine/generators/open_api/generator.rb', line 128

def generate_operation(method, base_url = nil)
  {
    tags: [method.schema.to_s.humanize.pluralize],
    parameters: generate_params(method, base_url),
  }.tap do |operation|
    operation[:requestBody] = generate_request_params(method) if method.body?
    operation[:summary]     = method.description if method.description
    operation[:responses]   = generate_responses(method)
  end
end

#generate_operations(endpoint, paths) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
# File 'lib/bluepine/generators/open_api/generator.rb', line 115

def generate_operations(endpoint, paths)
  base_url = endpoint.path
  group_methods_by_path(endpoint.methods(resolver)).each do |path, methods|
    resource_url = convert_id_params(url(base_url, path))
    paths[resource_url] = {}

    methods.each do |method|
      paths[resource_url][method.verb] = generate_operation(method, base_url)
    end
  end
end

#generate_param(param, in: :query, schema: nil) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/bluepine/generators/open_api/generator.rb', line 62

def generate_param(param, in: :query, schema: nil)
  return unless param.serializable?

  {
    name: param.name.to_s,
    in: binding.local_variable_get(:in),
    schema: PropertyGenerator.generate(param, schema: schema),
  }.tap do |parameter|
    parameter[:required]    = param.required if param.required
    parameter[:deprecated]  = param.deprecated if param.deprecated
    parameter[:description] = param.description if param.description
  end
end

#generate_params(method, base_url = nil) ⇒ Object

share for both specs



58
59
60
# File 'lib/bluepine/generators/open_api/generator.rb', line 58

def generate_params(method, base_url = nil)
  generate_path_params(method, base_url) + generate_query_params(method)
end

#generate_path_params(method, base_url = nil) ⇒ Object

path contains id? e.g. /users/:id



77
78
79
80
81
# File 'lib/bluepine/generators/open_api/generator.rb', line 77

def generate_path_params(method, base_url = nil)
  extract_ids(url(base_url, method.path))
    .map { |id| Attributes::StringAttribute.new(id, required: true) }
    .map { |id| generate_param(id, in: :path) }
end

#generate_paths(services) ⇒ Object



106
107
108
109
110
111
112
113
# File 'lib/bluepine/generators/open_api/generator.rb', line 106

def generate_paths(services)
  services.each_with_object({}) do |name, paths|
    # no need to initialize
    next unless (endpoint = resolver.endpoint(name))

    generate_operations(endpoint, paths)
  end
end

#generate_query_params(method) ⇒ Object

convert request body to ‘query` params when HTTP verb is `GET`



84
85
86
87
88
89
90
91
92
# File 'lib/bluepine/generators/open_api/generator.rb', line 84

def generate_query_params(method)
  return [] unless method.verb == :get

  method.params.attributes.values.each_with_object([]) do |param, params|
    # include original schema when method.schema differs from params.schema
    schema = method.params.schema if method.schema != method.params.schema
    params << generate_param(param, schema: schema)
  end.compact
end

#generate_request_params(method) ⇒ Object



139
140
141
142
143
144
145
146
147
# File 'lib/bluepine/generators/open_api/generator.rb', line 139

def generate_request_params(method)
  {
    "content": {
      "application/x-www-form-urlencoded": {
        schema: generate_schema(method.params),
      },
    },
  }
end

#generate_response(method) ⇒ Object



155
156
157
158
159
160
161
# File 'lib/bluepine/generators/open_api/generator.rb', line 155

def generate_response(method)
  {
    description: method.schema&.to_s&.humanize || EMPTY_RESPONSE,
  }.tap do |response|
    response[:content] = generate_json_response(method) if method.schema.present?
  end
end

#generate_responses(method) ⇒ Object



149
150
151
152
153
# File 'lib/bluepine/generators/open_api/generator.rb', line 149

def generate_responses(method)
  {
    method.status => generate_response(method),
  }
end

#generate_schema(object) ⇒ Object



181
182
183
184
185
# File 'lib/bluepine/generators/open_api/generator.rb', line 181

def generate_schema(object)
  self.class.assert_kind_of Bluepine::Attributes::Attribute, object

  PropertyGenerator.generate(object)
end

#generate_schemas(serializers) ⇒ Object



172
173
174
175
176
177
178
179
# File 'lib/bluepine/generators/open_api/generator.rb', line 172

def generate_schemas(serializers)
  serializers.each_with_object({}) do |name, schemas|
    # no need to initialize
    next unless (object = resolver.schema(name))

    schemas[name] = generate_schema(object)
  end
end

#generate_server_urls(urls = []) ⇒ Object

– end –



102
103
104
# File 'lib/bluepine/generators/open_api/generator.rb', line 102

def generate_server_urls(urls = [])
  urls.map { |name, url| { url: url, description: name } }
end

#group_methods_by_path(methods) ⇒ Object



94
95
96
97
98
99
# File 'lib/bluepine/generators/open_api/generator.rb', line 94

def group_methods_by_path(methods)
  methods.values.each_with_object({}) do |method, paths|
    paths[method.path] ||= []
    paths[method.path] << method
  end
end