api-regulator 0.1.1 → 0.1.3
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/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -3
- data/bin/console +1 -1
- data/bin/release +33 -0
- data/lib/api-regulator.rb +4 -2
- data/lib/api_regulator/api.rb +26 -6
- data/lib/api_regulator/configuration.rb +2 -7
- data/lib/api_regulator/dsl.rb +9 -2
- data/lib/api_regulator/open_api_generator.rb +50 -16
- data/lib/api_regulator/param.rb +12 -3
- data/lib/api_regulator/security.rb +13 -0
- data/lib/api_regulator/shared_schema.rb +18 -9
- data/lib/api_regulator/validator.rb +4 -5
- data/lib/api_regulator/version.rb +1 -1
- data/lib/api_regulator/webhook.rb +59 -0
- data/lib/tasks/api_regulator_tasks.rake +137 -6
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ad25c4c5446595f4f4b6f25511aef1277c8d446759e00c52a41336e7ce2aa5a
|
4
|
+
data.tar.gz: 92d627378f46e14e66eab66dfbadb228bd84b17caad7d5ef69301399f56ed0d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '091373d00ad931601cffcda761ec1d1d002d84b91ae8f4abd42a5633453437f294ae522b4d4840a40281d0e3cbd3acca1f26a8eda51f186eac93aa7adce4a043'
|
7
|
+
data.tar.gz: 875c4c38d98f26575d9a77f6bbe7629105b5d5145068a3800e253059092d6bf4101a9e0cc327559e7a3d6ff3032eabbe1d223937922f6c3608a16d4f4cb4ed3b
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -48,9 +48,8 @@ Run `bundle install` to install the gem.
|
|
48
48
|
|
49
49
|
```ruby
|
50
50
|
ApiRegulator.configure do |config|
|
51
|
-
config.base_controller = "Api::ApplicationController" # Set your base API controller
|
52
51
|
config.api_base_url = "/api/v1" # Set a common base path for your API endpoints
|
53
|
-
config.docs_path = Rails.root.join("doc"
|
52
|
+
config.docs_path = Rails.root.join("doc").to_s # Path for folder for docs
|
54
53
|
config.app_name = "My API" # shows in docs
|
55
54
|
config.rdme_api_id = ENV["RDME_API_ID"] # Optional: ReadMe API ID for schema uploads
|
56
55
|
config.servers = [
|
@@ -111,7 +110,7 @@ end
|
|
111
110
|
Define reusable schemas for common responses in your initializer:
|
112
111
|
|
113
112
|
```ruby
|
114
|
-
ApiRegulator.
|
113
|
+
ApiRegulator.register_shared_schema :validation_errors, "Validation error response" do
|
115
114
|
param :errors, :array, desc: "Array of validation errors", items_type: :string
|
116
115
|
end
|
117
116
|
```
|
data/bin/console
CHANGED
data/bin/release
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
# Load the gem version dynamically
|
6
|
+
GEMSPEC=$(ls *.gemspec | head -n 1)
|
7
|
+
GEM_NAME=$(basename "$GEMSPEC" .gemspec)
|
8
|
+
VERSION=$(ruby -r "./lib/api_regulator/version" -e "puts ApiRegulator::VERSION")
|
9
|
+
|
10
|
+
echo "Checking if version $VERSION of $GEM_NAME exists on RubyGems..."
|
11
|
+
|
12
|
+
# Check if the version already exists on RubyGems
|
13
|
+
if gem list -r "^${GEM_NAME}$" | grep -q "${VERSION}"; then
|
14
|
+
echo "Version $VERSION of $GEM_NAME already exists. Skipping release."
|
15
|
+
exit 0
|
16
|
+
fi
|
17
|
+
|
18
|
+
echo "Building gem: $GEM_NAME version $VERSION"
|
19
|
+
|
20
|
+
# Build the gem
|
21
|
+
gem build "$GEMSPEC"
|
22
|
+
|
23
|
+
# Push the gem to RubyGems
|
24
|
+
GEM_FILE="${GEM_NAME}-${VERSION}.gem"
|
25
|
+
if [ -f "$GEM_FILE" ]; then
|
26
|
+
echo "Pushing gem: $GEM_FILE"
|
27
|
+
gem push "$GEM_FILE"
|
28
|
+
else
|
29
|
+
echo "Error: Gem file $GEM_FILE not found!"
|
30
|
+
exit 1
|
31
|
+
fi
|
32
|
+
|
33
|
+
echo "Release successful: $GEM_FILE"
|
data/lib/api-regulator.rb
CHANGED
@@ -5,10 +5,12 @@ require_relative 'api_regulator/dsl'
|
|
5
5
|
require_relative 'api_regulator/formats'
|
6
6
|
require_relative 'api_regulator/open_api_generator'
|
7
7
|
require_relative 'api_regulator/param'
|
8
|
+
require_relative 'api_regulator/security'
|
8
9
|
require_relative 'api_regulator/shared_schema'
|
9
10
|
require_relative 'api_regulator/validation_error'
|
10
11
|
require_relative 'api_regulator/validator'
|
11
12
|
require_relative 'api_regulator/version'
|
13
|
+
require_relative 'api_regulator/webhook'
|
12
14
|
|
13
15
|
# Load tasks if Rails is present
|
14
16
|
if defined?(Rake)
|
@@ -28,8 +30,8 @@ module ApiRegulator
|
|
28
30
|
|
29
31
|
def prepare_validators
|
30
32
|
Rails.application.eager_load! # Ensure all controllers and API definitions are loaded
|
31
|
-
|
32
|
-
ApiRegulator::Validator.build_all(api_definitions)
|
33
|
+
|
34
|
+
ApiRegulator::Validator.build_all(ApiRegulator.api_definitions)
|
33
35
|
end
|
34
36
|
end
|
35
37
|
end
|
data/lib/api_regulator/api.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
module ApiRegulator
|
2
2
|
class Api
|
3
|
-
attr_reader :controller_class, :controller_path, :controller_name, :action_name, :description, :params, :responses
|
3
|
+
attr_reader :controller_class, :controller_path, :controller_name, :action_name, :description, :title, :params, :responses
|
4
4
|
|
5
|
-
def initialize(controller_class, action_name,
|
5
|
+
def initialize(controller_class, action_name, desc: nil, title: nil, &block)
|
6
6
|
@controller_class = controller_class
|
7
7
|
@controller_name = controller_class.name
|
8
8
|
@controller_path = controller_class.controller_path
|
9
9
|
@action_name = action_name.to_s
|
10
|
-
@description =
|
10
|
+
@description = desc
|
11
|
+
@title = title
|
11
12
|
|
12
13
|
@params = []
|
13
14
|
@responses = {}
|
@@ -20,11 +21,20 @@ module ApiRegulator
|
|
20
21
|
@params << param
|
21
22
|
end
|
22
23
|
|
23
|
-
def ref(ref_name)
|
24
|
-
shared_schema = ApiRegulator.
|
24
|
+
def ref(ref_name, except: [], only: [])
|
25
|
+
shared_schema = ApiRegulator.shared_schema(ref_name)
|
25
26
|
raise "Shared schema #{ref_name} not found" unless shared_schema
|
26
27
|
|
27
|
-
|
28
|
+
# Filter parameters based on `only` or `except` options
|
29
|
+
filtered_params = shared_schema.params
|
30
|
+
|
31
|
+
if only.any?
|
32
|
+
filtered_params = filtered_params.select { |param| only.include?(param.name) }
|
33
|
+
elsif except.any?
|
34
|
+
filtered_params = filtered_params.reject { |param| except.include?(param.name) }
|
35
|
+
end
|
36
|
+
|
37
|
+
filtered_params.each do |shared_param|
|
28
38
|
@params << shared_param
|
29
39
|
end
|
30
40
|
end
|
@@ -79,4 +89,14 @@ module ApiRegulator
|
|
79
89
|
http_method != "get"
|
80
90
|
end
|
81
91
|
end
|
92
|
+
|
93
|
+
class << self
|
94
|
+
def api_definitions
|
95
|
+
@api_definitions ||= []
|
96
|
+
end
|
97
|
+
|
98
|
+
def reset_api_definitions
|
99
|
+
@api_definitions = []
|
100
|
+
end
|
101
|
+
end
|
82
102
|
end
|
@@ -1,18 +1,13 @@
|
|
1
1
|
module ApiRegulator
|
2
2
|
class Configuration
|
3
|
-
attr_accessor :
|
3
|
+
attr_accessor :api_base_url, :app_name, :docs_path, :rdme_api_id, :servers
|
4
4
|
|
5
5
|
def initialize
|
6
6
|
# Set default values
|
7
|
-
@base_controller = "ApplicationController"
|
8
7
|
@api_base_url = "api/v1"
|
9
8
|
@app_name = "API Documentation"
|
10
|
-
@docs_path = "
|
9
|
+
@docs_path = "doc"
|
11
10
|
@servers = []
|
12
11
|
end
|
13
|
-
|
14
|
-
def base_controller_klass
|
15
|
-
ApiRegulator.configuration.base_controller.constantize
|
16
|
-
end
|
17
12
|
end
|
18
13
|
end
|
data/lib/api_regulator/dsl.rb
CHANGED
@@ -9,17 +9,24 @@ module ApiRegulator
|
|
9
9
|
end
|
10
10
|
|
11
11
|
module ClassMethods
|
12
|
-
def api(controller_class, action,
|
12
|
+
def api(controller_class, action, desc: nil, title: nil, &block)
|
13
13
|
@api_definitions ||= []
|
14
14
|
|
15
15
|
api_definition = Api.new(
|
16
16
|
controller_class,
|
17
17
|
action.to_s,
|
18
|
-
|
18
|
+
desc: desc,
|
19
|
+
title: title,
|
19
20
|
&block
|
20
21
|
)
|
21
22
|
|
22
23
|
@api_definitions << api_definition
|
24
|
+
ApiRegulator.api_definitions << api_definition
|
25
|
+
end
|
26
|
+
|
27
|
+
def webhook(event_name, desc: nil, title: nil, tags: [], &block)
|
28
|
+
webhook = Webhook.new(event_name, desc: desc, title: title, tags: tags, &block)
|
29
|
+
ApiRegulator.webhook_definitions << webhook
|
23
30
|
end
|
24
31
|
|
25
32
|
def api_definitions
|
@@ -16,13 +16,15 @@ module ApiRegulator
|
|
16
16
|
|
17
17
|
add_components(schema)
|
18
18
|
add_security(schema)
|
19
|
+
add_webhooks(schema)
|
19
20
|
|
20
21
|
api_definitions.each do |api|
|
21
22
|
add_api_to_schema(schema, api)
|
22
23
|
end
|
23
24
|
|
24
|
-
|
25
|
-
|
25
|
+
schema_path = "#{ApiRegulator.configuration.docs_path}/openapi.json"
|
26
|
+
File.write(schema_path, JSON.pretty_generate(schema))
|
27
|
+
puts "OpenAPI schema generated: #{schema_path}"
|
26
28
|
end
|
27
29
|
|
28
30
|
private
|
@@ -30,13 +32,13 @@ module ApiRegulator
|
|
30
32
|
def self.add_api_to_schema(schema, api)
|
31
33
|
schema[:paths][api.path] ||= {}
|
32
34
|
data = {
|
33
|
-
summary: api.
|
34
|
-
description: api.description,
|
35
|
+
summary: api.title,
|
36
|
+
description: api.description.presence || api.title,
|
35
37
|
operationId: api.operation_id,
|
36
38
|
tags: api.tags,
|
37
39
|
parameters: generate_parameters(api),
|
38
40
|
responses: generate_responses(api)
|
39
|
-
}
|
41
|
+
}.compact
|
40
42
|
|
41
43
|
data[:requestBody] = generate_request_body(api) if api.allows_body?
|
42
44
|
data.delete(:requestBody) if data[:requestBody].blank?
|
@@ -113,16 +115,20 @@ module ApiRegulator
|
|
113
115
|
def self.generate_param_schema(param)
|
114
116
|
schema = {}
|
115
117
|
|
118
|
+
schema[:description] = param.desc if param.desc.present?
|
119
|
+
|
116
120
|
if param.parameter?
|
117
121
|
schema[:in] = param.location
|
118
122
|
schema[:schema] = { type: param.type.to_s.downcase }
|
123
|
+
generate_param_schema_details(param, schema[:schema])
|
119
124
|
else
|
120
125
|
schema[:type] = param.type.to_s.downcase
|
126
|
+
generate_param_schema_details(param, schema)
|
121
127
|
end
|
128
|
+
schema
|
129
|
+
end
|
122
130
|
|
123
|
-
|
124
|
-
|
125
|
-
|
131
|
+
def self.generate_param_schema_details(param, schema)
|
126
132
|
# Add length constraints
|
127
133
|
if param.options[:length]
|
128
134
|
schema[:minLength] = param.options[:length][:minimum] if param.options[:length][:minimum]
|
@@ -142,12 +148,12 @@ module ApiRegulator
|
|
142
148
|
end
|
143
149
|
end
|
144
150
|
|
145
|
-
if param.options[:numericality]
|
146
|
-
schema[:type] =
|
147
|
-
schema[:minimum] =
|
148
|
-
schema[:maximum] =
|
149
|
-
schema[:exclusiveMinimum] =
|
150
|
-
schema[:exclusiveMaximum] =
|
151
|
+
if numericality = param.options[:numericality]
|
152
|
+
schema[:type] = numericality[:only_integer] || schema[:type] == "integer" ? 'integer' : 'number'
|
153
|
+
schema[:minimum] = numericality[:greater_than_or_equal_to] if numericality[:greater_than_or_equal_to]
|
154
|
+
schema[:maximum] = numericality[:less_than_or_equal_to] if numericality[:less_than_or_equal_to]
|
155
|
+
schema[:exclusiveMinimum] = numericality[:greater_than] if numericality[:greater_than]
|
156
|
+
schema[:exclusiveMaximum] = numericality[:less_than] if numericality[:less_than]
|
151
157
|
end
|
152
158
|
|
153
159
|
if param.options[:inclusion]
|
@@ -160,13 +166,12 @@ module ApiRegulator
|
|
160
166
|
|
161
167
|
schema[:nullable] = true if param.options[:allow_nil]
|
162
168
|
|
163
|
-
schema
|
164
169
|
end
|
165
170
|
|
166
171
|
def self.generate_responses(api)
|
167
172
|
api.responses.each_with_object({}) do |(status_code, schema), responses|
|
168
173
|
if schema.options[:ref]
|
169
|
-
shared_schema = ApiRegulator.
|
174
|
+
shared_schema = ApiRegulator.shared_schema(schema.options[:ref])
|
170
175
|
raise "Shared schema not found for ref: #{schema.options[:ref]}" unless shared_schema
|
171
176
|
|
172
177
|
responses[status_code.to_s] = {
|
@@ -177,6 +182,10 @@ module ApiRegulator
|
|
177
182
|
}
|
178
183
|
}
|
179
184
|
}.compact
|
185
|
+
elsif schema.children.empty?
|
186
|
+
responses[status_code.to_s] = {
|
187
|
+
description: schema.desc.presence
|
188
|
+
}
|
180
189
|
else
|
181
190
|
responses[status_code.to_s] = {
|
182
191
|
description: schema.desc.presence,
|
@@ -195,6 +204,31 @@ module ApiRegulator
|
|
195
204
|
add_security_schemes(schema)
|
196
205
|
end
|
197
206
|
|
207
|
+
def self.add_webhooks(schema)
|
208
|
+
return if ApiRegulator.webhook_definitions.empty?
|
209
|
+
|
210
|
+
schema[:webhooks] ||= {}
|
211
|
+
ApiRegulator.webhook_definitions.each do |webhook|
|
212
|
+
schema[:webhooks][webhook.event_name] = {
|
213
|
+
post: {
|
214
|
+
summary: webhook.title,
|
215
|
+
description: webhook.description.presence || webhook.title,
|
216
|
+
tags: webhook.tags,
|
217
|
+
requestBody: {
|
218
|
+
required: true,
|
219
|
+
content: {
|
220
|
+
'application/json' => {
|
221
|
+
schema: expand_nested_params(webhook.params),
|
222
|
+
examples: webhook.examples || {}
|
223
|
+
}
|
224
|
+
}
|
225
|
+
},
|
226
|
+
responses: generate_responses(webhook)
|
227
|
+
}.compact
|
228
|
+
}
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
198
232
|
def self.add_shared_schemas(schema)
|
199
233
|
return unless ApiRegulator.shared_schemas.present?
|
200
234
|
|
data/lib/api_regulator/param.rb
CHANGED
@@ -19,11 +19,20 @@ module ApiRegulator
|
|
19
19
|
@children << child
|
20
20
|
end
|
21
21
|
|
22
|
-
def ref(ref_name)
|
23
|
-
shared_schema = ApiRegulator.
|
22
|
+
def ref(ref_name, except: [], only: [])
|
23
|
+
shared_schema = ApiRegulator.shared_schema(ref_name)
|
24
24
|
raise "Shared schema #{ref_name} not found" unless shared_schema
|
25
25
|
|
26
|
-
|
26
|
+
# Filter parameters based on `only` or `except` options
|
27
|
+
filtered_params = shared_schema.params
|
28
|
+
|
29
|
+
if only.any?
|
30
|
+
filtered_params = filtered_params.select { |param| only.include?(param.name) }
|
31
|
+
elsif except.any?
|
32
|
+
filtered_params = filtered_params.reject { |param| except.include?(param.name) }
|
33
|
+
end
|
34
|
+
|
35
|
+
filtered_params.each do |shared_param|
|
27
36
|
@children << shared_param
|
28
37
|
end
|
29
38
|
end
|
@@ -15,23 +15,32 @@ module ApiRegulator
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
@shared_schemas = {}
|
19
|
+
@shared_schema_registry = {}
|
20
|
+
|
18
21
|
class << self
|
19
|
-
attr_accessor :
|
22
|
+
attr_accessor :shared_schema_registry
|
20
23
|
|
21
24
|
def shared_schemas
|
22
|
-
|
23
|
-
|
25
|
+
shared_schema_registry.each do |name, (description, block)|
|
26
|
+
@shared_schemas[name] = SharedSchema.new(name, description, &block)
|
27
|
+
end
|
24
28
|
|
25
|
-
|
26
|
-
@security_schemes ||= {}
|
29
|
+
@shared_schemas
|
27
30
|
end
|
28
31
|
|
29
|
-
def shared_schema(name
|
30
|
-
|
32
|
+
def shared_schema(name)
|
33
|
+
if shared_schema_registry[name]
|
34
|
+
description, block = shared_schema_registry.delete(name)
|
35
|
+
|
36
|
+
@shared_schemas[name] = SharedSchema.new(name, description, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
@shared_schemas[name]
|
31
40
|
end
|
32
41
|
|
33
|
-
def
|
34
|
-
|
42
|
+
def register_shared_schema(name, description, &block)
|
43
|
+
shared_schema_registry[name] = [description, block]
|
35
44
|
end
|
36
45
|
end
|
37
46
|
end
|
@@ -232,6 +232,10 @@ module ApiRegulator
|
|
232
232
|
end
|
233
233
|
end
|
234
234
|
|
235
|
+
def self.reset_validators
|
236
|
+
@validators = {}
|
237
|
+
end
|
238
|
+
|
235
239
|
def initialize(attributes = {})
|
236
240
|
@raw_attributes = attributes.deep_symbolize_keys
|
237
241
|
allowed_attributes = attributes.slice(*self.class.defined_attributes.map(&:to_sym))
|
@@ -239,8 +243,3 @@ module ApiRegulator
|
|
239
243
|
end
|
240
244
|
end
|
241
245
|
end
|
242
|
-
|
243
|
-
class FooValidator
|
244
|
-
include ActiveModel::Model
|
245
|
-
include ActiveModel::Attributes
|
246
|
-
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module ApiRegulator
|
2
|
+
class Webhook
|
3
|
+
attr_reader :event_name, :description, :params, :responses, :examples, :tags, :title
|
4
|
+
|
5
|
+
def initialize(event_name, desc: nil, title: nil, tags: [], &block)
|
6
|
+
@event_name = event_name
|
7
|
+
@description = desc
|
8
|
+
@title = title
|
9
|
+
@tags = tags
|
10
|
+
@params = []
|
11
|
+
@responses = {}
|
12
|
+
|
13
|
+
instance_eval(&block) if block_given?
|
14
|
+
end
|
15
|
+
|
16
|
+
def param(name, type = nil, item_type: nil, desc: "", location: :body, **options, &block)
|
17
|
+
param = Param.new(name, type, item_type: item_type, desc: desc, location: location, **options, &block)
|
18
|
+
@params << param
|
19
|
+
end
|
20
|
+
|
21
|
+
def ref(ref_name, except: [], only: [])
|
22
|
+
shared_schema = ApiRegulator.shared_schema(ref_name)
|
23
|
+
raise "Shared schema #{ref_name} not found" unless shared_schema
|
24
|
+
|
25
|
+
# Filter parameters based on `only` or `except` options
|
26
|
+
filtered_params = shared_schema.params
|
27
|
+
|
28
|
+
if only.any?
|
29
|
+
filtered_params = filtered_params.select { |param| only.include?(param.name) }
|
30
|
+
elsif except.any?
|
31
|
+
filtered_params = filtered_params.reject { |param| except.include?(param.name) }
|
32
|
+
end
|
33
|
+
|
34
|
+
filtered_params.each do |shared_param|
|
35
|
+
@params << shared_param
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def response(status_code, description, &block)
|
40
|
+
@responses[status_code] = Param.new(:root, :object, desc: description, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def example(name, value, default: false)
|
44
|
+
@examples ||= {}
|
45
|
+
@examples[name] = { summary: "#{name} Example", value: value }
|
46
|
+
@default_example = value if default
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class << self
|
51
|
+
def webhook_definitions
|
52
|
+
@webhook_definitions ||= []
|
53
|
+
end
|
54
|
+
|
55
|
+
def reset_webhook_definitions
|
56
|
+
@webhook_definitions = []
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -1,10 +1,11 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
1
3
|
namespace :api_docs do
|
2
4
|
desc 'Generate OpenAPI schema'
|
3
5
|
task generate: :environment do
|
4
|
-
Rails.application.eager_load!
|
6
|
+
Rails.application.eager_load! # Ensure all controllers and API definitions are loaded
|
5
7
|
|
6
|
-
|
7
|
-
ApiRegulator::OpenApiGenerator.generate(api_definitions)
|
8
|
+
ApiRegulator::OpenApiGenerator.generate(ApiRegulator.api_definitions)
|
8
9
|
end
|
9
10
|
|
10
11
|
desc "Upload OpenAPI schema to ReadMe"
|
@@ -16,10 +17,11 @@ namespace :api_docs do
|
|
16
17
|
readme_api_endpoint = "https://dash.readme.com/api/v1/api-specification"
|
17
18
|
|
18
19
|
# Read the OpenAPI schema file
|
19
|
-
|
20
|
-
|
20
|
+
schema_path = "#{ApiRegulator.configuration.docs_path}/openapi.json"
|
21
|
+
unless File.exist?(schema_path)
|
22
|
+
raise "OpenAPI schema file not found at #{schema_path}"
|
21
23
|
end
|
22
|
-
openapi_content = File.read(
|
24
|
+
openapi_content = File.read(schema_path)
|
23
25
|
|
24
26
|
# Upload to ReadMe
|
25
27
|
require 'net/http'
|
@@ -63,5 +65,134 @@ namespace :api_docs do
|
|
63
65
|
task publish: :environment do
|
64
66
|
Rake::Task["api_docs:generate"].invoke
|
65
67
|
Rake::Task["api_docs:upload"].invoke
|
68
|
+
Rake::Task["api_docs:upload_pages"].invoke
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "Upload custom pages to ReadMe"
|
72
|
+
task :upload_pages => :environment do
|
73
|
+
# Configuration
|
74
|
+
readme_api_key = ENV['RDME_API_KEY'] || raise("RDME_API_KEY is not set")
|
75
|
+
base_readme_api_endpoint = "https://dash.readme.com/api/v1/docs"
|
76
|
+
|
77
|
+
# Discover all documentation files
|
78
|
+
pages_directory = "#{ApiRegulator.configuration.docs_path}/**/*.md"
|
79
|
+
page_files = Dir.glob(pages_directory)
|
80
|
+
|
81
|
+
# Iterate through each file
|
82
|
+
page_files.each do |file_path|
|
83
|
+
# Extract metadata and body
|
84
|
+
metadata, body = parse_markdown_file(file_path)
|
85
|
+
raise "No metadata found in #{file_path}" unless metadata
|
86
|
+
|
87
|
+
# Use metadata to build the API request
|
88
|
+
slug = metadata["slug"] || File.basename(file_path, ".md").gsub("_", "-")
|
89
|
+
body = {
|
90
|
+
type: "basic",
|
91
|
+
categorySlug: "documentation",
|
92
|
+
hidden: false
|
93
|
+
}.merge(metadata)
|
94
|
+
body["slug"] ||= slug
|
95
|
+
|
96
|
+
raise("Title missing in #{file_path}") unless body["title"].present?
|
97
|
+
|
98
|
+
# Build the API request
|
99
|
+
if check_if_page_exists(slug)
|
100
|
+
uri = URI.parse("#{base_readme_api_endpoint}/#{slug}")
|
101
|
+
request = Net::HTTP::Put.new(uri)
|
102
|
+
else
|
103
|
+
uri = URI.parse("#{base_readme_api_endpoint}")
|
104
|
+
request = Net::HTTP::Post.new(uri)
|
105
|
+
end
|
106
|
+
request["Authorization"] = "Basic #{Base64.strict_encode64(readme_api_key)}"
|
107
|
+
request["Content-Type"] = "application/json"
|
108
|
+
request.body = body.compact.to_json
|
109
|
+
|
110
|
+
# Send the request
|
111
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
112
|
+
http.request(request)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Handle the response
|
116
|
+
case response.code.to_i
|
117
|
+
when 200
|
118
|
+
puts "Page '#{body["title"]}' successfully updated!"
|
119
|
+
when 201
|
120
|
+
puts "Page '#{body["title"]}' successfully created!"
|
121
|
+
else
|
122
|
+
puts "Failed to upload page '#{body["title"]}'!"
|
123
|
+
puts "Response Code: #{response.code}"
|
124
|
+
puts "Response Body: #{response.body}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
desc "View all categories on Readme.com"
|
130
|
+
task :fetch_categories => :environment do
|
131
|
+
# Configuration
|
132
|
+
readme_api_key = ENV['RDME_API_KEY'] || raise("RDME_API_KEY is not set")
|
133
|
+
readme_categories_api_endpoint = "https://dash.readme.com/api/v1/categories"
|
134
|
+
|
135
|
+
# Build the API request
|
136
|
+
uri = URI.parse(readme_categories_api_endpoint)
|
137
|
+
request = Net::HTTP::Get.new(uri)
|
138
|
+
request["Authorization"] = "Basic #{Base64.strict_encode64(readme_api_key)}"
|
139
|
+
request["Content-Type"] = "application/json"
|
140
|
+
|
141
|
+
# Send the request
|
142
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
143
|
+
http.request(request)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Handle the response
|
147
|
+
case response.code.to_i
|
148
|
+
when 200
|
149
|
+
puts "Categories"
|
150
|
+
pp JSON.parse(response.body)
|
151
|
+
else
|
152
|
+
puts "Failed to fetch categories"
|
153
|
+
puts "Response Code: #{response.code}"
|
154
|
+
puts "Response Body: #{response.body}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def parse_markdown_file(file_path)
|
159
|
+
content = File.read(file_path)
|
160
|
+
metadata = nil
|
161
|
+
body = ""
|
162
|
+
|
163
|
+
# Split metadata and body
|
164
|
+
if content =~ /\A---\s*\n(.*?)\n---\s*\n(.*)/m
|
165
|
+
metadata = YAML.safe_load($1)
|
166
|
+
body = $2
|
167
|
+
else
|
168
|
+
raise "YAML front matter missing in #{file_path}"
|
169
|
+
end
|
170
|
+
|
171
|
+
[metadata, body]
|
172
|
+
end
|
173
|
+
|
174
|
+
def check_if_page_exists(slug)
|
175
|
+
readme_api_key = ENV['RDME_API_KEY'] || raise("RDME_API_KEY is not set")
|
176
|
+
uri = URI.parse("https://dash.readme.com/api/v1/docs/#{slug}")
|
177
|
+
request = Net::HTTP::Get.new(uri)
|
178
|
+
request["Authorization"] = "Basic #{Base64.strict_encode64(readme_api_key)}"
|
179
|
+
request["Content-Type"] = "application/json"
|
180
|
+
|
181
|
+
# Send the request
|
182
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
183
|
+
http.request(request)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Handle the response
|
187
|
+
case response.code.to_i
|
188
|
+
when 200
|
189
|
+
true
|
190
|
+
when 404
|
191
|
+
false
|
192
|
+
else
|
193
|
+
puts "Failed to serach for page"
|
194
|
+
puts "Response Code: #{response.code}"
|
195
|
+
puts "Response Body: #{response.body}"
|
196
|
+
end
|
66
197
|
end
|
67
198
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api-regulator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Geoff Massanek
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -142,6 +142,7 @@ files:
|
|
142
142
|
- Rakefile
|
143
143
|
- api-regulator.gemspec
|
144
144
|
- bin/console
|
145
|
+
- bin/release
|
145
146
|
- bin/setup
|
146
147
|
- lib/api-regulator.rb
|
147
148
|
- lib/api_regulator.rb
|
@@ -152,10 +153,12 @@ files:
|
|
152
153
|
- lib/api_regulator/formats.rb
|
153
154
|
- lib/api_regulator/open_api_generator.rb
|
154
155
|
- lib/api_regulator/param.rb
|
156
|
+
- lib/api_regulator/security.rb
|
155
157
|
- lib/api_regulator/shared_schema.rb
|
156
158
|
- lib/api_regulator/validation_error.rb
|
157
159
|
- lib/api_regulator/validator.rb
|
158
160
|
- lib/api_regulator/version.rb
|
161
|
+
- lib/api_regulator/webhook.rb
|
159
162
|
- lib/tasks/api_regulator_tasks.rake
|
160
163
|
homepage: https://github.com/Stellarcred/stellar-gears
|
161
164
|
licenses:
|