gitlab-grape-openapi 0.0.0 → 0.1.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.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +222 -4
- data/lib/gitlab/grape_openapi/concerns/fail_fast_annotatable.rb +23 -0
- data/lib/gitlab/grape_openapi/concerns/limit_resolver.rb +31 -0
- data/lib/gitlab/grape_openapi/concerns/regex_converter.rb +58 -0
- data/lib/gitlab/grape_openapi/concerns/serializable.rb +19 -0
- data/lib/gitlab/grape_openapi/configuration.rb +24 -0
- data/lib/gitlab/grape_openapi/converters/coercer_resolver.rb +74 -0
- data/lib/gitlab/grape_openapi/converters/entity_converter.rb +267 -0
- data/lib/gitlab/grape_openapi/converters/operation_converter.rb +250 -0
- data/lib/gitlab/grape_openapi/converters/parameter_converter.rb +252 -0
- data/lib/gitlab/grape_openapi/converters/path_converter.rb +152 -0
- data/lib/gitlab/grape_openapi/converters/request_body_converter.rb +97 -0
- data/lib/gitlab/grape_openapi/converters/response_converter.rb +185 -0
- data/lib/gitlab/grape_openapi/converters/tag_converter.rb +36 -0
- data/lib/gitlab/grape_openapi/converters/type_resolver.rb +75 -0
- data/lib/gitlab/grape_openapi/generator.rb +60 -0
- data/lib/gitlab/grape_openapi/models/info.rb +29 -0
- data/lib/gitlab/grape_openapi/models/operation.rb +47 -0
- data/lib/gitlab/grape_openapi/models/parameter.rb +43 -0
- data/lib/gitlab/grape_openapi/models/path_item.rb +26 -0
- data/lib/gitlab/grape_openapi/models/request_body/parameter_schema.rb +250 -0
- data/lib/gitlab/grape_openapi/models/request_body/parameters.rb +87 -0
- data/lib/gitlab/grape_openapi/models/response.rb +48 -0
- data/lib/gitlab/grape_openapi/models/schema.rb +61 -0
- data/lib/gitlab/grape_openapi/models/security_scheme.rb +130 -0
- data/lib/gitlab/grape_openapi/models/server.rb +31 -0
- data/lib/gitlab/grape_openapi/models/server_variable.rb +25 -0
- data/lib/gitlab/grape_openapi/models/tag.rb +44 -0
- data/lib/gitlab/grape_openapi/request_body_registry.rb +57 -0
- data/lib/gitlab/grape_openapi/schema_registry.rb +26 -0
- data/lib/gitlab/grape_openapi/serializers/time.rb +19 -0
- data/lib/gitlab/grape_openapi/tag_registry.rb +29 -0
- data/lib/gitlab/grape_openapi/version.rb +8 -0
- data/lib/gitlab-grape-openapi.rb +64 -0
- metadata +162 -12
- data/lib/gitlab/grape/openapi/version.rb +0 -9
- data/lib/gitlab/grape/openapi.rb +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5548e70ecc3368b56d965a6bbbc6ae8d448a8a230f0ff453c05d91fc3dfc4714
|
|
4
|
+
data.tar.gz: f535c2530fa0eed45b85b92805973d62dc294b3c7475fd6464229dea5113f1b2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8410aab600a941dca435dd2da070dc1c68141cc5fea001452081216fd3b8f0714a1a87d39811ae9b482c55fc2350953c5bbe6979189631ad96a9d8641b7f42e8
|
|
7
|
+
data.tar.gz: d1ed6e54fc2819e34b8b2acfdd2ef9077647f5c11b4b3bf7de59dad8466a480f4240edb8e30b22fb1a6e8c747e088ed8d2632f3e92e92aa75555f69f7760895a
|
data/LICENSE
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,224 @@
|
|
|
1
|
-
#
|
|
1
|
+
# GitLab Grape OpenAPI
|
|
2
2
|
|
|
3
|
-
> [!
|
|
4
|
-
> This gem
|
|
3
|
+
> [!IMPORTANT]
|
|
4
|
+
> **Internal use only.** This gem exists to generate the OpenAPI 3.0 spec for
|
|
5
|
+
> the [GitLab Rails monorepo](https://gitlab.com/gitlab-org/gitlab) and is not
|
|
6
|
+
> intended for use outside of GitLab.
|
|
7
|
+
>
|
|
8
|
+
> - It is published to rubygems.org only so the monorepo's `Gemfile` can
|
|
9
|
+
> depend on it — not as a general-purpose Grape → OpenAPI tool.
|
|
10
|
+
> - The public API, configuration DSL, and generated output may change in
|
|
11
|
+
> **any** release, including patch releases. There is no semantic-versioning
|
|
12
|
+
> contract.
|
|
13
|
+
> - No external support is provided. Issues and merge requests from outside
|
|
14
|
+
> GitLab may be closed without review.
|
|
15
|
+
> - Feature work is driven by the needs of `gitlab-org/gitlab`; capabilities
|
|
16
|
+
> that aren't needed there will not be added.
|
|
5
17
|
|
|
6
|
-
|
|
18
|
+
Internal gem for generating [OpenAPI 3.0](https://spec.openapis.org/oas/v3.0.0) specifications from [Grape](https://github.com/ruby-grape/grape) API definitions, used by [`gitlab-org/gitlab`](https://gitlab.com/gitlab-org/gitlab) to publish its REST API reference.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
Add to your Gemfile:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
gem 'gitlab-grape-openapi'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then run:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
bundle install
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
Configure the gem using the `Gitlab::GrapeOpenapi.configure` block, typically in an initializer:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
Gitlab::GrapeOpenapi.configure do |config|
|
|
40
|
+
# Required: API metadata
|
|
41
|
+
config.info = Gitlab::GrapeOpenapi::Models::Info.new(
|
|
42
|
+
title: 'My API',
|
|
43
|
+
description: 'API description',
|
|
44
|
+
version: 'v1',
|
|
45
|
+
terms_of_service: 'https://example.com/terms'
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# API path configuration
|
|
49
|
+
config.api_prefix = "api" # Default: "api"
|
|
50
|
+
config.api_version = "v1" # Default: "v1"
|
|
51
|
+
|
|
52
|
+
# Server definitions
|
|
53
|
+
config.servers = [
|
|
54
|
+
Gitlab::GrapeOpenapi::Models::Server.new(
|
|
55
|
+
url: 'https://{hostname}',
|
|
56
|
+
description: "Production API",
|
|
57
|
+
variables: {
|
|
58
|
+
hostname: Gitlab::GrapeOpenapi::Models::ServerVariable.new(
|
|
59
|
+
default: 'api.example.com',
|
|
60
|
+
description: 'API hostname'
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
# Security schemes
|
|
67
|
+
config.security_schemes = [
|
|
68
|
+
Gitlab::GrapeOpenapi::Models::SecurityScheme.new(
|
|
69
|
+
name: "bearerAuth",
|
|
70
|
+
type: "http",
|
|
71
|
+
scheme: "bearer"
|
|
72
|
+
)
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
# Exclude specific API classes from generation
|
|
76
|
+
config.excluded_api_classes = [
|
|
77
|
+
'API::Internal::Base',
|
|
78
|
+
'API::Internal::Admin'
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
# Override tag names for better display
|
|
82
|
+
config.tag_overrides = {
|
|
83
|
+
'Ci' => 'CI',
|
|
84
|
+
'Oauth' => 'OAuth'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Map Grape route settings to OpenAPI extensions
|
|
88
|
+
config.annotations = {
|
|
89
|
+
lifecycle: 'x-gitlab-lifecycle'
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Configuration Options
|
|
95
|
+
|
|
96
|
+
| Option | Type | Default | Description |
|
|
97
|
+
| ---------------------- | ------------------------------- | ------- | ------------------------------------------------------------ |
|
|
98
|
+
| `info` | `Models::Info` | `nil` | API metadata (title, description, version, terms of service) |
|
|
99
|
+
| `api_prefix` | `String` | `"api"` | URL prefix for API routes |
|
|
100
|
+
| `api_version` | `String` | `"v1"` | API version string |
|
|
101
|
+
| `servers` | `Array<Models::Server>` | `[]` | Server definitions for the API |
|
|
102
|
+
| `security_schemes` | `Array<Models::SecurityScheme>` | `[]` | Authentication/authorization schemes |
|
|
103
|
+
| `excluded_api_classes` | `Array<String>` | `[]` | API class names to exclude from generation |
|
|
104
|
+
| `tag_overrides` | `Hash` | `{}` | Map of tag names to their display overrides |
|
|
105
|
+
| `annotations` | `Hash` | `{}` | Map of Grape route settings to OpenAPI extension names |
|
|
106
|
+
|
|
107
|
+
### Annotations
|
|
108
|
+
|
|
109
|
+
The `annotations` configuration maps Grape route settings to OpenAPI vendor extensions. For example:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
config.annotations = {
|
|
113
|
+
lifecycle: 'x-gitlab-lifecycle'
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
When a Grape endpoint has:
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
route_setting :lifecycle, 'mature'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The generated OpenAPI spec will include:
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
x-gitlab-lifecycle: mature
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Usage
|
|
129
|
+
|
|
130
|
+
### Generating an OpenAPI Specification
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
# Load all API and entity classes
|
|
134
|
+
Rails.application.eager_load!
|
|
135
|
+
|
|
136
|
+
api_classes = API::Base.descendants
|
|
137
|
+
entity_classes = Grape::Entity.descendants
|
|
138
|
+
|
|
139
|
+
# Generate the specification
|
|
140
|
+
spec = Gitlab::GrapeOpenapi.generate(
|
|
141
|
+
api_classes: api_classes,
|
|
142
|
+
entity_classes: entity_classes
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Output as JSON
|
|
146
|
+
File.write('openapi.json', JSON.pretty_generate(spec))
|
|
147
|
+
|
|
148
|
+
# Or as YAML
|
|
149
|
+
require 'yaml'
|
|
150
|
+
File.write('openapi.yaml', spec.to_yaml)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Usage with `gitlab-org/gitlab`
|
|
154
|
+
|
|
155
|
+
1. Start a Rails console in your GDK:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
cd ~/gdk/gitlab
|
|
159
|
+
rails console
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
2. Generate the OpenAPI specification:
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
Rails.application.eager_load!
|
|
166
|
+
api_classes = API::Base.descendants
|
|
167
|
+
entity_classes = Grape::Entity.descendants
|
|
168
|
+
spec = Gitlab::GrapeOpenapi.generate(api_classes: api_classes, entity_classes: entity_classes)
|
|
169
|
+
File.write(Rails.root.join('tmp', 'openapi.json'), JSON.pretty_generate(spec))
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
3. The spec will be saved to `tmp/openapi.json` in your GitLab directory.
|
|
173
|
+
|
|
174
|
+
## Architecture
|
|
175
|
+
|
|
176
|
+
The gem follows a converter-based architecture:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
Generator
|
|
180
|
+
├── TagConverter - Extracts tags from API classes
|
|
181
|
+
├── EntityConverter - Converts Grape::Entity to OpenAPI schemas
|
|
182
|
+
├── PathConverter - Converts routes to OpenAPI paths
|
|
183
|
+
│ ├── OperationConverter - Converts individual endpoints
|
|
184
|
+
│ ├── ParameterConverter - Converts endpoint parameters
|
|
185
|
+
│ ├── ResponseConverter - Converts endpoint responses
|
|
186
|
+
│ └── RequestBodyConverter - Converts request bodies
|
|
187
|
+
└── TypeResolver - Maps Ruby/Grape types to OpenAPI types
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Registries
|
|
191
|
+
|
|
192
|
+
- **SchemaRegistry** - Tracks converted entity schemas
|
|
193
|
+
- **RequestBodyRegistry** - Tracks request body schemas
|
|
194
|
+
- **TagRegistry** - Tracks API tags
|
|
195
|
+
|
|
196
|
+
## Development
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
bundle install
|
|
200
|
+
bundle exec rspec
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Running Tests
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
bundle exec rspec
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Linting
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
bundle exec rubocop
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Contributing
|
|
216
|
+
|
|
217
|
+
This gem is maintained by GitLab's API Platform team for internal use.
|
|
218
|
+
External contributions are not actively solicited; issues and merge
|
|
219
|
+
requests opened by non-GitLab contributors may be closed without review.
|
|
220
|
+
GitLab team members should follow the [standard contribution guidelines](https://docs.gitlab.com/ee/development/contributing/) — see the [project page](https://gitlab.com/gitlab-org/ruby/gems/gitlab-grape-openapi).
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
Released under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gitlab
|
|
4
|
+
module GrapeOpenapi
|
|
5
|
+
module Concerns
|
|
6
|
+
module FailFastAnnotatable
|
|
7
|
+
FAIL_FAST_ANNOTATION = '(validation stops on first error)'
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def fail_fast_in_validations?(validations)
|
|
12
|
+
validations&.any? { |v| v.dig(:opts, :fail_fast) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def annotate_fail_fast(desc)
|
|
16
|
+
return desc.to_s if desc.to_s.include?(FAIL_FAST_ANNOTATION)
|
|
17
|
+
|
|
18
|
+
"#{desc} #{FAIL_FAST_ANNOTATION}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# API::Validations::Validators::Limit is a custom validator unique to GitLab.
|
|
4
|
+
# This resolver compares by class name string rather than constant reference so that this gem
|
|
5
|
+
# does not blow up in environments where the validator is not defined.
|
|
6
|
+
|
|
7
|
+
module Gitlab
|
|
8
|
+
module GrapeOpenapi
|
|
9
|
+
module Concerns
|
|
10
|
+
module LimitResolver
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def limit_for(validations)
|
|
14
|
+
validation = validations&.find do |v|
|
|
15
|
+
v[:validator_class].name == 'API::Validations::Validators::Limit'
|
|
16
|
+
rescue NoMethodError
|
|
17
|
+
false
|
|
18
|
+
end
|
|
19
|
+
validation && validation[:options]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def apply_limit!(schema, validations)
|
|
23
|
+
return unless schema[:type] == 'string'
|
|
24
|
+
|
|
25
|
+
limit = limit_for(validations)
|
|
26
|
+
schema[:maxLength] = limit if limit.is_a?(Integer) && limit.positive?
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "js_regex"
|
|
4
|
+
|
|
5
|
+
module Gitlab
|
|
6
|
+
module GrapeOpenapi
|
|
7
|
+
module Concerns
|
|
8
|
+
# Converts Ruby Regexp objects into ECMA-262 (JavaScript) compatible
|
|
9
|
+
# pattern strings for use in OpenAPI `pattern` schema fields.
|
|
10
|
+
#
|
|
11
|
+
# OpenAPI requires `pattern` values to be valid ECMA-262 regular
|
|
12
|
+
# expressions, but Ruby RegExes routinely contain constructs that ECMA-262
|
|
13
|
+
# cannot parse: \A / \z anchors, inline option groups like (?i-mx:...),
|
|
14
|
+
# POSIX classes, etc. js_regex translates these into JS-compatible equivalents.
|
|
15
|
+
#
|
|
16
|
+
# The Ruby /i flag is folded into the pattern itself (via character class
|
|
17
|
+
# expansion) so the resulting pattern carries no flags, which OpenAPI's
|
|
18
|
+
# flag-less `pattern` field requires
|
|
19
|
+
module RegexConverter
|
|
20
|
+
# js_regex's `target:` controls which ECMAScript version's regex
|
|
21
|
+
# features it will emit. ES2018 is the newest target the gem
|
|
22
|
+
# supports (as of 3.14.0); earlier targets like the default (ES5)
|
|
23
|
+
# silently drop lookbehinds, changing the semantics of any Ruby
|
|
24
|
+
# regex that relies on them
|
|
25
|
+
JS_REGEX_TARGET = 'ES2018'
|
|
26
|
+
|
|
27
|
+
def regexp_to_pattern(value)
|
|
28
|
+
regexp = extract_regexp(value)
|
|
29
|
+
return unless regexp
|
|
30
|
+
|
|
31
|
+
JsRegex.new(inline_case_insensitivity(regexp), target: JS_REGEX_TARGET).source
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# Grape stores the validation's `:options` as the Regexp itself for
|
|
37
|
+
# `regexp: /.../`, or as a Hash `{ value: /.../, message: '...' }` for the
|
|
38
|
+
# long form. Pull the Regexp out of either shape
|
|
39
|
+
def extract_regexp(value)
|
|
40
|
+
return value if value.is_a?(Regexp)
|
|
41
|
+
|
|
42
|
+
value[:value] if value.is_a?(Hash) && value[:value].is_a?(Regexp)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# js_regex bakes case-insensitivity into character classes only when /i
|
|
46
|
+
# appears as an inline group; an outer /i flag survives as a JS-level
|
|
47
|
+
# option that we cannot represent in OpenAPI. Wrap the source in (?i:...)
|
|
48
|
+
# and drop the outer flag so js_regex expands the character classes
|
|
49
|
+
def inline_case_insensitivity(regexp)
|
|
50
|
+
return regexp if (regexp.options & Regexp::IGNORECASE).zero?
|
|
51
|
+
|
|
52
|
+
remaining_options = regexp.options & ~Regexp::IGNORECASE
|
|
53
|
+
Regexp.new("(?i:#{regexp.source})", remaining_options)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gitlab
|
|
4
|
+
module GrapeOpenapi
|
|
5
|
+
module Concerns
|
|
6
|
+
module Serializable
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def serializable?(value)
|
|
10
|
+
return false if value.is_a?(Proc)
|
|
11
|
+
return false if defined?(ActiveSupport::TimeWithZone) && value.is_a?(ActiveSupport::TimeWithZone)
|
|
12
|
+
return false if value.is_a?(Time)
|
|
13
|
+
|
|
14
|
+
true
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gitlab
|
|
4
|
+
module GrapeOpenapi
|
|
5
|
+
class Configuration
|
|
6
|
+
attr_accessor :api_version, :api_prefix, :excluded_api_classes, :servers, :security_schemes, :info,
|
|
7
|
+
:tag_overrides, :annotations, :coercer_mappings
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@api_prefix = "api"
|
|
11
|
+
@api_version = "v1"
|
|
12
|
+
@excluded_api_classes = []
|
|
13
|
+
@info = nil
|
|
14
|
+
|
|
15
|
+
@servers = []
|
|
16
|
+
@security_schemes = []
|
|
17
|
+
|
|
18
|
+
@tag_overrides = {}
|
|
19
|
+
@annotations = {}
|
|
20
|
+
@coercer_mappings = {}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gitlab
|
|
4
|
+
module GrapeOpenapi
|
|
5
|
+
module Converters
|
|
6
|
+
module CoercerResolver
|
|
7
|
+
def coercer_mapping_for(validations)
|
|
8
|
+
coercer_method = extract_coercer_method(validations)
|
|
9
|
+
return unless coercer_method # Validation doesn't use `coerce_with`
|
|
10
|
+
return if inline_proc?(coercer_method) # Inline Procs shouldn't need a coercer method
|
|
11
|
+
|
|
12
|
+
coercer_name = resolve_coercer_name(coercer_method)
|
|
13
|
+
config = Gitlab::GrapeOpenapi.configuration
|
|
14
|
+
mapping = config.coercer_mappings.find { |pattern, _mapping| coercer_name == pattern }&.last
|
|
15
|
+
return mapping if mapping
|
|
16
|
+
|
|
17
|
+
raise GenerationError,
|
|
18
|
+
"No OpenAPI schema mapping found for coercer '#{coercer_name}'. " \
|
|
19
|
+
"Add an entry for '#{coercer_name}' to coercer_mappings in " \
|
|
20
|
+
"config/initializers/gitlab_grape_openapi.rb, or use an inline lambda instead."
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def build_coerced_schema(mapping)
|
|
24
|
+
schema = {}
|
|
25
|
+
schema[:type] = mapping[:type] if mapping[:type]
|
|
26
|
+
schema[:items] = { type: mapping[:items_type] } if mapping[:items_type]
|
|
27
|
+
schema[:format] = mapping[:format] if mapping[:format]
|
|
28
|
+
schema[:additionalProperties] = mapping[:additional_properties] if mapping[:additional_properties]
|
|
29
|
+
|
|
30
|
+
schema
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def extract_coercer_method(validations)
|
|
36
|
+
return unless validations
|
|
37
|
+
|
|
38
|
+
coerce_validation = validations.find do |v|
|
|
39
|
+
v[:validator_class] == Grape::Validations::Validators::CoerceValidator
|
|
40
|
+
end
|
|
41
|
+
return unless coerce_validation
|
|
42
|
+
|
|
43
|
+
coerce_validation.dig(:options, :method)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def inline_proc?(coercer_method)
|
|
47
|
+
return false unless coercer_method.is_a?(Proc)
|
|
48
|
+
|
|
49
|
+
source_file, _line = coercer_method.source_location
|
|
50
|
+
return true unless source_file
|
|
51
|
+
|
|
52
|
+
source_file.exclude?('/validations/types/')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def resolve_coercer_name(coercer_method)
|
|
56
|
+
return coercer_name_from_source_location(coercer_method) if coercer_method.is_a?(Proc)
|
|
57
|
+
|
|
58
|
+
coercer_method.name.to_s
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def coercer_name_from_source_location(coercer_method)
|
|
62
|
+
source_file, _line = coercer_method.source_location
|
|
63
|
+
return coercer_method.to_s unless source_file
|
|
64
|
+
|
|
65
|
+
camelize(File.basename(source_file, '.rb'))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def camelize(str)
|
|
69
|
+
str.split('_').map(&:capitalize).join
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|