apicraft-rails 1.0.1 โ†’ 1.0.2

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 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