croods 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +19 -0
- data/lib/croods.rb +58 -0
- data/lib/croods/action.rb +17 -0
- data/lib/croods/api.rb +26 -0
- data/lib/croods/api/initial_schema.json +52 -0
- data/lib/croods/attribute.rb +15 -0
- data/lib/croods/controller.rb +28 -0
- data/lib/croods/controller/actions.rb +71 -0
- data/lib/croods/controller/already_taken.rb +27 -0
- data/lib/croods/controller/authentication.rb +22 -0
- data/lib/croods/controller/authorization.rb +31 -0
- data/lib/croods/controller/collection.rb +15 -0
- data/lib/croods/controller/member.rb +45 -0
- data/lib/croods/controller/model.rb +17 -0
- data/lib/croods/controller/multi_tenancy.rb +54 -0
- data/lib/croods/controller/not_found.rb +22 -0
- data/lib/croods/controller/record_invalid.rb +22 -0
- data/lib/croods/controller/resource.rb +30 -0
- data/lib/croods/middleware.rb +44 -0
- data/lib/croods/middleware/base.rb +20 -0
- data/lib/croods/middleware/request_validation.rb +10 -0
- data/lib/croods/middleware/response_validation.rb +10 -0
- data/lib/croods/model.rb +41 -0
- data/lib/croods/policy.rb +118 -0
- data/lib/croods/policy/scope.rb +131 -0
- data/lib/croods/railtie.rb +6 -0
- data/lib/croods/resource.rb +39 -0
- data/lib/croods/resource/actions.rb +51 -0
- data/lib/croods/resource/attributes.rb +53 -0
- data/lib/croods/resource/attributes/base.rb +30 -0
- data/lib/croods/resource/attributes/request.rb +29 -0
- data/lib/croods/resource/attributes/response.rb +13 -0
- data/lib/croods/resource/authentication.rb +46 -0
- data/lib/croods/resource/authorization.rb +47 -0
- data/lib/croods/resource/controller.rb +50 -0
- data/lib/croods/resource/filters.rb +32 -0
- data/lib/croods/resource/identifier.rb +13 -0
- data/lib/croods/resource/json_schema.rb +32 -0
- data/lib/croods/resource/json_schema/definition.rb +34 -0
- data/lib/croods/resource/json_schema/definitions.rb +31 -0
- data/lib/croods/resource/json_schema/initial_schema.json +8 -0
- data/lib/croods/resource/json_schema/links.rb +40 -0
- data/lib/croods/resource/json_schema/links/collection.rb +70 -0
- data/lib/croods/resource/json_schema/links/create.rb +41 -0
- data/lib/croods/resource/json_schema/links/destroy.rb +40 -0
- data/lib/croods/resource/json_schema/links/index.rb +17 -0
- data/lib/croods/resource/json_schema/links/member.rb +40 -0
- data/lib/croods/resource/json_schema/links/show.rb +40 -0
- data/lib/croods/resource/json_schema/links/update.rb +40 -0
- data/lib/croods/resource/json_schema/properties.rb +32 -0
- data/lib/croods/resource/json_schema/required.rb +34 -0
- data/lib/croods/resource/model.rb +35 -0
- data/lib/croods/resource/names.rb +23 -0
- data/lib/croods/resource/paths.rb +16 -0
- data/lib/croods/resource/policy.rb +51 -0
- data/lib/croods/resource/services.rb +18 -0
- data/lib/croods/resource/sorting.rb +13 -0
- data/lib/croods/routes.rb +46 -0
- data/lib/croods/service.rb +28 -0
- data/lib/croods/version.rb +5 -0
- data/lib/tasks/croods_tasks.rake +4 -0
- metadata +316 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Croods
|
4
|
+
module Resource
|
5
|
+
module Controller
|
6
|
+
def extend_controller(&block)
|
7
|
+
return unless block
|
8
|
+
|
9
|
+
controller_blocks << block
|
10
|
+
end
|
11
|
+
|
12
|
+
def controller_blocks
|
13
|
+
@controller_blocks ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def controller_name
|
17
|
+
"#{namespace}Controller"
|
18
|
+
end
|
19
|
+
|
20
|
+
def controller
|
21
|
+
controller_name.constantize
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_controller!
|
25
|
+
Object.const_set(controller_name, Class.new(ApplicationController))
|
26
|
+
|
27
|
+
controller_blocks.each do |block|
|
28
|
+
controller.instance_eval(&block)
|
29
|
+
end
|
30
|
+
|
31
|
+
create_actions!
|
32
|
+
create_additional_actions!
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_actions!
|
36
|
+
actions.each do |action|
|
37
|
+
controller.define_method(
|
38
|
+
action.name, Croods::Controller::Actions.send(action.name)
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_additional_actions!
|
44
|
+
additional_actions.each do |action|
|
45
|
+
controller.define_method(action.name, &action.block)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Croods
|
4
|
+
module Resource
|
5
|
+
module Filters
|
6
|
+
def filter_by(name, optional: nil)
|
7
|
+
filters << Croods::Attribute.new(
|
8
|
+
name, :string, null: optional
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
def filters
|
13
|
+
@filters ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def apply_filters(scope, params)
|
17
|
+
filters.each do |attribute|
|
18
|
+
unless model.has_attribute?(attribute.name)
|
19
|
+
attribute.name = "#{attribute.name}_id"
|
20
|
+
end
|
21
|
+
|
22
|
+
value = params[attribute.name]
|
23
|
+
next unless value
|
24
|
+
|
25
|
+
scope = scope.where(attribute.name => value)
|
26
|
+
end
|
27
|
+
|
28
|
+
scope
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'json_schema/definition'
|
4
|
+
require_relative 'json_schema/definitions'
|
5
|
+
require_relative 'json_schema/properties'
|
6
|
+
require_relative 'json_schema/required'
|
7
|
+
require_relative 'json_schema/links'
|
8
|
+
|
9
|
+
module Croods
|
10
|
+
module Resource
|
11
|
+
module JsonSchema
|
12
|
+
def json_schema
|
13
|
+
return @json_schema if @json_schema
|
14
|
+
|
15
|
+
path = File.expand_path('json_schema/initial_schema.json', __dir__)
|
16
|
+
@json_schema = JSON.parse(File.read(path))
|
17
|
+
@json_schema['definitions'] = Definitions.schema(self)
|
18
|
+
@json_schema['properties'] = Properties.schema(self)
|
19
|
+
@json_schema['required'] = Required.schema(self)
|
20
|
+
@json_schema['links'] = Links.schema(self)
|
21
|
+
@json_schema
|
22
|
+
end
|
23
|
+
|
24
|
+
def ref(attribute = nil)
|
25
|
+
{
|
26
|
+
'$ref': "#/definitions/#{resource_name}" +
|
27
|
+
(attribute ? "/definitions/#{attribute}" : '')
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Croods
|
4
|
+
module Resource
|
5
|
+
module JsonSchema
|
6
|
+
module Definition
|
7
|
+
TYPES = { datetime: 'string', text: 'string', json: 'object' }.freeze
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def schema(attribute)
|
11
|
+
{ type: types(attribute) }.merge(format(attribute))
|
12
|
+
end
|
13
|
+
|
14
|
+
def format(attribute)
|
15
|
+
return {} unless attribute.type == :datetime
|
16
|
+
|
17
|
+
{ format: 'date-time' }
|
18
|
+
end
|
19
|
+
|
20
|
+
def types(attribute)
|
21
|
+
types = []
|
22
|
+
types << type(attribute.type)
|
23
|
+
types << 'null' if attribute.null
|
24
|
+
types
|
25
|
+
end
|
26
|
+
|
27
|
+
def type(type)
|
28
|
+
TYPES[type] || type.to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Croods
|
4
|
+
module Resource
|
5
|
+
module JsonSchema
|
6
|
+
module Definitions
|
7
|
+
TYPES = { datetime: 'string', text: 'string', json: 'object' }.freeze
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def schema(resource)
|
11
|
+
attributes(resource).merge(identity: identity(resource))
|
12
|
+
end
|
13
|
+
|
14
|
+
def attributes(resource)
|
15
|
+
attributes = {}
|
16
|
+
|
17
|
+
resource.definitions.each_value do |attribute|
|
18
|
+
attributes[attribute.name] = Definition.schema(attribute)
|
19
|
+
end
|
20
|
+
|
21
|
+
attributes
|
22
|
+
end
|
23
|
+
|
24
|
+
def identity(resource)
|
25
|
+
resource.ref(resource.identifier)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'links/collection'
|
4
|
+
require_relative 'links/member'
|
5
|
+
require_relative 'links/index'
|
6
|
+
require_relative 'links/create'
|
7
|
+
require_relative 'links/update'
|
8
|
+
require_relative 'links/destroy'
|
9
|
+
require_relative 'links/show'
|
10
|
+
|
11
|
+
module Croods
|
12
|
+
module Resource
|
13
|
+
module JsonSchema
|
14
|
+
module Links
|
15
|
+
class << self
|
16
|
+
def schema(resource)
|
17
|
+
additional_actions(resource) + actions(resource)
|
18
|
+
end
|
19
|
+
|
20
|
+
def actions(resource)
|
21
|
+
resource.actions.map do |action|
|
22
|
+
action_module(action.name).link(resource)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def additional_actions(resource)
|
27
|
+
resource.additional_actions.map do |action|
|
28
|
+
action_module(action.on).link(resource, action)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def action_module(name)
|
33
|
+
"Croods::Resource::JsonSchema::Links::#{name.to_s.titleize}"
|
34
|
+
.constantize
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Croods
|
4
|
+
module Resource
|
5
|
+
module JsonSchema
|
6
|
+
module Links
|
7
|
+
module Collection
|
8
|
+
class << self
|
9
|
+
def link(resource, action = nil)
|
10
|
+
href = resource.collection_path
|
11
|
+
href += "/#{action.name}" if action
|
12
|
+
|
13
|
+
{
|
14
|
+
href: href,
|
15
|
+
method: action&.method&.upcase || 'GET',
|
16
|
+
rel: 'instances',
|
17
|
+
schema: schema(resource),
|
18
|
+
targetSchema: target_schema(resource)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def filters(resource)
|
23
|
+
filters = {}
|
24
|
+
|
25
|
+
resource.filters.each do |attribute|
|
26
|
+
unless resource.model.has_attribute?(attribute.name)
|
27
|
+
attribute.name = "#{attribute.name}_id"
|
28
|
+
end
|
29
|
+
|
30
|
+
filters[attribute.name] = definition(attribute)
|
31
|
+
end
|
32
|
+
|
33
|
+
filters
|
34
|
+
end
|
35
|
+
|
36
|
+
def definition(attribute)
|
37
|
+
definition = Definition.schema(attribute)
|
38
|
+
|
39
|
+
{
|
40
|
+
anyOf: [{ type: ['array'], items: definition }, definition]
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def required(resource)
|
45
|
+
resource.filters.reject(&:null).map(&:name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def schema(resource)
|
49
|
+
{
|
50
|
+
additionalProperties: false,
|
51
|
+
properties: filters(resource),
|
52
|
+
required: required(resource),
|
53
|
+
type: ['object']
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def target_schema(resource)
|
58
|
+
{
|
59
|
+
anyOf: [
|
60
|
+
{ type: ['array'], items: resource.ref },
|
61
|
+
{ '$ref': '#/definitions/error' }
|
62
|
+
]
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Croods
|
4
|
+
module Resource
|
5
|
+
module JsonSchema
|
6
|
+
module Links
|
7
|
+
module Create
|
8
|
+
class << self
|
9
|
+
def link(resource)
|
10
|
+
{
|
11
|
+
href: resource.collection_path,
|
12
|
+
method: 'POST',
|
13
|
+
rel: 'new',
|
14
|
+
schema: schema(resource),
|
15
|
+
targetSchema: target_schema(resource)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def schema(resource)
|
20
|
+
{
|
21
|
+
additionalProperties: false,
|
22
|
+
properties: Properties.schema(resource, request: true),
|
23
|
+
required: Required.schema(resource, request: true),
|
24
|
+
type: ['object']
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def target_schema(resource)
|
29
|
+
{
|
30
|
+
anyOf: [
|
31
|
+
resource.ref,
|
32
|
+
{ '$ref': '#/definitions/error' }
|
33
|
+
]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Croods
|
4
|
+
module Resource
|
5
|
+
module JsonSchema
|
6
|
+
module Links
|
7
|
+
module Destroy
|
8
|
+
class << self
|
9
|
+
def link(resource)
|
10
|
+
{
|
11
|
+
href: resource.member_path,
|
12
|
+
method: 'DELETE',
|
13
|
+
rel: 'self',
|
14
|
+
schema: schema,
|
15
|
+
targetSchema: target_schema(resource)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def schema
|
20
|
+
{
|
21
|
+
strictProperties: true,
|
22
|
+
properties: {},
|
23
|
+
type: ['object']
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def target_schema(resource)
|
28
|
+
{
|
29
|
+
anyOf: [
|
30
|
+
resource.ref,
|
31
|
+
{ '$ref': '#/definitions/error' }
|
32
|
+
]
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Croods
|
4
|
+
module Resource
|
5
|
+
module JsonSchema
|
6
|
+
module Links
|
7
|
+
module Member
|
8
|
+
class << self
|
9
|
+
def link(resource, action)
|
10
|
+
{
|
11
|
+
href: "#{resource.member_path}/#{action.name}",
|
12
|
+
method: action.method.upcase,
|
13
|
+
rel: 'self',
|
14
|
+
schema: schema,
|
15
|
+
targetSchema: target_schema(resource)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def schema
|
20
|
+
{
|
21
|
+
strictProperties: true,
|
22
|
+
properties: {},
|
23
|
+
type: ['object']
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def target_schema(resource)
|
28
|
+
{
|
29
|
+
anyOf: [
|
30
|
+
resource.ref,
|
31
|
+
{ '$ref': '#/definitions/error' }
|
32
|
+
]
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|