apicraft-rails 1.0.1 โ 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +33 -34
- data/lib/apicraft/config.rb +4 -0
- data/lib/apicraft/loader.rb +2 -1
- data/lib/apicraft/railtie.rb +15 -1
- data/lib/apicraft/tasks/generate.rake +30 -0
- data/lib/apicraft/tasks/init.rake +20 -0
- data/lib/apicraft/tasks/validate.rake +8 -0
- data/lib/apicraft/templates/initializer.rb +62 -0
- data/lib/apicraft/templates/openapi.example.yaml +66 -0
- data/lib/apicraft/validator.rb +41 -0
- data/lib/apicraft/version.rb +1 -1
- data/lib/apicraft-rails.rb +1 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b30ecb65e234276a9d0af7dca63778397b8e1873a1a6a062b92853e791b980ba
|
4
|
+
data.tar.gz: da1c7cffc242a5008aa0166ae6ac874a4f6739def0d928c3e5417361c492cbbf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be09f4dd790950e1857c8cec15e945eac4058ac891de58fd8218a3b4c3cf033df314ccc951197235e2a5f5045cf30ef2b5e751acb42fb1988c9eabd1bd6a344c
|
7
|
+
data.tar.gz: 11da384ef306df936c9e1d288552da41001f6c4c7084617f76c0dfaf3edb85312bb22bf21914bd9e034cf36935bc5b79320e9c201f7e10f3a7a9c5db398e1dbe
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# APICraft Rails
|
2
2
|
[](https://github.com/apicraft-dev/apicraft-rails/actions/workflows/build.yml)
|
3
|
-
[](https://badge.fury.io/rb/apicraft-rails)
|
4
4
|
|
5
5
|
๐ Accelerates your development by 2-3x with an API Design First approach. Seamlessly integrates with your Rails application server โ no fancy tooling or expenses required.
|
6
6
|
|
@@ -14,8 +14,6 @@ It avoids the pitfalls of the code-first methodology, where contracts are auto-g
|
|
14
14
|
|
15
15
|
- [APICraft Rails](#apicraft-rails)
|
16
16
|
- [โจ Features](#-features)
|
17
|
-
- [๐ Upcoming Features](#-upcoming-features)
|
18
|
-
- [๐ช Works Like Magic](#-works-like-magic)
|
19
17
|
- [๐ API Design First Philosophy](#-api-design-first-philosophy)
|
20
18
|
- [๐ Installation](#-installation)
|
21
19
|
- [โ๏ธ Usage](#๏ธ-usage)
|
@@ -24,6 +22,7 @@ It avoids the pitfalls of the code-first methodology, where contracts are auto-g
|
|
24
22
|
- [๐ฎ Behaviour Mocking](#-behaviour-mocking)
|
25
23
|
- [๐ง Introspection](#-introspection)
|
26
24
|
- [๐ Documentation (Swagger docs and RapiDoc)](#-documentation-swagger-docs-and-rapidoc)
|
25
|
+
- [๐ CLI Support](#-cli-support)
|
27
26
|
- [๐ง Configuration](#-configuration)
|
28
27
|
- [๐ค Contributing](#-contributing)
|
29
28
|
- [๐ License](#-license)
|
@@ -42,13 +41,7 @@ It avoids the pitfalls of the code-first methodology, where contracts are auto-g
|
|
42
41
|
|
43
42
|
- ๐ **Easy Contracts Management** - Management of `openapi` specifications from within `app/contracts` directory. No new syntax, just plain old `openapi` standard with `.json` or `.yaml` formats
|
44
43
|
|
45
|
-
|
46
|
-
- ๐ **Clean & Custom Ruby DSL** - Support for a Ruby DSL alongwith the current `.json` and `.yaml` formats.
|
47
|
-
|
48
|
-
|
49
|
-
## ๐ช Works Like Magic
|
50
|
-
|
51
|
-
Once youโve installed the gem, getting started is a breeze. Simply create your OpenAPI contracts within the `app/contracts` directory of your Rails application. Youโre free to organize this directory in a way that aligns with your project's standards and preferences. Thatโs itโyour APIs will be up and running with mock responses, ready for development without any additional setup. It's as effortless as it sounds!
|
44
|
+
- ๐ **CLI Support** - Specification validations can be triggered from the CLI allowing integrations into your CI/CD pipelines.
|
52
45
|
|
53
46
|
## ๐ API Design First Philosophy
|
54
47
|
|
@@ -71,41 +64,38 @@ By adopting an API Design First approach with APICraft Rails, you can accelerate
|
|
71
64
|
|
72
65
|
## ๐ Installation
|
73
66
|
|
74
|
-
Add this line to your application's Gemfile:
|
67
|
+
1. Add this line to your application's Gemfile:
|
75
68
|
|
76
69
|
```ruby
|
77
|
-
gem 'apicraft-rails', '~> 1.0.
|
70
|
+
gem 'apicraft-rails', '~> 1.0.2'
|
78
71
|
```
|
79
72
|
|
80
|
-
And then execute:
|
73
|
+
2. And then execute:
|
74
|
+
```bash
|
75
|
+
$ bundle install
|
76
|
+
$ rails apicraft:init
|
77
|
+
```
|
81
78
|
|
82
|
-
|
79
|
+
This will create a file called `config/initializers/apicraft.rb` with all the necessary configurations. It will also create the default contracts directory called `app/contracts`.
|
83
80
|
|
84
|
-
After the installation in your rails project, you can start adding contracts in the `app/contracts` directory. This can have any internal directory structure based on your API versions, standards, etc.
|
85
81
|
|
86
|
-
Add the
|
82
|
+
3. Add the `apicraft` route to your route file (for documentation):
|
87
83
|
|
88
84
|
```ruby
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
# Rest of the configuration...
|
93
|
-
|
94
|
-
[
|
95
|
-
Apicraft::Middlewares::Mocker,
|
96
|
-
Apicraft::Middlewares::Introspector,
|
97
|
-
Apicraft::Middlewares::RequestValidator
|
98
|
-
].each { |mw| config.middleware.use mw }
|
99
|
-
|
100
|
-
Apicraft.configure do |config|
|
101
|
-
config.contracts_path = Rails.root.join("app/contracts")
|
102
|
-
end
|
103
|
-
end
|
85
|
+
Rails.application.routes.draw do
|
86
|
+
# other routes
|
87
|
+
mount Apicraft::Web::App, at: "/apicraft"
|
104
88
|
end
|
105
89
|
```
|
106
90
|
|
107
91
|
Now every API in the specification has a functional version. For any path (from the contracts), APICraft serves a mock response when `Apicraft-Mock: true` is passed in the headers otherwise, it forwards the request to your application as usual.
|
108
92
|
|
93
|
+
4. Generate a sample spec file
|
94
|
+
```
|
95
|
+
rails apicraft:generate file=v2/openapi
|
96
|
+
```
|
97
|
+
|
98
|
+
This will generate a sample file called `app/contracts/v2/openapi.yaml`
|
109
99
|
## โ๏ธ Usage
|
110
100
|
|
111
101
|
Add your specification files to the `app/contracts` directory in your Rails project. You can also configure this directory to be something else.
|
@@ -199,9 +189,7 @@ Example: `https://yoursite.com/api/orders`
|
|
199
189
|
}
|
200
190
|
}
|
201
191
|
],
|
202
|
-
"responses": {
|
203
|
-
...
|
204
|
-
}
|
192
|
+
"responses": {}
|
205
193
|
}
|
206
194
|
```
|
207
195
|
### ๐ Documentation (Swagger docs and RapiDoc)
|
@@ -238,6 +226,17 @@ RapiDoc | SwaggerDoc
|
|
238
226
|
:-------------------------:|:-------------------------:
|
239
227
|
 | 
|
240
228
|
|
229
|
+
### ๐ CLI Support
|
230
|
+
|
231
|
+
To check if all the specification are valid
|
232
|
+
```
|
233
|
+
$ rails apicraft:validate
|
234
|
+
```
|
235
|
+
|
236
|
+
To generate a new spec file in the contracts directory
|
237
|
+
```
|
238
|
+
$ rails apicraft:generate file=openapi
|
239
|
+
```
|
241
240
|
## ๐ง Configuration
|
242
241
|
|
243
242
|
List of available configurations.
|
data/lib/apicraft/config.rb
CHANGED
data/lib/apicraft/loader.rb
CHANGED
data/lib/apicraft/railtie.rb
CHANGED
@@ -4,8 +4,22 @@ module Apicraft
|
|
4
4
|
# Hooks into the application boot process
|
5
5
|
# using Rails::Railtie
|
6
6
|
class Railtie < Rails::Railtie
|
7
|
-
initializer "apicraft.
|
7
|
+
initializer "apicraft.use_middlewares" do
|
8
|
+
[
|
9
|
+
Apicraft::Middlewares::Mocker,
|
10
|
+
Apicraft::Middlewares::Introspector,
|
11
|
+
Apicraft::Middlewares::RequestValidator
|
12
|
+
].each { |mw| Rails.application.config.middleware.use mw }
|
13
|
+
end
|
14
|
+
|
15
|
+
config.after_initialize do
|
8
16
|
Apicraft::Loader.load!
|
9
17
|
end
|
18
|
+
|
19
|
+
rake_tasks do
|
20
|
+
load "apicraft/tasks/validate.rake"
|
21
|
+
load "apicraft/tasks/init.rake"
|
22
|
+
load "apicraft/tasks/generate.rake"
|
23
|
+
end
|
10
24
|
end
|
11
25
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :apicraft do
|
4
|
+
desc "Generate an example spec file"
|
5
|
+
|
6
|
+
task generate: :environment do |_t, _args|
|
7
|
+
arguments = ARGV.reduce({}) do |final, current|
|
8
|
+
key, val = current.split("=").map(&:strip)
|
9
|
+
final.merge!({
|
10
|
+
key => val
|
11
|
+
})
|
12
|
+
end
|
13
|
+
|
14
|
+
filepath = arguments["file"]
|
15
|
+
template = File.expand_path("../templates/openapi.example.yaml", __dir__)
|
16
|
+
|
17
|
+
# root path of all contracts
|
18
|
+
contracts_path = Apicraft.config.contracts_path
|
19
|
+
|
20
|
+
# Split the filepath into parts to extract the directory structure and file name
|
21
|
+
path_parts = filepath.split("/")
|
22
|
+
dir_path = File.join(contracts_path, *path_parts[0..-2])
|
23
|
+
file_name = "#{path_parts[-1]}.yaml"
|
24
|
+
|
25
|
+
# Create the directory if it doesn't exist
|
26
|
+
FileUtils.mkdir_p(dir_path) unless Dir.exist?(dir_path)
|
27
|
+
|
28
|
+
File.write(File.join(dir_path, file_name), File.read(template))
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :apicraft do
|
4
|
+
desc "Initialize apicraft"
|
5
|
+
task init: :environment do
|
6
|
+
# Setup the apicraft initializer
|
7
|
+
destination = Rails.root.join("config", "initializers", "apicraft.rb")
|
8
|
+
if File.exist?(destination)
|
9
|
+
puts "File already exists: #{destination}"
|
10
|
+
else
|
11
|
+
template = File.expand_path("../templates/initializer.rb", __dir__)
|
12
|
+
FileUtils.cp(template, destination)
|
13
|
+
puts "Apicraft initializer created at config/initializers/apicraft.rb"
|
14
|
+
end
|
15
|
+
|
16
|
+
# Create the default contracts directory
|
17
|
+
contracts_path = Apicraft.config.default_contracts_path
|
18
|
+
FileUtils.mkdir_p(contracts_path) unless Dir.exist?(contracts_path)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Apicraft::Web::App.use do |user, password|
|
4
|
+
[user, password] == %w[admin password]
|
5
|
+
end
|
6
|
+
|
7
|
+
Apicraft.configure do |config|
|
8
|
+
config.contracts_path = Rails.root.join("app", "contracts")
|
9
|
+
|
10
|
+
# Enables or disables the mocking features
|
11
|
+
# Defaults to true
|
12
|
+
config.mocks = true
|
13
|
+
|
14
|
+
# Enables or disables the introspection features
|
15
|
+
# Defaults to true
|
16
|
+
config.introspection = true
|
17
|
+
|
18
|
+
# allows you to enforce stricter validation of $ref
|
19
|
+
# references in your OpenAPI specifications.
|
20
|
+
# When this option is enabled, the parser will raise
|
21
|
+
# an error if any $ref references in your OpenAPI
|
22
|
+
# document are invalid, ensuring that all references
|
23
|
+
# are correctly defined and resolved.
|
24
|
+
# Defaults to true
|
25
|
+
config.strict_reference_validation = true
|
26
|
+
|
27
|
+
# When simulating delay using the mocks, the max
|
28
|
+
# delay in seconds that can be simulated
|
29
|
+
config.max_allowed_delay = 0
|
30
|
+
|
31
|
+
config.headers = {
|
32
|
+
# The name of the header used to control
|
33
|
+
# the response code of the mock
|
34
|
+
# Defaults to Apicraft-Response-Code
|
35
|
+
response_code: "Apicraft-Response-Code",
|
36
|
+
|
37
|
+
# The name of the header to introspect the API.
|
38
|
+
# Defaults to Apicraft-Introspect
|
39
|
+
introspect: "Apicraft-Introspect",
|
40
|
+
|
41
|
+
# The name of the header to mock the API.
|
42
|
+
# Defaults to Apicraft-Mock
|
43
|
+
mock: "Apicraft-Mock",
|
44
|
+
|
45
|
+
# Delay simulation header name
|
46
|
+
delay: "Apicraft-Delay"
|
47
|
+
}
|
48
|
+
|
49
|
+
config.request_validation = {
|
50
|
+
enabled: true,
|
51
|
+
|
52
|
+
# Return the http code for validation errors, defaults to 400
|
53
|
+
http_code: 400,
|
54
|
+
|
55
|
+
# Return a custom response body, defaults to `{ message: "..." }`
|
56
|
+
response_body: proc do |ex|
|
57
|
+
{
|
58
|
+
message: ex.message
|
59
|
+
}
|
60
|
+
end
|
61
|
+
}
|
62
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
openapi: 3.0.0
|
2
|
+
info:
|
3
|
+
title: Sample API
|
4
|
+
version: 1.0.0
|
5
|
+
description: API template to manage users.
|
6
|
+
|
7
|
+
servers:
|
8
|
+
- url: https://api.example.com
|
9
|
+
|
10
|
+
paths:
|
11
|
+
/users:
|
12
|
+
get:
|
13
|
+
summary: Get a list of users
|
14
|
+
responses:
|
15
|
+
'200':
|
16
|
+
description: A list of users
|
17
|
+
content:
|
18
|
+
application/json:
|
19
|
+
schema:
|
20
|
+
type: array
|
21
|
+
items:
|
22
|
+
$ref: '#/components/schemas/User'
|
23
|
+
post:
|
24
|
+
summary: Create a new user
|
25
|
+
requestBody:
|
26
|
+
required: true
|
27
|
+
content:
|
28
|
+
application/json:
|
29
|
+
schema:
|
30
|
+
$ref: '#/components/schemas/User'
|
31
|
+
responses:
|
32
|
+
'201':
|
33
|
+
description: User created successfully
|
34
|
+
|
35
|
+
/users/{id}:
|
36
|
+
get:
|
37
|
+
summary: Get a user by id
|
38
|
+
parameters:
|
39
|
+
- name: id
|
40
|
+
in: path
|
41
|
+
required: true
|
42
|
+
schema:
|
43
|
+
type: string
|
44
|
+
responses:
|
45
|
+
'200':
|
46
|
+
description: A user object
|
47
|
+
content:
|
48
|
+
application/json:
|
49
|
+
schema:
|
50
|
+
$ref: '#/components/schemas/User'
|
51
|
+
|
52
|
+
components:
|
53
|
+
schemas:
|
54
|
+
User:
|
55
|
+
type: object
|
56
|
+
properties:
|
57
|
+
id:
|
58
|
+
type: string
|
59
|
+
name:
|
60
|
+
type: string
|
61
|
+
email:
|
62
|
+
type: string
|
63
|
+
required:
|
64
|
+
- id
|
65
|
+
- name
|
66
|
+
- email
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Apicraft
|
4
|
+
# Recursively loads and processes YAML files from the
|
5
|
+
# application's contract folder during the application boot.
|
6
|
+
# This class is responsible for loading and initializing the
|
7
|
+
# contracts defined in the YAML files.
|
8
|
+
class Validator
|
9
|
+
def self.validate!
|
10
|
+
contracts_path = Apicraft.config.contracts_path
|
11
|
+
raise Errors::InvalidContractsPath if contracts_path.blank? || !Dir.exist?(contracts_path)
|
12
|
+
|
13
|
+
Find.find(contracts_path) do |path|
|
14
|
+
next unless File.file?(path) && %w[.yaml .yml .json].include?(File.extname(path))
|
15
|
+
|
16
|
+
validate_file!(path)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.validate_file!(file)
|
21
|
+
ext = File.extname(file)
|
22
|
+
|
23
|
+
parsed = if ext == ".json"
|
24
|
+
JSON.parse(File.read(file))
|
25
|
+
else
|
26
|
+
YAML.load_file(file)
|
27
|
+
end
|
28
|
+
|
29
|
+
OpenAPIParser.parse(
|
30
|
+
parsed,
|
31
|
+
{
|
32
|
+
strict_reference_validation: config.strict_reference_validation
|
33
|
+
}
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.config
|
38
|
+
Apicraft.config
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/apicraft/version.rb
CHANGED
data/lib/apicraft-rails.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apicraft-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abhishek Sarkar
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -151,6 +151,12 @@ files:
|
|
151
151
|
- lib/apicraft/openapi/operation.rb
|
152
152
|
- lib/apicraft/openapi/response.rb
|
153
153
|
- lib/apicraft/railtie.rb
|
154
|
+
- lib/apicraft/tasks/generate.rake
|
155
|
+
- lib/apicraft/tasks/init.rake
|
156
|
+
- lib/apicraft/tasks/validate.rake
|
157
|
+
- lib/apicraft/templates/initializer.rb
|
158
|
+
- lib/apicraft/templates/openapi.example.yaml
|
159
|
+
- lib/apicraft/validator.rb
|
154
160
|
- lib/apicraft/version.rb
|
155
161
|
- lib/apicraft/web.rb
|
156
162
|
- lib/apicraft/web/actions.rb
|