apicasso 0.1.2 → 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/README.md +84 -84
- data/Rakefile +32 -32
- data/app/assets/config/apicasso_manifest.js +2 -2
- data/app/assets/javascripts/apicasso/application.js +15 -15
- data/app/assets/stylesheets/apicasso/application.css +15 -15
- data/app/controllers/apicasso/apidocs_controller.rb +0 -0
- data/app/controllers/apicasso/application_controller.rb +104 -104
- data/app/controllers/apicasso/crud_controller.rb +148 -148
- data/app/controllers/concerns/orderable.rb +44 -44
- data/app/helpers/apicasso/application_helper.rb +4 -4
- data/app/jobs/apicasso/application_job.rb +4 -4
- data/app/mailers/apicasso/application_mailer.rb +6 -6
- data/app/models/apicasso/ability.rb +40 -40
- data/app/models/apicasso/application_record.rb +6 -6
- data/app/models/apicasso/key.rb +25 -25
- data/app/models/apicasso/request.rb +8 -8
- data/app/views/layouts/apicasso/application.html.erb +16 -16
- data/config/routes.rb +13 -13
- data/lib/apicasso/active_record_extension.rb +0 -0
- data/lib/apicasso/engine.rb +6 -6
- data/lib/apicasso/version.rb +3 -3
- data/lib/apicasso.rb +9 -9
- data/lib/generators/apicasso/install/install_generator.rb +25 -25
- data/lib/generators/apicasso/install/templates/create_apicasso_tables.rb +17 -17
- data/lib/tasks/apicasso_tasks.rake +4 -4
- metadata +3 -3
@@ -1,148 +1,148 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Apicasso
|
4
|
-
# Controller to consume read-only data to be used on client's frontend
|
5
|
-
class CrudController < Apicasso::ApplicationController
|
6
|
-
before_action :set_root_resource
|
7
|
-
before_action :set_object, except: %i[index schema create]
|
8
|
-
before_action :set_nested_resource, only: %i[nested_index]
|
9
|
-
before_action :set_records, only: %i[index nested_index]
|
10
|
-
before_action :set_schema, only: %i[schema]
|
11
|
-
|
12
|
-
include Orderable
|
13
|
-
|
14
|
-
# GET /:resource
|
15
|
-
# Returns a paginated, ordered and filtered query based response.
|
16
|
-
# Consider this
|
17
|
-
# To get all `Channel` sorted by ascending `name` and descending
|
18
|
-
# `updated_at`, filtered by the ones that have a `domain` that matches
|
19
|
-
# exactly `"domain.com"`, paginating records 42 per page and retrieving
|
20
|
-
# the page 42 of that collection. Usage:
|
21
|
-
# GET /sites?sort=+name,-updated_at&q[domain_eq]=domain.com&page=42&per_page=42
|
22
|
-
def index
|
23
|
-
render json: response_json
|
24
|
-
end
|
25
|
-
|
26
|
-
# GET /:resource/1
|
27
|
-
def show
|
28
|
-
render json: @object.to_json(include: parsed_include)
|
29
|
-
end
|
30
|
-
|
31
|
-
# PATCH/PUT /:resource/1
|
32
|
-
def update
|
33
|
-
authorize_for(action: :update,
|
34
|
-
resource: resource.name.underscore.to_sym,
|
35
|
-
object: @object)
|
36
|
-
if @object.update(object_params)
|
37
|
-
render json: @object
|
38
|
-
else
|
39
|
-
render json: @object.errors, status: :unprocessable_entity
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# DELETE /:resource/1
|
44
|
-
def destroy
|
45
|
-
authorize_for(action: :destroy,
|
46
|
-
resource: resource.name.underscore.to_sym,
|
47
|
-
object: @object)
|
48
|
-
if @object.destroy
|
49
|
-
head :no_content
|
50
|
-
else
|
51
|
-
render json: @object.errors, status: :unprocessable_entity
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# GET /:resource/1/:nested_resource
|
56
|
-
alias nested_index index
|
57
|
-
|
58
|
-
# POST /:resource
|
59
|
-
def create
|
60
|
-
@object = resource.new(resource_params)
|
61
|
-
authorize_for(action: :create,
|
62
|
-
resource: resource.name.underscore.to_sym,
|
63
|
-
object: @object)
|
64
|
-
if @object.save
|
65
|
-
render json: @object, status: :created, location: @object
|
66
|
-
else
|
67
|
-
render json: @object.errors, status: :unprocessable_entity
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# OPTIONS /:resource
|
72
|
-
# OPTIONS /:resource/1/:nested_resource
|
73
|
-
# Will return a JSON with the schema of the current resource, using
|
74
|
-
# attribute names as keys and attirbute types as values.
|
75
|
-
def schema
|
76
|
-
render json: resource_schema.to_json
|
77
|
-
end
|
78
|
-
|
79
|
-
private
|
80
|
-
|
81
|
-
# Common setup to stablish which model is the resource of this request
|
82
|
-
def set_root_resource
|
83
|
-
@root_resource = params[:resource].classify.constantize
|
84
|
-
end
|
85
|
-
|
86
|
-
# Common setup to stablish which object this request is querying
|
87
|
-
def set_object
|
88
|
-
id = params[:id]
|
89
|
-
@object = resource.friendly.find(id)
|
90
|
-
rescue NoMethodError
|
91
|
-
@object = resource.find(id)
|
92
|
-
ensure
|
93
|
-
authorize! :read, @object
|
94
|
-
end
|
95
|
-
|
96
|
-
# Setup to stablish the nested model to be queried
|
97
|
-
def set_nested_resource
|
98
|
-
@nested_resource = @object.send(params[:nested].underscore.pluralize)
|
99
|
-
end
|
100
|
-
|
101
|
-
# Reutrns root_resource if nested_resource is not set scoped by permissions
|
102
|
-
def resource
|
103
|
-
(@nested_resource || @root_resource)
|
104
|
-
end
|
105
|
-
|
106
|
-
# Used to setup the resource's schema, mapping attributes and it's types
|
107
|
-
def resource_schema
|
108
|
-
schemated = {}
|
109
|
-
resource.columns_hash.each { |key, value| schemated[key] = value.type }
|
110
|
-
schemated
|
111
|
-
end
|
112
|
-
|
113
|
-
# Used to setup the records from the selected resource that are
|
114
|
-
# going to be rendered, if authorized
|
115
|
-
def set_records
|
116
|
-
authorize! :read, resource.name.underscore.to_sym
|
117
|
-
@records = resource.ransack(parsed_query).result
|
118
|
-
reorder_records if params[:sort].present?
|
119
|
-
end
|
120
|
-
|
121
|
-
# Reordering of records which happens when receiving `params[:sort]`
|
122
|
-
def reorder_records
|
123
|
-
@records = @records.unscope(:order).order(ordering_params(params))
|
124
|
-
end
|
125
|
-
|
126
|
-
# Raw paginated records object
|
127
|
-
def paginated_records
|
128
|
-
@records.accessible_by(current_ability)
|
129
|
-
.paginate(page: params[:page], per_page: params[:per_page])
|
130
|
-
end
|
131
|
-
|
132
|
-
# Parsing of `paginated_records` with pagination variables metadata
|
133
|
-
def response_json
|
134
|
-
{ entries: entries_json }.merge(pagination_metadata_for(paginated_records))
|
135
|
-
end
|
136
|
-
|
137
|
-
# Parsed JSON to be used as response payload
|
138
|
-
def entries_json
|
139
|
-
JSON.parse(paginated_records.to_json(include: parsed_include))
|
140
|
-
end
|
141
|
-
|
142
|
-
# Only allow a trusted parameter "white list" through,
|
143
|
-
# based on resource's schema.
|
144
|
-
def object_params
|
145
|
-
params.fetch(resource.name.underscore.to_sym, resource_schema.keys)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Apicasso
|
4
|
+
# Controller to consume read-only data to be used on client's frontend
|
5
|
+
class CrudController < Apicasso::ApplicationController
|
6
|
+
before_action :set_root_resource
|
7
|
+
before_action :set_object, except: %i[index schema create]
|
8
|
+
before_action :set_nested_resource, only: %i[nested_index]
|
9
|
+
before_action :set_records, only: %i[index nested_index]
|
10
|
+
before_action :set_schema, only: %i[schema]
|
11
|
+
|
12
|
+
include Orderable
|
13
|
+
|
14
|
+
# GET /:resource
|
15
|
+
# Returns a paginated, ordered and filtered query based response.
|
16
|
+
# Consider this
|
17
|
+
# To get all `Channel` sorted by ascending `name` and descending
|
18
|
+
# `updated_at`, filtered by the ones that have a `domain` that matches
|
19
|
+
# exactly `"domain.com"`, paginating records 42 per page and retrieving
|
20
|
+
# the page 42 of that collection. Usage:
|
21
|
+
# GET /sites?sort=+name,-updated_at&q[domain_eq]=domain.com&page=42&per_page=42
|
22
|
+
def index
|
23
|
+
render json: response_json
|
24
|
+
end
|
25
|
+
|
26
|
+
# GET /:resource/1
|
27
|
+
def show
|
28
|
+
render json: @object.to_json(include: parsed_include)
|
29
|
+
end
|
30
|
+
|
31
|
+
# PATCH/PUT /:resource/1
|
32
|
+
def update
|
33
|
+
authorize_for(action: :update,
|
34
|
+
resource: resource.name.underscore.to_sym,
|
35
|
+
object: @object)
|
36
|
+
if @object.update(object_params)
|
37
|
+
render json: @object
|
38
|
+
else
|
39
|
+
render json: @object.errors, status: :unprocessable_entity
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# DELETE /:resource/1
|
44
|
+
def destroy
|
45
|
+
authorize_for(action: :destroy,
|
46
|
+
resource: resource.name.underscore.to_sym,
|
47
|
+
object: @object)
|
48
|
+
if @object.destroy
|
49
|
+
head :no_content
|
50
|
+
else
|
51
|
+
render json: @object.errors, status: :unprocessable_entity
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# GET /:resource/1/:nested_resource
|
56
|
+
alias nested_index index
|
57
|
+
|
58
|
+
# POST /:resource
|
59
|
+
def create
|
60
|
+
@object = resource.new(resource_params)
|
61
|
+
authorize_for(action: :create,
|
62
|
+
resource: resource.name.underscore.to_sym,
|
63
|
+
object: @object)
|
64
|
+
if @object.save
|
65
|
+
render json: @object, status: :created, location: @object
|
66
|
+
else
|
67
|
+
render json: @object.errors, status: :unprocessable_entity
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# OPTIONS /:resource
|
72
|
+
# OPTIONS /:resource/1/:nested_resource
|
73
|
+
# Will return a JSON with the schema of the current resource, using
|
74
|
+
# attribute names as keys and attirbute types as values.
|
75
|
+
def schema
|
76
|
+
render json: resource_schema.to_json
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# Common setup to stablish which model is the resource of this request
|
82
|
+
def set_root_resource
|
83
|
+
@root_resource = params[:resource].classify.constantize
|
84
|
+
end
|
85
|
+
|
86
|
+
# Common setup to stablish which object this request is querying
|
87
|
+
def set_object
|
88
|
+
id = params[:id]
|
89
|
+
@object = resource.friendly.find(id)
|
90
|
+
rescue NoMethodError
|
91
|
+
@object = resource.find(id)
|
92
|
+
ensure
|
93
|
+
authorize! :read, @object
|
94
|
+
end
|
95
|
+
|
96
|
+
# Setup to stablish the nested model to be queried
|
97
|
+
def set_nested_resource
|
98
|
+
@nested_resource = @object.send(params[:nested].underscore.pluralize)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Reutrns root_resource if nested_resource is not set scoped by permissions
|
102
|
+
def resource
|
103
|
+
(@nested_resource || @root_resource)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Used to setup the resource's schema, mapping attributes and it's types
|
107
|
+
def resource_schema
|
108
|
+
schemated = {}
|
109
|
+
resource.columns_hash.each { |key, value| schemated[key] = value.type }
|
110
|
+
schemated
|
111
|
+
end
|
112
|
+
|
113
|
+
# Used to setup the records from the selected resource that are
|
114
|
+
# going to be rendered, if authorized
|
115
|
+
def set_records
|
116
|
+
authorize! :read, resource.name.underscore.to_sym
|
117
|
+
@records = resource.ransack(parsed_query).result
|
118
|
+
reorder_records if params[:sort].present?
|
119
|
+
end
|
120
|
+
|
121
|
+
# Reordering of records which happens when receiving `params[:sort]`
|
122
|
+
def reorder_records
|
123
|
+
@records = @records.unscope(:order).order(ordering_params(params))
|
124
|
+
end
|
125
|
+
|
126
|
+
# Raw paginated records object
|
127
|
+
def paginated_records
|
128
|
+
@records.accessible_by(current_ability)
|
129
|
+
.paginate(page: params[:page], per_page: params[:per_page])
|
130
|
+
end
|
131
|
+
|
132
|
+
# Parsing of `paginated_records` with pagination variables metadata
|
133
|
+
def response_json
|
134
|
+
{ entries: entries_json }.merge(pagination_metadata_for(paginated_records))
|
135
|
+
end
|
136
|
+
|
137
|
+
# Parsed JSON to be used as response payload
|
138
|
+
def entries_json
|
139
|
+
JSON.parse(paginated_records.to_json(include: parsed_include))
|
140
|
+
end
|
141
|
+
|
142
|
+
# Only allow a trusted parameter "white list" through,
|
143
|
+
# based on resource's schema.
|
144
|
+
def object_params
|
145
|
+
params.fetch(resource.name.underscore.to_sym, resource_schema.keys)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -1,44 +1,44 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# This concern is used to provide abstract ordering based on `params[:sort]`
|
4
|
-
module Orderable
|
5
|
-
extend ActiveSupport::Concern
|
6
|
-
SORT_ORDER = { '+' => :asc, '-' => :desc }.freeze
|
7
|
-
|
8
|
-
# A list of the param names that can be used for ordering the model list
|
9
|
-
def ordering_params(params)
|
10
|
-
# For example it retrieves a list of orders in descending order of total_value.
|
11
|
-
# Within a specific total_value, older orders are ordered first
|
12
|
-
#
|
13
|
-
# GET /orders?sort=-total_value,created_at
|
14
|
-
# ordering_params(params) # => { total_value: :desc, created_at: :asc }
|
15
|
-
#
|
16
|
-
# Usage:
|
17
|
-
# Order.order(ordering_params(params))
|
18
|
-
ordering = {}
|
19
|
-
params[:sort].try(:split, ',').try(:each) do |attr|
|
20
|
-
parsed_attr = parse_attr attr
|
21
|
-
if model.attribute_names.include?(parsed_attr)
|
22
|
-
ordering[parsed_attr] = SORT_ORDER[parse_sign
|
23
|
-
end
|
24
|
-
end
|
25
|
-
ordering
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
# Parsing of attributes to avoid empty starts in case browser passes "+" as " "
|
31
|
-
def parse_attr(attr)
|
32
|
-
return attr.gsub(/^\ (.*)/, '\1') if attr.starts_with?(' ')
|
33
|
-
attr[1..-1]
|
34
|
-
end
|
35
|
-
|
36
|
-
# Ordering sign parse, which separates
|
37
|
-
def parse_sign(attr)
|
38
|
-
attr =~ /\A[+-]/ ? attr.slice!(0) : '+'
|
39
|
-
end
|
40
|
-
|
41
|
-
def model
|
42
|
-
(params[:resource] || params[:nested] || controller_name).classify.constantize
|
43
|
-
end
|
44
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This concern is used to provide abstract ordering based on `params[:sort]`
|
4
|
+
module Orderable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
SORT_ORDER = { '+' => :asc, '-' => :desc }.freeze
|
7
|
+
|
8
|
+
# A list of the param names that can be used for ordering the model list
|
9
|
+
def ordering_params(params)
|
10
|
+
# For example it retrieves a list of orders in descending order of total_value.
|
11
|
+
# Within a specific total_value, older orders are ordered first
|
12
|
+
#
|
13
|
+
# GET /orders?sort=-total_value,created_at
|
14
|
+
# ordering_params(params) # => { total_value: :desc, created_at: :asc }
|
15
|
+
#
|
16
|
+
# Usage:
|
17
|
+
# Order.order(ordering_params(params))
|
18
|
+
ordering = {}
|
19
|
+
params[:sort].try(:split, ',').try(:each) do |attr|
|
20
|
+
parsed_attr = parse_attr attr
|
21
|
+
if model.attribute_names.include?(parsed_attr)
|
22
|
+
ordering[parsed_attr] = SORT_ORDER[parse_sign attr]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
ordering
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Parsing of attributes to avoid empty starts in case browser passes "+" as " "
|
31
|
+
def parse_attr(attr)
|
32
|
+
return attr.gsub(/^\ (.*)/, '\1') if attr.starts_with?(' ')
|
33
|
+
attr[1..-1]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Ordering sign parse, which separates
|
37
|
+
def parse_sign(attr)
|
38
|
+
attr =~ /\A[+-]/ ? attr.slice!(0) : '+'
|
39
|
+
end
|
40
|
+
|
41
|
+
def model
|
42
|
+
(params[:resource] || params[:nested] || controller_name).classify.constantize
|
43
|
+
end
|
44
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Apicasso
|
2
|
-
module ApplicationHelper
|
3
|
-
end
|
4
|
-
end
|
1
|
+
module Apicasso
|
2
|
+
module ApplicationHelper
|
3
|
+
end
|
4
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Apicasso
|
2
|
-
class ApplicationJob < ActiveJob::Base
|
3
|
-
end
|
4
|
-
end
|
1
|
+
module Apicasso
|
2
|
+
class ApplicationJob < ActiveJob::Base
|
3
|
+
end
|
4
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
module Apicasso
|
2
|
-
class ApplicationMailer < ActionMailer::Base
|
3
|
-
default from: 'from@example.com'
|
4
|
-
layout 'mailer'
|
5
|
-
end
|
6
|
-
end
|
1
|
+
module Apicasso
|
2
|
+
class ApplicationMailer < ActionMailer::Base
|
3
|
+
default from: 'from@example.com'
|
4
|
+
layout 'mailer'
|
5
|
+
end
|
6
|
+
end
|
@@ -1,40 +1,40 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Apicasso
|
4
|
-
# Ability to parse a scope object from Apicasso::Key
|
5
|
-
class Ability
|
6
|
-
include CanCan::Ability
|
7
|
-
|
8
|
-
def initialize(key)
|
9
|
-
key ||= Apicasso::Key.new
|
10
|
-
cannot :manage, :all
|
11
|
-
cannot :read, :all
|
12
|
-
key.scope.each do |permission, klasses_clearances|
|
13
|
-
klasses_clearances.each do |klasses|
|
14
|
-
klasses.each do |klass, clearance|
|
15
|
-
if clearance == true
|
16
|
-
# Usage:
|
17
|
-
# To have a key reading all channels and all accouts
|
18
|
-
# you would have a scope:
|
19
|
-
# => `{read: [{channel: true}, {accout: true}]}`
|
20
|
-
can permission.to_sym, klass.underscore.to_sym
|
21
|
-
can permission.to_sym, klass.classify.constantize
|
22
|
-
elsif clearance.class == Hash
|
23
|
-
# Usage:
|
24
|
-
# To have a key reading all banners from a channel with id 999
|
25
|
-
# you would have a scope:
|
26
|
-
# => `{read: [{banner: {owner_id: [999]}}]}`
|
27
|
-
can permission.to_sym,
|
28
|
-
klass.underscore.to_sym
|
29
|
-
clearance.each do |by_field, values|
|
30
|
-
can permission.to_sym,
|
31
|
-
klass.classify.constantize,
|
32
|
-
by_field => values
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Apicasso
|
4
|
+
# Ability to parse a scope object from Apicasso::Key
|
5
|
+
class Ability
|
6
|
+
include CanCan::Ability
|
7
|
+
|
8
|
+
def initialize(key)
|
9
|
+
key ||= Apicasso::Key.new
|
10
|
+
cannot :manage, :all
|
11
|
+
cannot :read, :all
|
12
|
+
key.scope.each do |permission, klasses_clearances|
|
13
|
+
klasses_clearances.each do |klasses|
|
14
|
+
klasses.each do |klass, clearance|
|
15
|
+
if clearance == true
|
16
|
+
# Usage:
|
17
|
+
# To have a key reading all channels and all accouts
|
18
|
+
# you would have a scope:
|
19
|
+
# => `{read: [{channel: true}, {accout: true}]}`
|
20
|
+
can permission.to_sym, klass.underscore.to_sym
|
21
|
+
can permission.to_sym, klass.classify.constantize
|
22
|
+
elsif clearance.class == Hash
|
23
|
+
# Usage:
|
24
|
+
# To have a key reading all banners from a channel with id 999
|
25
|
+
# you would have a scope:
|
26
|
+
# => `{read: [{banner: {owner_id: [999]}}]}`
|
27
|
+
can permission.to_sym,
|
28
|
+
klass.underscore.to_sym
|
29
|
+
clearance.each do |by_field, values|
|
30
|
+
can permission.to_sym,
|
31
|
+
klass.classify.constantize,
|
32
|
+
by_field => values
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
module Apicasso
|
2
|
-
class ApplicationRecord < ActiveRecord::Base
|
3
|
-
self.abstract_class = true
|
4
|
-
self.table_name_prefix = 'apicasso_'
|
5
|
-
end
|
6
|
-
end
|
1
|
+
module Apicasso
|
2
|
+
class ApplicationRecord < ActiveRecord::Base
|
3
|
+
self.abstract_class = true
|
4
|
+
self.table_name_prefix = 'apicasso_'
|
5
|
+
end
|
6
|
+
end
|
data/app/models/apicasso/key.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'securerandom'
|
4
|
-
module Apicasso
|
5
|
-
# A model to abstract API access, with scope options, token generation, request limiting
|
6
|
-
class Key < ApplicationRecord
|
7
|
-
before_create :set_auth_token
|
8
|
-
|
9
|
-
private
|
10
|
-
|
11
|
-
# Method that generates `SecureRandom.uuid` as token until
|
12
|
-
# an unique one has been acquired
|
13
|
-
def set_auth_token
|
14
|
-
loop do
|
15
|
-
self.token = generate_auth_token
|
16
|
-
break unless self.class.exists?(token: token)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# RFC4122 style token
|
21
|
-
def generate_auth_token
|
22
|
-
SecureRandom.uuid.delete('-')
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
module Apicasso
|
5
|
+
# A model to abstract API access, with scope options, token generation, request limiting
|
6
|
+
class Key < ApplicationRecord
|
7
|
+
before_create :set_auth_token
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# Method that generates `SecureRandom.uuid` as token until
|
12
|
+
# an unique one has been acquired
|
13
|
+
def set_auth_token
|
14
|
+
loop do
|
15
|
+
self.token = generate_auth_token
|
16
|
+
break unless self.class.exists?(token: token)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# RFC4122 style token
|
21
|
+
def generate_auth_token
|
22
|
+
SecureRandom.uuid.delete('-')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Apicasso
|
4
|
-
# A model to abstract API access, with scope options, token generation, request limiting
|
5
|
-
class Request < ApplicationRecord
|
6
|
-
belongs_to :api_key, class_name: 'Apicasso::Key'
|
7
|
-
end
|
8
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Apicasso
|
4
|
+
# A model to abstract API access, with scope options, token generation, request limiting
|
5
|
+
class Request < ApplicationRecord
|
6
|
+
belongs_to :api_key, class_name: 'Apicasso::Key'
|
7
|
+
end
|
8
|
+
end
|
@@ -1,16 +1,16 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<title>Apicasso</title>
|
5
|
-
<%= csrf_meta_tags %>
|
6
|
-
<%= csp_meta_tag %>
|
7
|
-
|
8
|
-
<%= stylesheet_link_tag "apicasso/application", media: "all" %>
|
9
|
-
<%= javascript_include_tag "apicasso/application" %>
|
10
|
-
</head>
|
11
|
-
<body>
|
12
|
-
|
13
|
-
<%= yield %>
|
14
|
-
|
15
|
-
</body>
|
16
|
-
</html>
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Apicasso</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<%= stylesheet_link_tag "apicasso/application", media: "all" %>
|
9
|
+
<%= javascript_include_tag "apicasso/application" %>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
|
13
|
+
<%= yield %>
|
14
|
+
|
15
|
+
</body>
|
16
|
+
</html>
|
data/config/routes.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
Apicasso::Engine.routes.draw do
|
2
|
-
scope module: :apicasso do
|
3
|
-
get '/:resource/', to: 'crud#index', via: :get
|
4
|
-
match '/:resource/', to: 'crud#create', via: :post
|
5
|
-
get '/:resource/:id', to: 'crud#show', via: :get
|
6
|
-
match '/:resource/:id', to: 'crud#update', via: :patch
|
7
|
-
match '/:resource/:id', to: 'crud#destroy', via: :delete
|
8
|
-
match '/:resource/:id/:nested/', to: 'crud#nested_index', via: :get
|
9
|
-
match '/:resource/', to: 'crud#schema', via: :options
|
10
|
-
match '/:resource/:id/:nested/', to: 'crud#schema', via: :options
|
11
|
-
resources :apidocs, only: [:index]
|
12
|
-
end
|
13
|
-
end
|
1
|
+
Apicasso::Engine.routes.draw do
|
2
|
+
scope module: :apicasso do
|
3
|
+
get '/:resource/', to: 'crud#index', via: :get
|
4
|
+
match '/:resource/', to: 'crud#create', via: :post
|
5
|
+
get '/:resource/:id', to: 'crud#show', via: :get
|
6
|
+
match '/:resource/:id', to: 'crud#update', via: :patch
|
7
|
+
match '/:resource/:id', to: 'crud#destroy', via: :delete
|
8
|
+
match '/:resource/:id/:nested/', to: 'crud#nested_index', via: :get
|
9
|
+
match '/:resource/', to: 'crud#schema', via: :options
|
10
|
+
match '/:resource/:id/:nested/', to: 'crud#schema', via: :options
|
11
|
+
resources :apidocs, only: [:index]
|
12
|
+
end
|
13
|
+
end
|
File without changes
|
data/lib/apicasso/engine.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Apicasso
|
4
|
-
class Engine < ::Rails::Engine
|
5
|
-
end
|
6
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Apicasso
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
end
|
6
|
+
end
|
data/lib/apicasso/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module Apicasso
|
2
|
-
VERSION = '0.1.
|
3
|
-
end
|
1
|
+
module Apicasso
|
2
|
+
VERSION = '0.1.3'
|
3
|
+
end
|