grape-oas 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +82 -0
- data/CONTRIBUTING.md +87 -0
- data/LICENSE.txt +21 -0
- data/README.md +184 -0
- data/RELEASING.md +109 -0
- data/grape-oas.gemspec +27 -0
- data/lib/grape-oas.rb +3 -0
- data/lib/grape_oas/api_model/api.rb +42 -0
- data/lib/grape_oas/api_model/media_type.rb +22 -0
- data/lib/grape_oas/api_model/node.rb +57 -0
- data/lib/grape_oas/api_model/operation.rb +55 -0
- data/lib/grape_oas/api_model/parameter.rb +24 -0
- data/lib/grape_oas/api_model/path.rb +29 -0
- data/lib/grape_oas/api_model/request_body.rb +27 -0
- data/lib/grape_oas/api_model/response.rb +28 -0
- data/lib/grape_oas/api_model/schema.rb +60 -0
- data/lib/grape_oas/api_model_builder.rb +63 -0
- data/lib/grape_oas/api_model_builders/concerns/content_type_resolver.rb +93 -0
- data/lib/grape_oas/api_model_builders/concerns/oas_utilities.rb +75 -0
- data/lib/grape_oas/api_model_builders/concerns/type_resolver.rb +142 -0
- data/lib/grape_oas/api_model_builders/operation.rb +168 -0
- data/lib/grape_oas/api_model_builders/path.rb +122 -0
- data/lib/grape_oas/api_model_builders/request.rb +304 -0
- data/lib/grape_oas/api_model_builders/request_params.rb +128 -0
- data/lib/grape_oas/api_model_builders/request_params_support/nested_params_builder.rb +155 -0
- data/lib/grape_oas/api_model_builders/request_params_support/param_location_resolver.rb +64 -0
- data/lib/grape_oas/api_model_builders/request_params_support/param_schema_builder.rb +163 -0
- data/lib/grape_oas/api_model_builders/request_params_support/schema_enhancer.rb +111 -0
- data/lib/grape_oas/api_model_builders/response.rb +241 -0
- data/lib/grape_oas/api_model_builders/response_parsers/base.rb +56 -0
- data/lib/grape_oas/api_model_builders/response_parsers/default_response_parser.rb +31 -0
- data/lib/grape_oas/api_model_builders/response_parsers/documentation_responses_parser.rb +35 -0
- data/lib/grape_oas/api_model_builders/response_parsers/http_codes_parser.rb +85 -0
- data/lib/grape_oas/constants.rb +81 -0
- data/lib/grape_oas/documentation_extension.rb +124 -0
- data/lib/grape_oas/exporter/base/operation.rb +88 -0
- data/lib/grape_oas/exporter/base/paths.rb +53 -0
- data/lib/grape_oas/exporter/concerns/schema_indexer.rb +93 -0
- data/lib/grape_oas/exporter/concerns/tag_builder.rb +55 -0
- data/lib/grape_oas/exporter/oas2/operation.rb +31 -0
- data/lib/grape_oas/exporter/oas2/parameter.rb +116 -0
- data/lib/grape_oas/exporter/oas2/paths.rb +19 -0
- data/lib/grape_oas/exporter/oas2/response.rb +74 -0
- data/lib/grape_oas/exporter/oas2/schema.rb +125 -0
- data/lib/grape_oas/exporter/oas2_schema.rb +133 -0
- data/lib/grape_oas/exporter/oas3/operation.rb +24 -0
- data/lib/grape_oas/exporter/oas3/parameter.rb +27 -0
- data/lib/grape_oas/exporter/oas3/paths.rb +21 -0
- data/lib/grape_oas/exporter/oas3/request_body.rb +54 -0
- data/lib/grape_oas/exporter/oas3/response.rb +85 -0
- data/lib/grape_oas/exporter/oas3/schema.rb +249 -0
- data/lib/grape_oas/exporter/oas30_schema.rb +13 -0
- data/lib/grape_oas/exporter/oas31/schema.rb +42 -0
- data/lib/grape_oas/exporter/oas31_schema.rb +34 -0
- data/lib/grape_oas/exporter/oas3_schema.rb +130 -0
- data/lib/grape_oas/exporter/registry.rb +82 -0
- data/lib/grape_oas/exporter.rb +16 -0
- data/lib/grape_oas/introspectors/base.rb +44 -0
- data/lib/grape_oas/introspectors/dry_introspector.rb +131 -0
- data/lib/grape_oas/introspectors/dry_introspector_support/argument_extractor.rb +51 -0
- data/lib/grape_oas/introspectors/dry_introspector_support/ast_walker.rb +125 -0
- data/lib/grape_oas/introspectors/dry_introspector_support/constraint_applier.rb +136 -0
- data/lib/grape_oas/introspectors/dry_introspector_support/constraint_extractor.rb +85 -0
- data/lib/grape_oas/introspectors/dry_introspector_support/constraint_merger.rb +47 -0
- data/lib/grape_oas/introspectors/dry_introspector_support/contract_resolver.rb +60 -0
- data/lib/grape_oas/introspectors/dry_introspector_support/inheritance_handler.rb +87 -0
- data/lib/grape_oas/introspectors/dry_introspector_support/predicate_handler.rb +131 -0
- data/lib/grape_oas/introspectors/dry_introspector_support/type_schema_builder.rb +143 -0
- data/lib/grape_oas/introspectors/dry_introspector_support/type_unwrapper.rb +143 -0
- data/lib/grape_oas/introspectors/entity_introspector.rb +165 -0
- data/lib/grape_oas/introspectors/entity_introspector_support/cycle_tracker.rb +42 -0
- data/lib/grape_oas/introspectors/entity_introspector_support/discriminator_handler.rb +83 -0
- data/lib/grape_oas/introspectors/entity_introspector_support/exposure_processor.rb +261 -0
- data/lib/grape_oas/introspectors/entity_introspector_support/inheritance_builder.rb +112 -0
- data/lib/grape_oas/introspectors/entity_introspector_support/property_extractor.rb +53 -0
- data/lib/grape_oas/introspectors/registry.rb +136 -0
- data/lib/grape_oas/rake/oas_tasks.rb +127 -0
- data/lib/grape_oas/version.rb +5 -0
- data/lib/grape_oas.rb +145 -0
- metadata +152 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1a578224c8df0f07ef5151db8496a084a64c6000f1f4c4158d7cc21efee1f91f
|
|
4
|
+
data.tar.gz: a1427ab277998cd7828e24c91f2555be6ea41b254312f14c4ad40b43204fa13c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c03fa30c214a62b2f3164ced48ff16e6c8ba120126ac3d7397db0cd0a7e6ee191224b10bd59badb91f848d3bfcded18cc03e4da331f883c80203795dfa0a143b
|
|
7
|
+
data.tar.gz: b232aa12c2211dc35853933ad09e1d1d5451d1df9fc3c2e1cb2c3bc1d58f8769b892141381323669c3c158e6ca409bbab3358012f9be0360f2736cb6194b1ada
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2025-12-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
#### Core Features
|
|
13
|
+
- OpenAPI specification generation for Grape APIs
|
|
14
|
+
- Support for OpenAPI 2.0 (Swagger), 3.0, and 3.1 specifications
|
|
15
|
+
- `GrapeOAS.generate(app:, schema_type:)` for programmatic generation
|
|
16
|
+
- `add_oas_documentation` DSL for mounting documentation endpoint
|
|
17
|
+
- `add_swagger_documentation` compatibility shim for grape-swagger migration
|
|
18
|
+
- Query parameter `?oas=2|3|3.1` for version selection at runtime
|
|
19
|
+
|
|
20
|
+
#### Entity Support
|
|
21
|
+
- Built-in Grape::Entity introspection (no separate gem needed)
|
|
22
|
+
- Dry::Validation::Contract and Dry::Struct support
|
|
23
|
+
- Entity inheritance with `allOf` composition
|
|
24
|
+
- Polymorphism support with `discriminator`
|
|
25
|
+
- Sum types (`|`) converted to `anyOf`
|
|
26
|
+
- Circular reference handling with `$ref`
|
|
27
|
+
|
|
28
|
+
#### Parameter Documentation
|
|
29
|
+
- All Grape parameter types (String, Integer, Float, Boolean, Date, DateTime, Array, Hash, File)
|
|
30
|
+
- Nested parameters (Hash with block)
|
|
31
|
+
- Array parameters with item types (`Array[String]`, `Array[Integer]`)
|
|
32
|
+
- Multi-type parameters (`types: [String, Integer]`)
|
|
33
|
+
- Parameter validation constraints (values, regexp, minimum, maximum, etc.)
|
|
34
|
+
- Parameter hiding (`documentation: { hidden: true }`)
|
|
35
|
+
- `collectionFormat` support for OAS2 arrays
|
|
36
|
+
|
|
37
|
+
#### Response Documentation
|
|
38
|
+
- `success` and `failure` response definitions
|
|
39
|
+
- Multiple success/failure status codes
|
|
40
|
+
- Response headers
|
|
41
|
+
- Response examples
|
|
42
|
+
- Multiple present responses with `as:` key combination
|
|
43
|
+
- Root element wrapping support
|
|
44
|
+
- `suppress_default_error_response` option
|
|
45
|
+
|
|
46
|
+
#### Endpoint Documentation
|
|
47
|
+
- `desc` block syntax with detail, tags, deprecated, consumes, produces
|
|
48
|
+
- Endpoint hiding (`hidden: true` or lambda)
|
|
49
|
+
- `body_name` for custom body parameter naming (OAS2)
|
|
50
|
+
- Request body for GET/HEAD/DELETE when explicitly enabled
|
|
51
|
+
- Operation extensions (`x-*` properties)
|
|
52
|
+
|
|
53
|
+
#### Configuration
|
|
54
|
+
- Global options: host, base_path, schemes, servers, consumes, produces
|
|
55
|
+
- Info object: title, version, description, contact, license, terms_of_service
|
|
56
|
+
- Security definitions (API key, OAuth2, Bearer)
|
|
57
|
+
- Tag definitions with descriptions
|
|
58
|
+
- `models` option to pre-register entities
|
|
59
|
+
- Namespace filtering for partial schema generation
|
|
60
|
+
- URL-based namespace filtering for mounted docs (`/swagger_doc/users`)
|
|
61
|
+
- Tag filtering to only include used tags
|
|
62
|
+
|
|
63
|
+
#### Rake Tasks
|
|
64
|
+
- `grape_oas:generate[API,schema_type,output_path]` for file generation
|
|
65
|
+
- `grape_oas:validate[file_path]` for spec validation
|
|
66
|
+
|
|
67
|
+
#### Migration Support
|
|
68
|
+
- Comprehensive migration guide from grape-swagger
|
|
69
|
+
- Feature parity documentation
|
|
70
|
+
- Compatibility shim for `add_swagger_documentation`
|
|
71
|
+
|
|
72
|
+
#### Extensibility
|
|
73
|
+
- Introspector registry - register custom introspectors via `GrapeOAS.introspectors.register()`
|
|
74
|
+
- Exporter registry - register custom exporters via `GrapeOAS.exporters.register(ExporterClass, as: :alias)`
|
|
75
|
+
|
|
76
|
+
### Documentation
|
|
77
|
+
- README with full usage examples
|
|
78
|
+
- `docs/MIGRATING_FROM_GRAPE_SWAGGER.md` - detailed migration guide
|
|
79
|
+
- `docs/ARCHITECTURE.md` - system architecture overview
|
|
80
|
+
- `docs/INTROSPECTORS.md` - introspector system documentation
|
|
81
|
+
- `docs/EXPORTERS.md` - exporter system documentation
|
|
82
|
+
- `docs/API_MODEL.md` - internal API model reference
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Contributing to Grape::OAS
|
|
2
|
+
|
|
3
|
+
We welcome contributions to Grape::OAS! This document provides guidelines for contributing.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
1. **Fork the repository** on GitHub
|
|
8
|
+
|
|
9
|
+
2. **Clone your fork**:
|
|
10
|
+
```bash
|
|
11
|
+
git clone https://github.com/YOUR_USERNAME/grape-oas.git
|
|
12
|
+
cd grape-oas
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
3. **Set up upstream remote**:
|
|
16
|
+
```bash
|
|
17
|
+
git remote add upstream https://github.com/numbata/grape-oas.git
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
4. **Install dependencies**:
|
|
21
|
+
```bash
|
|
22
|
+
bin/setup
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Development Workflow
|
|
26
|
+
|
|
27
|
+
1. **Create a topic branch**:
|
|
28
|
+
```bash
|
|
29
|
+
git checkout -b my-feature
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
2. **Write tests** for your changes in `test/`
|
|
33
|
+
|
|
34
|
+
3. **Implement your feature or fix**
|
|
35
|
+
|
|
36
|
+
4. **Run the test suite**:
|
|
37
|
+
```bash
|
|
38
|
+
bundle exec rake test
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
5. **Run RuboCop**:
|
|
42
|
+
```bash
|
|
43
|
+
bundle exec rubocop
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
6. **Run all checks**:
|
|
47
|
+
```bash
|
|
48
|
+
bundle exec rake
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Submitting Changes
|
|
52
|
+
|
|
53
|
+
1. **Update CHANGELOG.md** under "Unreleased" section
|
|
54
|
+
|
|
55
|
+
2. **Write clear commit messages** describing what and why
|
|
56
|
+
|
|
57
|
+
3. **Push to your fork**:
|
|
58
|
+
```bash
|
|
59
|
+
git push origin my-feature
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
4. **Open a Pull Request** against the `main` branch
|
|
63
|
+
|
|
64
|
+
## Pull Request Guidelines
|
|
65
|
+
|
|
66
|
+
- Keep PRs focused on a single change
|
|
67
|
+
- Include tests for new functionality
|
|
68
|
+
- Update documentation if needed
|
|
69
|
+
- Ensure CI passes before requesting review
|
|
70
|
+
|
|
71
|
+
## Code Style
|
|
72
|
+
|
|
73
|
+
- Follow existing code conventions
|
|
74
|
+
- RuboCop enforces style - run it before committing
|
|
75
|
+
- Use descriptive variable and method names
|
|
76
|
+
|
|
77
|
+
## Testing
|
|
78
|
+
|
|
79
|
+
- Write tests for all new functionality
|
|
80
|
+
- Ensure existing tests pass
|
|
81
|
+
- Aim for good coverage of edge cases
|
|
82
|
+
|
|
83
|
+
## Questions?
|
|
84
|
+
|
|
85
|
+
Open an issue for questions or discussions about potential changes.
|
|
86
|
+
|
|
87
|
+
Thank you for contributing!
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Andrei Subbota
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Grape::OAS
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/grape-oas)
|
|
4
|
+
[](https://github.com/numbata/grape-oas/actions)
|
|
5
|
+
|
|
6
|
+
OpenAPI Specification (OAS) documentation generator for [Grape](https://github.com/ruby-grape/grape) APIs. Supports OpenAPI 2.0 (Swagger), 3.0, and 3.1 specifications.
|
|
7
|
+
|
|
8
|
+
## Why Grape::OAS?
|
|
9
|
+
|
|
10
|
+
Grape::OAS is built around a **DTO (Data Transfer Object) architecture** that separates collecting API metadata from generating schemas. This clean separation makes the codebase easier to reason about and enables support for multiple output formats (OAS 2.0, 3.0, 3.1) from the same API definition.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Multi-version support**: Generate OAS 2.0, 3.0, or 3.1 from the same API
|
|
15
|
+
- **Entity integration**: Works with [grape-entity](https://github.com/ruby-grape/grape-entity) and [dry-struct](https://dry-rb.org/gems/dry-struct/)
|
|
16
|
+
- **Automatic type inference**: Derives OpenAPI types from Grape parameter definitions
|
|
17
|
+
- **Flexible output**: Mount as an endpoint or generate programmatically
|
|
18
|
+
|
|
19
|
+
## Compatibility
|
|
20
|
+
|
|
21
|
+
| grape-oas | grape | grape-entity | dry-struct | Ruby |
|
|
22
|
+
|-----------|-------|--------------|------------|------|
|
|
23
|
+
| 0.1.x | >= 3.0 | >= 0.7 | >= 1.0 | >= 3.2 |
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
gem 'grape-oas'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For entity support:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
gem 'grape-entity' # For grape-entity support
|
|
35
|
+
gem 'dry-struct' # For dry-struct contract support
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### Mount Documentation Endpoint
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
class API < Grape::API
|
|
44
|
+
format :json
|
|
45
|
+
|
|
46
|
+
add_oas_documentation(
|
|
47
|
+
info: {
|
|
48
|
+
title: 'My API',
|
|
49
|
+
version: '1.0.0'
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
resource :users do
|
|
54
|
+
desc 'List users', entity: Entity::User
|
|
55
|
+
get { User.all }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Documentation available at:
|
|
61
|
+
- `/swagger_doc` - OpenAPI 3.0 (default)
|
|
62
|
+
- `/swagger_doc?oas=2` - OpenAPI 2.0
|
|
63
|
+
- `/swagger_doc?oas=3.1` - OpenAPI 3.1
|
|
64
|
+
|
|
65
|
+
### Manual Generation
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
# Generate OpenAPI 3.0 spec
|
|
69
|
+
spec = GrapeOAS.generate(app: API, schema_type: :oas3)
|
|
70
|
+
puts JSON.pretty_generate(spec)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Rake Tasks
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
# In Rakefile
|
|
77
|
+
require 'grape_oas/tasks'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
rake grape_oas:generate[MyAPI,oas31,spec/openapi.json]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Documentation
|
|
85
|
+
|
|
86
|
+
| Document | Description |
|
|
87
|
+
|----------|-------------|
|
|
88
|
+
| [Configuration](docs/CONFIGURATION.md) | All configuration options |
|
|
89
|
+
| [Usage Guide](docs/USAGE.md) | Detailed usage examples |
|
|
90
|
+
| [Architecture](docs/ARCHITECTURE.md) | System architecture overview |
|
|
91
|
+
| [Introspectors](docs/INTROSPECTORS.md) | Custom introspector development |
|
|
92
|
+
| [Exporters](docs/EXPORTERS.md) | Custom exporter development |
|
|
93
|
+
| [API Model](docs/API_MODEL.md) | Internal API model reference |
|
|
94
|
+
|
|
95
|
+
## Basic Usage
|
|
96
|
+
|
|
97
|
+
### Documenting Endpoints
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
desc 'Get a user by ID',
|
|
101
|
+
detail: 'Returns detailed user information',
|
|
102
|
+
tags: ['users']
|
|
103
|
+
|
|
104
|
+
params do
|
|
105
|
+
requires :id, type: Integer, desc: 'User ID'
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
get ':id' do
|
|
109
|
+
User.find(params[:id])
|
|
110
|
+
end
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Response Documentation
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
desc 'Get user' do
|
|
117
|
+
success Entity::User
|
|
118
|
+
failure [[404, 'Not Found'], [500, 'Server Error']]
|
|
119
|
+
end
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Entity Definition
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
class Entity::User < Grape::Entity
|
|
126
|
+
expose :id, documentation: { type: Integer }
|
|
127
|
+
expose :name, documentation: { type: String }
|
|
128
|
+
expose :posts, using: Entity::Post, documentation: { is_array: true }
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Extensibility
|
|
133
|
+
|
|
134
|
+
### Custom Introspectors
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
class MyModelIntrospector
|
|
138
|
+
extend GrapeOAS::Introspectors::Base
|
|
139
|
+
|
|
140
|
+
def self.handles?(subject)
|
|
141
|
+
subject.is_a?(Class) && subject < MyBaseModel
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def self.build_schema(subject, stack: [], registry: {})
|
|
145
|
+
GrapeOAS::ApiModel::Schema.new(type: "object", canonical_name: subject.name)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
GrapeOAS.introspectors.register(MyModelIntrospector)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Custom Exporters
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
GrapeOAS.exporters.register(MyCustomExporter, as: :custom)
|
|
156
|
+
schema = GrapeOAS.generate(app: API, schema_type: :custom)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Related Projects
|
|
160
|
+
|
|
161
|
+
| Project | Description |
|
|
162
|
+
|---------|-------------|
|
|
163
|
+
| [grape](https://github.com/ruby-grape/grape) | REST-like API framework for Ruby |
|
|
164
|
+
| [grape-entity](https://github.com/ruby-grape/grape-entity) | Entity exposure for Grape APIs |
|
|
165
|
+
| [grape-swagger](https://github.com/ruby-grape/grape-swagger) | OpenAPI documentation for Grape APIs |
|
|
166
|
+
| [grape-swagger-entity](https://github.com/ruby-grape/grape-swagger-entity) | grape-swagger adapter for grape-entity |
|
|
167
|
+
| [oas_grape](https://github.com/a-chacon/oas_grape) | Another OpenAPI 3.1 generator for Grape |
|
|
168
|
+
|
|
169
|
+
## Development
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
git clone https://github.com/numbata/grape-oas.git
|
|
173
|
+
cd grape-oas
|
|
174
|
+
bin/setup
|
|
175
|
+
bundle exec rake test
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Contributing
|
|
179
|
+
|
|
180
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/numbata/grape-oas. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT License. Copyright (c) Andrei Subbota.
|
data/RELEASING.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Releasing Grape::OAS
|
|
2
|
+
|
|
3
|
+
This document describes the release process for the grape-oas gem.
|
|
4
|
+
|
|
5
|
+
## Pre-Release Checks
|
|
6
|
+
|
|
7
|
+
Before releasing, ensure:
|
|
8
|
+
|
|
9
|
+
1. All tests pass locally and in CI:
|
|
10
|
+
```bash
|
|
11
|
+
bundle install
|
|
12
|
+
bundle exec rake
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
2. The CHANGELOG.md is up to date with all changes since the last release.
|
|
16
|
+
|
|
17
|
+
3. The version number in `lib/grape_oas/version.rb` is correct.
|
|
18
|
+
|
|
19
|
+
## Release Steps
|
|
20
|
+
|
|
21
|
+
### Automated Release Process
|
|
22
|
+
|
|
23
|
+
1. **Update the version** in `lib/grape_oas/version.rb` to the release version
|
|
24
|
+
|
|
25
|
+
2. **Prepare the CHANGELOG**:
|
|
26
|
+
```bash
|
|
27
|
+
bundle exec rake release:prerelease
|
|
28
|
+
```
|
|
29
|
+
This task will:
|
|
30
|
+
- Replace "Unreleased" with the version number and today's date
|
|
31
|
+
- Remove any "Your contribution here" placeholder lines
|
|
32
|
+
- Commit with message "Preparing for release vX.X.X"
|
|
33
|
+
|
|
34
|
+
**Note**: The task is idempotent - safe to run multiple times.
|
|
35
|
+
|
|
36
|
+
3. **Push and create the release**:
|
|
37
|
+
```bash
|
|
38
|
+
git push origin main
|
|
39
|
+
bundle exec rake release
|
|
40
|
+
```
|
|
41
|
+
This will:
|
|
42
|
+
- Build the gem
|
|
43
|
+
- Create a git tag
|
|
44
|
+
- Push the tag to GitHub
|
|
45
|
+
- Push the gem to RubyGems.org
|
|
46
|
+
|
|
47
|
+
<details>
|
|
48
|
+
<summary>Manual Release Steps (if needed)</summary>
|
|
49
|
+
|
|
50
|
+
1. **Update CHANGELOG.md**
|
|
51
|
+
- Change "Unreleased" to the version number and date
|
|
52
|
+
- Remove any "Your contribution here" placeholder lines
|
|
53
|
+
|
|
54
|
+
2. **Commit the release preparation**:
|
|
55
|
+
```bash
|
|
56
|
+
git add CHANGELOG.md
|
|
57
|
+
git commit -m "Preparing for release v0.x.x"
|
|
58
|
+
git push origin main
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
3. **Create and push the release**:
|
|
62
|
+
```bash
|
|
63
|
+
bundle exec rake release
|
|
64
|
+
```
|
|
65
|
+
This will:
|
|
66
|
+
- Build the gem
|
|
67
|
+
- Create a git tag
|
|
68
|
+
- Push the tag to GitHub
|
|
69
|
+
- Push the gem to RubyGems.org
|
|
70
|
+
</details>
|
|
71
|
+
|
|
72
|
+
## Post-Release
|
|
73
|
+
|
|
74
|
+
You can automate the post-release preparation with:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
bundle exec rake release:postrelease
|
|
78
|
+
git push origin main
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
This task will:
|
|
82
|
+
- Bump the patch version in `lib/grape_oas/version.rb`
|
|
83
|
+
- Ensure CHANGELOG.md has an "Unreleased" section
|
|
84
|
+
- Commit the changes with message "Prepare for next development iteration"
|
|
85
|
+
|
|
86
|
+
**Note**: The task is idempotent - safe to run multiple times without duplicating work.
|
|
87
|
+
|
|
88
|
+
<details>
|
|
89
|
+
<summary>Manual Post-Release Steps (if needed)</summary>
|
|
90
|
+
|
|
91
|
+
1. **Prepare for next development cycle**:
|
|
92
|
+
- Add "Unreleased" section to CHANGELOG.md
|
|
93
|
+
- Bump version in `lib/grape_oas/version.rb`
|
|
94
|
+
|
|
95
|
+
2. **Commit post-release changes**:
|
|
96
|
+
```bash
|
|
97
|
+
git add CHANGELOG.md lib/grape_oas/version.rb
|
|
98
|
+
git commit -m "Prepare for next development iteration"
|
|
99
|
+
git push origin main
|
|
100
|
+
```
|
|
101
|
+
</details>
|
|
102
|
+
|
|
103
|
+
## Versioning
|
|
104
|
+
|
|
105
|
+
This project follows [Semantic Versioning](https://semver.org/):
|
|
106
|
+
|
|
107
|
+
- **MAJOR**: Incompatible API changes
|
|
108
|
+
- **MINOR**: New functionality in a backward compatible manner
|
|
109
|
+
- **PATCH**: Backward compatible bug fixes
|
data/grape-oas.gemspec
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/grape_oas/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "grape-oas"
|
|
7
|
+
spec.version = GrapeOAS::VERSION
|
|
8
|
+
spec.authors = ["Andrei Subbota"]
|
|
9
|
+
spec.email = ["subbota@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "OpenAPI (Swagger) v2 and v3 documentation for Grape APIs"
|
|
12
|
+
spec.description = "A Grape extension that provides OpenAPI (Swagger) v2 and v3 documentation support"
|
|
13
|
+
spec.homepage = "https://github.com/numbata/grape-oas"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 3.2"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
20
|
+
|
|
21
|
+
spec.files = Dir["lib/**/*", "*.md", "LICENSE.txt", "grape-oas.gemspec"]
|
|
22
|
+
|
|
23
|
+
spec.add_dependency "grape", ">= 3.0"
|
|
24
|
+
spec.add_dependency "zeitwerk"
|
|
25
|
+
|
|
26
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
27
|
+
end
|
data/lib/grape-oas.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GrapeOAS
|
|
4
|
+
module ApiModel
|
|
5
|
+
# Represents the root API object in the DTO model for OpenAPI v2/v3.
|
|
6
|
+
# Contains metadata, paths, servers, tags, and components.
|
|
7
|
+
# Used as the entry point for building OpenAPIv2 and OpenAPIv3 documents.
|
|
8
|
+
#
|
|
9
|
+
# @see https://swagger.io/specification/
|
|
10
|
+
# @see GrapeOAS::ApiModel::Path
|
|
11
|
+
class API < Node
|
|
12
|
+
attr_accessor :title, :version, :paths, :servers, :tag_defs, :components,
|
|
13
|
+
:host, :base_path, :schemes, :security_definitions, :security,
|
|
14
|
+
:registered_schemas, :suppress_default_error_response
|
|
15
|
+
|
|
16
|
+
def initialize(title:, version:)
|
|
17
|
+
super()
|
|
18
|
+
@title = title
|
|
19
|
+
@version = version
|
|
20
|
+
@paths = Set.new
|
|
21
|
+
@servers = []
|
|
22
|
+
@tag_defs = Set.new
|
|
23
|
+
@components = {}
|
|
24
|
+
@host = nil
|
|
25
|
+
@base_path = nil
|
|
26
|
+
@schemes = []
|
|
27
|
+
@security_definitions = {}
|
|
28
|
+
@security = []
|
|
29
|
+
@registered_schemas = []
|
|
30
|
+
@suppress_default_error_response = false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def add_path(path)
|
|
34
|
+
@paths << path
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def add_tags(*tags)
|
|
38
|
+
@tag_defs.merge(tags)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GrapeOAS
|
|
4
|
+
module ApiModel
|
|
5
|
+
# Represents a media type (e.g., application/json) in the DTO model for OpenAPI v2/v3.
|
|
6
|
+
# Used for request bodies and responses to specify content type and schema.
|
|
7
|
+
#
|
|
8
|
+
# @see https://swagger.io/specification/
|
|
9
|
+
# @see GrapeOAS::ApiModel::RequestBody, GrapeOAS::ApiModel::Response
|
|
10
|
+
class MediaType < Node
|
|
11
|
+
attr_accessor :mime_type, :schema, :examples, :extensions
|
|
12
|
+
|
|
13
|
+
def initialize(mime_type:, schema:, examples: nil, extensions: nil)
|
|
14
|
+
super()
|
|
15
|
+
@mime_type = mime_type
|
|
16
|
+
@schema = schema
|
|
17
|
+
@examples = examples
|
|
18
|
+
@extensions = extensions
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module GrapeOAS
|
|
6
|
+
module ApiModel
|
|
7
|
+
# Base class for all DTO (intermediate) nodes used in OpenAPI v2/v3 conversion.
|
|
8
|
+
# Provides a unique ID and helper methods for referencing and bucketing.
|
|
9
|
+
# All DTO classes for OpenAPIv2 and OpenAPIv3 inherit from Node.
|
|
10
|
+
#
|
|
11
|
+
# @abstract
|
|
12
|
+
# @see GrapeOAS::ApiModel::Schema, GrapeOAS::ApiModel::Parameter, etc.
|
|
13
|
+
class Node
|
|
14
|
+
# OpenAPI component bucket names per spec (some are irregular plurals)
|
|
15
|
+
BUCKET_NAMES = {
|
|
16
|
+
"Schema" => "schemas",
|
|
17
|
+
"Parameter" => "parameters",
|
|
18
|
+
"RequestBody" => "requestBodies",
|
|
19
|
+
"Response" => "responses",
|
|
20
|
+
"Header" => "headers",
|
|
21
|
+
"SecurityScheme" => "securitySchemes",
|
|
22
|
+
"Link" => "links",
|
|
23
|
+
"Callback" => "callbacks",
|
|
24
|
+
"Example" => "examples",
|
|
25
|
+
"PathItem" => "pathItems"
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
# Returns the pluralized bucket name for this class (e.g., "schemas", "parameters").
|
|
30
|
+
# Uses OpenAPI spec-compliant names for irregular plurals (e.g., "requestBodies").
|
|
31
|
+
# Memoized at the class level to avoid repeated string manipulation.
|
|
32
|
+
def bucket
|
|
33
|
+
@bucket ||= begin
|
|
34
|
+
class_name = name.split("::").last
|
|
35
|
+
BUCKET_NAMES.fetch(class_name, "#{class_name.downcase}s")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
attr_reader :id
|
|
41
|
+
|
|
42
|
+
def initialize(node_id: nil)
|
|
43
|
+
@id = node_id || generate_id
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def ref
|
|
47
|
+
"#/components/#{self.class.bucket}/#{id}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def generate_id
|
|
53
|
+
SecureRandom.uuid
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|