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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 985bb9a26327f539fba9cfde3fecaab346e445100d32549bab4e393d6656d8b6
4
- data.tar.gz: 9df0525f057680a45d18dd5ce899f0151e09366d62ab3b190e895c473e79f4d8
3
+ metadata.gz: b30ecb65e234276a9d0af7dca63778397b8e1873a1a6a062b92853e791b980ba
4
+ data.tar.gz: da1c7cffc242a5008aa0166ae6ac874a4f6739def0d928c3e5417361c492cbbf
5
5
  SHA512:
6
- metadata.gz: '078e7d785cdeb7f95b36984e57dbf8616e2f12832d0b272e589d28f2e1a20ab8dee73f8775ae270ac0293f8b8d974a726d58934adf5bd4308b616fcfbbb69e96'
7
- data.tar.gz: 571165414ba898067e3b1d28c9daa0aacb20d8ddb350ac425ea6ac9198066bfe90844a25df3bd2fe47f077c0df1b0a1fa087b2e56f4db62602f5bc225eee8e84
6
+ metadata.gz: be09f4dd790950e1857c8cec15e945eac4058ac891de58fd8218a3b4c3cf033df314ccc951197235e2a5f5045cf30ef2b5e751acb42fb1988c9eabd1bd6a344c
7
+ data.tar.gz: 11da384ef306df936c9e1d288552da41001f6c4c7084617f76c0dfaf3edb85312bb22bf21914bd9e034cf36935bc5b79320e9c201f7e10f3a7a9c5db398e1dbe
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # APICraft Rails
2
2
  [![Build](https://github.com/apicraft-dev/apicraft-rails/actions/workflows/build.yml/badge.svg)](https://github.com/apicraft-dev/apicraft-rails/actions/workflows/build.yml)
3
- [![Gem Version](https://d25lcipzij17d.cloudfront.net/badge.png?id=rb&r=r&ts=1683906897&type=3e&v=1.0.1&x2=0)](https://badge.fury.io/rb/apicraft-rails)
3
+ [![Gem Version](https://d25lcipzij17d.cloudfront.net/badge.png?id=rb&r=r&ts=1683906897&type=3e&v=1.0.2&x2=0)](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
- ## ๐Ÿ”œ Upcoming Features
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.1'
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
- $ bundle install
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 following into your Rails application, via the `config/application.rb`
82
+ 3. Add the `apicraft` route to your route file (for documentation):
87
83
 
88
84
  ```ruby
89
- # config/application.rb
90
- module App
91
- class Application < Rails::Application
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
  ![](assets/rapidoc.png) | ![](assets/swaggerdoc.png)
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.
@@ -43,6 +43,10 @@ module Apicraft
43
43
  @opts[:contracts_path]
44
44
  end
45
45
 
46
+ def default_contracts_path
47
+ Rails.root.join("app", "contracts")
48
+ end
49
+
46
50
  def mocks
47
51
  @opts[:mocks]
48
52
  end
@@ -38,7 +38,8 @@ module Apicraft
38
38
  OpenAPIParser.parse(
39
39
  parsed,
40
40
  {
41
- strict_reference_validation: config.strict_reference_validation
41
+ strict_reference_validation: config.strict_reference_validation,
42
+ expand_reference: true
42
43
  }
43
44
  )
44
45
 
@@ -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.load_api_contracts" do
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,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :apicraft do
4
+ desc "Validate all contracts"
5
+ task validate: :environment do
6
+ Apicraft::Validator.validate!
7
+ end
8
+ 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
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Current version of Apicraft.
4
4
  module Apicraft
5
- VERSION = "1.0.1"
5
+ VERSION = "1.0.2"
6
6
  end
@@ -19,6 +19,7 @@ require_relative "apicraft/errors"
19
19
  require_relative "apicraft/mocker"
20
20
  require_relative "apicraft/openapi"
21
21
 
22
+ require_relative "apicraft/validator"
22
23
  require_relative "apicraft/loader"
23
24
  require_relative "apicraft/railtie"
24
25
 
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.1
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-12 00:00:00.000000000 Z
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