restme 0.0.37

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.
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../shared/user_role"
4
+ require_relative "../shared/current_model"
5
+ require_relative "../shared/controller_params"
6
+
7
+ module Restme
8
+ module Create
9
+ # Defines create restrictions rules for a record.
10
+ module Rules
11
+ include ::Restme::Shared::ControllerParams
12
+ include ::Restme::Shared::CurrentModel
13
+ include ::Restme::Shared::UserRole
14
+
15
+ attr_reader :create_temp_record
16
+
17
+ private
18
+
19
+ def creatable_record
20
+ @creatable_record ||= begin
21
+ @create_temp_record = klass.new(controller_params)
22
+
23
+ set_create_temp_record_current_user
24
+
25
+ create_record_errors.presence || create_temp_record
26
+ end
27
+ end
28
+
29
+ def set_create_temp_record_current_user
30
+ return unless create_temp_record.respond_to?(:current_user)
31
+ return unless restme_current_user
32
+
33
+ create_temp_record.current_user = restme_current_user
34
+ end
35
+
36
+ def restme_create_status
37
+ return :unprocessable_entity if create_record_errors
38
+
39
+ :created
40
+ end
41
+
42
+ def create_record_errors
43
+ return unless creatable_current_action
44
+
45
+ return createable_unscoped_error_response unless createable_scope?
46
+
47
+ create_object! unless create_temp_record.persisted?
48
+
49
+ createable_object_errors_messages
50
+ end
51
+
52
+ def create_object!
53
+ ActiveRecord::Base.transaction do
54
+ create_temp_record.save!
55
+ end
56
+ rescue StandardError
57
+ nil
58
+ end
59
+
60
+ def createable_unscoped_error_response
61
+ { message: "Unscoped", body: controller_params }
62
+ end
63
+
64
+ def createable_scope?
65
+ return true unless restme_current_user
66
+
67
+ method_scope = "#{creatable_current_action}_#{user_role}_scope?"
68
+
69
+ createable_super_admin_scope? || create_rules_class.try(method_scope) || false
70
+ end
71
+
72
+ def createable_super_admin_scope?
73
+ restme_current_user.super_admin?
74
+ end
75
+
76
+ def createable_object_errors_messages
77
+ return if create_temp_record.errors.blank?
78
+
79
+ "#{current_action}_error_scope?".constantize
80
+ rescue StandardError
81
+ createable_default_error_response
82
+ end
83
+
84
+ def creatable_current_action
85
+ return true unless restme_current_user
86
+
87
+ current_action.presence_in create_rules_class.class::CREATABLE_ACTIONS_RULES
88
+ rescue StandardError
89
+ nil
90
+ end
91
+
92
+ def current_action
93
+ action_name.to_sym
94
+ end
95
+
96
+ def createable_default_error_response
97
+ {
98
+ message: "Error #{create_temp_record.errors.full_messages.to_sentence}",
99
+ body: controller_params
100
+ }
101
+ end
102
+
103
+ def create_rules_class
104
+ @create_rules_class ||=
105
+ "#{controller_class.to_s.split("::").last}::Create::Rules"
106
+ .constantize.new(create_temp_record, restme_current_user)
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "authorize/rules"
4
+ require_relative "scope/rules"
5
+ require_relative "create/rules"
6
+ require_relative "update/rules"
7
+
8
+ module Restme
9
+ # Defines the initialization rules for Restme.
10
+ module Restme
11
+ include ::Restme::Update::Rules
12
+ include ::Restme::Create::Rules
13
+ include ::Restme::Scope::Rules
14
+ include ::Restme::Authorize::Rules
15
+
16
+ attr_reader :restme_current_user
17
+
18
+ def initialize_restme
19
+ use_current_user
20
+
21
+ user_authorize
22
+ end
23
+
24
+ private
25
+
26
+ def use_current_user
27
+ @restme_current_user = try(:current_user)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Restme
4
+ module Scope
5
+ module Field
6
+ # Defines the rules that determine which attachable fields can be attached.
7
+ module Attachable
8
+ def insert_attachments(scope)
9
+ unallowed_attachment_fields_error
10
+
11
+ return scope.uniq if attachment_fields_select.blank?
12
+
13
+ scope = scope.includes(attachment_fields_select_includes).uniq
14
+
15
+ scope.map do |record|
16
+ attachment_fields_select.each do |field|
17
+ @record = record.as_json.merge({ "#{field}": record.send(field).url })
18
+ end
19
+
20
+ @record
21
+ end
22
+ end
23
+
24
+ def attachment_fields_select_includes
25
+ attachment_fields_select.map { |field| { "#{field}_attachment": :blob } }
26
+ end
27
+
28
+ def model_attachment_fields
29
+ @model_attachment_fields ||= klass.attachment_reflections.map do |attachment|
30
+ attachment.last.name
31
+ end
32
+ end
33
+
34
+ def unallowed_attachment_fields_error
35
+ return if unallowed_attachment_fields.blank?
36
+
37
+ render json: {
38
+ body: unallowed_attachment_fields,
39
+ message: "Selected not allowed attachment fields"
40
+ }, status: :bad_request
41
+ end
42
+
43
+ def unallowed_attachment_fields
44
+ return if attachment_fields_select.blank?
45
+
46
+ attachment_fields_select - model_attachment_fields
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "attachable"
4
+
5
+ module Restme
6
+ module Scope
7
+ module Field
8
+ # Defines the rules that determine which fields can be attached.
9
+ module Rules
10
+ include Restme::Scope::Field::Attachable
11
+
12
+ def fieldable_scope(user_scope)
13
+ unallowed_select_fields_error
14
+
15
+ return user_scope unless select_any_field?
16
+
17
+ scoped = user_scope
18
+
19
+ scoped = user_scope.select(model_fields_select) if model_fields_select
20
+
21
+ scoped = select_nested_scope(scoped) if valid_nested_fields_select
22
+
23
+ insert_attachments(scoped)
24
+ end
25
+
26
+ def select_nested_scope(scoped)
27
+ scoped.joins(nested_fields_joins)
28
+ .left_joins(nested_fields_left_joins)
29
+ .preload(valid_nested_fields_select)
30
+ .select(nesteds_table)
31
+ end
32
+
33
+ def select_any_field?
34
+ fields_select || nested_fields_select || attachment_fields_select
35
+ end
36
+
37
+ def nesteds_table
38
+ valid_nested_fields_select&.map do |field|
39
+ table = nested_selectable_fields_keys.dig(field, :table_name)
40
+ table.present? ? "#{table}::text AS #{field}" : nil
41
+ end
42
+ end
43
+
44
+ def model_fields_select
45
+ @model_fields_select ||= begin
46
+ fields = fields_select&.split(",")
47
+ fields = fields&.map { |field| "#{klass.table_name}.#{field}" }&.join(",")
48
+ fields || model_attributes
49
+ end
50
+ end
51
+
52
+ def model_attributes
53
+ @model_attributes ||= klass.new.attributes.keys
54
+ end
55
+
56
+ def nested_fields_joins
57
+ @nested_fields_joins ||= valid_nested_fields_select.select do |field|
58
+ nested_selectable_fields_keys[field.to_sym][:join_type].blank?
59
+ end
60
+ end
61
+
62
+ def nested_fields_left_joins
63
+ @nested_fields_left_joins ||= valid_nested_fields_select.select do |field|
64
+ nested_selectable_fields_keys[field.to_sym][:join_type] == :left_joins
65
+ end
66
+ end
67
+
68
+ def valid_nested_fields_select
69
+ @valid_nested_fields_select ||=
70
+ nested_fields_select&.split(",")&.select do |field|
71
+ nested_selectable_fields_keys[field.to_sym].present?
72
+ end&.map(&:to_sym)
73
+ end
74
+
75
+ def unallowed_select_fields_error
76
+ return if unallowed_fields_selected.blank?
77
+
78
+ restme_scope_errors({ body: unallowed_fields_selected, message: "Selected not allowed fields" })
79
+
80
+ restme_scope_status(:bad_request)
81
+ end
82
+
83
+ def unallowed_fields_selected
84
+ unallowed_nested_fields_select + unallowed_fields_select
85
+ end
86
+
87
+ def unallowed_nested_fields_select
88
+ return [] if nested_fields_select.blank?
89
+
90
+ nested_fields_select.split(",").map(&:to_sym) - valid_nested_fields_select
91
+ end
92
+
93
+ def unallowed_fields_select
94
+ return [] if fields_select.blank?
95
+
96
+ fields_select.split(",").map(&:to_sym) - model_attributes.map(&:to_sym)
97
+ end
98
+
99
+ def fields_select
100
+ @fields_select ||= controller_query_params[:fields_select]
101
+ end
102
+
103
+ def nested_fields_select
104
+ @nested_fields_select ||= controller_query_params[:nested_fields_select]
105
+ end
106
+
107
+ def attachment_fields_select
108
+ @attachment_fields_select ||= controller_query_params[:attachment_fields_select]
109
+ &.split(",")&.map(&:to_sym)
110
+ end
111
+
112
+ def nested_selectable_fields_keys
113
+ @nested_selectable_fields_keys ||= field_class_rules::NESTED_SELECTABLE_FIELDS
114
+ rescue StandardError
115
+ {}
116
+ end
117
+
118
+ def field_class_rules
119
+ "#{controller_class.to_s.split("::").last}::Field::Rules".constantize
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "types/equal_filterable"
4
+ require_relative "types/like_filterable"
5
+ require_relative "types/bigger_than_filterable"
6
+ require_relative "types/less_than_filterable"
7
+ require_relative "types/bigger_than_or_equal_to_filterable"
8
+ require_relative "types/less_than_or_equal_to_filterable"
9
+ require_relative "types/in_filterable"
10
+
11
+ module Restme
12
+ module Scope
13
+ module Filter
14
+ # Defines filter rules
15
+ module Rules
16
+ include ::Scope::Filter::Types::InFilterable
17
+ include ::Scope::Filter::Types::LessThanOrEqualToFilterable
18
+ include ::Scope::Filter::Types::BiggerThanOrEqualToFilterable
19
+ include ::Scope::Filter::Types::LessThanFilterable
20
+ include ::Scope::Filter::Types::BiggerThanFilterable
21
+ include ::Scope::Filter::Types::LikeFilterable
22
+ include ::Scope::Filter::Types::EqualFilterable
23
+
24
+ ID = :id
25
+
26
+ FILTERS_TYPES = %i[equal like bigger_than less_than
27
+ bigger_than_or_equal_to less_than_or_equal_to in].freeze
28
+
29
+ private
30
+
31
+ def filterable_scope(user_scope)
32
+ return unallowed_filter_fields_response if unallowed_fields_to_filter?
33
+ return user_scope unless filterable_scope?
34
+
35
+ next_scope = where_equal(user_scope)
36
+ next_scope = where_like(next_scope)
37
+ next_scope = where_bigger_than(next_scope)
38
+ next_scope = where_less_than(next_scope)
39
+ next_scope = where_bigger_than_or_equal_to(next_scope)
40
+ next_scope = where_less_than_or_equal_to(next_scope)
41
+ where_in(next_scope)
42
+ end
43
+
44
+ def allowed_fields
45
+ @allowed_fields ||= controller_params_filters_fields.map do |param_key|
46
+ filter_type = FILTERS_TYPES.find do |filter_type|
47
+ param_key.to_s.end_with?(filter_type.to_s)
48
+ end
49
+
50
+ record_field = param_key.to_s.gsub("_#{filter_type}", "")&.to_sym
51
+
52
+ next unless filter_type
53
+ next unless filteable_fields.include?(record_field)
54
+
55
+ send(:"add_#{filter_type}_field", record_field)
56
+ end.compact.flatten
57
+ end
58
+
59
+ def params_filters
60
+ @params_filters ||= {}
61
+ end
62
+
63
+ def filteable_fields
64
+ @filteable_fields ||= Array.new(klass::FILTERABLE_FIELDS).push(ID)
65
+ rescue StandardError
66
+ [ID]
67
+ end
68
+
69
+ def unallowed_fields_to_filter?
70
+ try_insert_id_equal
71
+
72
+ filterable_scope? && unallowed_fields_to_filter.present?
73
+ end
74
+
75
+ def filterable_scope?
76
+ request.get? && controller_params_filters_fields.present?
77
+ end
78
+
79
+ def unallowed_fields_to_filter
80
+ @unallowed_fields_to_filter ||= controller_params_filters_fields - allowed_fields
81
+ end
82
+
83
+ def try_insert_id_equal
84
+ return if params[:id].blank?
85
+
86
+ controller_params_filters_fields.push(:id_equal)
87
+ end
88
+
89
+ def unallowed_filter_fields_response
90
+ render json: { message: "Unknown Filter Fields", body: unallowed_fields_to_filter },
91
+ status: :bad_request
92
+ end
93
+
94
+ def controller_params_filters_fields
95
+ @controller_params_filters_fields ||= controller_query_params.keys.select do |item|
96
+ FILTERS_TYPES.any? { |filter| item.to_s.end_with?(filter.to_s) }
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scope
4
+ module Filter
5
+ module Types
6
+ # Defines the behavior of the "bigger than" filter in queries.
7
+ module BiggerThanFilterable
8
+ FIELD_SUFFIX = :bigger_than
9
+
10
+ private
11
+
12
+ def where_bigger_than(scope)
13
+ return scope if bigger_than_fields.blank?
14
+
15
+ scope.where(bigger_than_sql, bigger_than_fields)
16
+ end
17
+
18
+ def bigger_than_sql
19
+ bigger_than_fields.keys.map do |param|
20
+ "#{klass.table_name}.#{param} > :#{param}"
21
+ end.join(" AND ")
22
+ end
23
+
24
+ def add_bigger_than_field(field)
25
+ field_key = :"#{field}_#{FIELD_SUFFIX}"
26
+ field_value = controller_query_params[field_key]
27
+ bigger_than_fields[field] = field_value if field_value
28
+
29
+ field_key
30
+ end
31
+
32
+ def bigger_than_fields
33
+ params_filters[FIELD_SUFFIX] ||= {}
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scope
4
+ module Filter
5
+ module Types
6
+ # Defines the behavior of the "bigger than or equal to" filter in queries.
7
+ module BiggerThanOrEqualToFilterable
8
+ FIELD_SUFFIX = :bigger_than_or_equal_to
9
+
10
+ private
11
+
12
+ def where_bigger_than_or_equal_to(scope)
13
+ return scope if bigger_than_or_equal_to_fields.blank?
14
+
15
+ scope.where(bigger_than_or_equal_to_sql, bigger_than_or_equal_to_fields)
16
+ end
17
+
18
+ def bigger_than_or_equal_to_sql
19
+ bigger_than_or_equal_to_fields.keys.map do |param|
20
+ "#{klass.table_name}.#{param} >= :#{param}"
21
+ end.join(" AND ")
22
+ end
23
+
24
+ def add_bigger_than_or_equal_to_field(field)
25
+ field_key = :"#{field}_#{FIELD_SUFFIX}"
26
+ field_value = controller_query_params[field_key]
27
+ bigger_than_or_equal_to_fields[field] = field_value if field_value
28
+
29
+ field_key
30
+ end
31
+
32
+ def bigger_than_or_equal_to_fields
33
+ params_filters[FIELD_SUFFIX] ||= {}
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scope
4
+ module Filter
5
+ module Types
6
+ # Defines the behavior of the "equal" filter in queries.
7
+ module EqualFilterable
8
+ FIELD_SUFFIX = :equal
9
+
10
+ private
11
+
12
+ def where_equal(scope)
13
+ return scope if equal_fields.blank?
14
+
15
+ scope.where(equal_sql, equal_fields)
16
+ end
17
+
18
+ def equal_sql
19
+ equal_fields.keys.map do |param|
20
+ "#{klass.table_name}.#{param} = :#{param}"
21
+ end.join(" AND ")
22
+ end
23
+
24
+ def add_equal_field(field)
25
+ field_key = :"#{field}_#{FIELD_SUFFIX}"
26
+ field_value = controller_query_params[field_key] || params[field]
27
+ equal_fields[field] = field_value if field_value
28
+
29
+ field_key
30
+ end
31
+
32
+ def equal_fields
33
+ params_filters[FIELD_SUFFIX] ||= {}
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scope
4
+ module Filter
5
+ module Types
6
+ # Defines the behavior of the "in" filter in queries.
7
+ module InFilterable
8
+ FIELD_SUFFIX = :in
9
+
10
+ private
11
+
12
+ def where_in(scope)
13
+ return scope if in_fields.blank?
14
+
15
+ serialize_in_fields
16
+
17
+ scope.where(in_sql, in_fields)
18
+ end
19
+
20
+ def in_sql
21
+ in_fields.keys.map do |param|
22
+ "#{klass.table_name}.#{param} IN (:#{param})"
23
+ end.join(" AND ")
24
+ end
25
+
26
+ def serialize_in_fields
27
+ in_fields.each do |key, value|
28
+ in_fields[key] = value.split(",")
29
+ end
30
+ end
31
+
32
+ def add_in_field(field)
33
+ field_key = :"#{field}_#{FIELD_SUFFIX}"
34
+ field_value = controller_query_params[field_key]
35
+ in_fields[field] = field_value if field_value
36
+
37
+ field_key
38
+ end
39
+
40
+ def in_fields
41
+ params_filters[FIELD_SUFFIX] ||= {}
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scope
4
+ module Filter
5
+ module Types
6
+ # Defines the behavior of the "less than" filter in queries.
7
+ module LessThanFilterable
8
+ FIELD_SUFFIX = :less_than
9
+
10
+ private
11
+
12
+ def where_less_than(scope)
13
+ return scope if less_than_fields.blank?
14
+
15
+ scope.where(less_than_sql, less_than_fields)
16
+ end
17
+
18
+ def less_than_sql
19
+ less_than_fields.keys.map do |param|
20
+ "#{klass.table_name}.#{param} < :#{param}"
21
+ end.join(" AND ")
22
+ end
23
+
24
+ def add_less_than_field(field)
25
+ field_key = :"#{field}_#{FIELD_SUFFIX}"
26
+ field_value = controller_query_params[field_key]
27
+ less_than_fields[field] = field_value if field_value
28
+
29
+ field_key
30
+ end
31
+
32
+ def less_than_fields
33
+ params_filters[FIELD_SUFFIX] ||= {}
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scope
4
+ module Filter
5
+ module Types
6
+ # Defines the behavior of the "less than or equal to" filter in queries.
7
+ module LessThanOrEqualToFilterable
8
+ FIELD_SUFFIX = :less_than_or_equal_to
9
+
10
+ private
11
+
12
+ def where_less_than_or_equal_to(scope)
13
+ return scope if less_than_or_equal_to_fields.blank?
14
+
15
+ scope.where(less_than_or_equal_to_sql, less_than_or_equal_to_fields)
16
+ end
17
+
18
+ def less_than_or_equal_to_sql
19
+ less_than_or_equal_to_fields.keys.map do |param|
20
+ "#{klass.table_name}.#{param} <= :#{param}"
21
+ end.join(" AND ")
22
+ end
23
+
24
+ def add_less_than_or_equal_to_field(field)
25
+ field_key = :"#{field}_#{FIELD_SUFFIX}"
26
+ field_value = controller_query_params[field_key]
27
+ less_than_or_equal_to_fields[field] = field_value if field_value
28
+
29
+ field_key
30
+ end
31
+
32
+ def less_than_or_equal_to_fields
33
+ params_filters[FIELD_SUFFIX] ||= {}
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scope
4
+ module Filter
5
+ module Types
6
+ # Defines the behavior of "like" filters used for partial matching in queries.
7
+ module LikeFilterable
8
+ FIELD_SUFFIX = :like
9
+
10
+ private
11
+
12
+ def where_like(scope)
13
+ return scope if like_fields.blank?
14
+
15
+ scope.where(like_sql, like_fields)
16
+ end
17
+
18
+ def like_sql
19
+ like_fields.keys.map do |param|
20
+ "CAST(#{klass.table_name}.#{param} AS TEXT) ILIKE :#{param}"
21
+ end.join(" AND ")
22
+ end
23
+
24
+ def add_like_field(field)
25
+ field_key = :"#{field}_#{FIELD_SUFFIX}"
26
+ field_value = controller_query_params[field_key].to_s
27
+
28
+ field_value = "%#{field_value}%" if field_value.present?
29
+ like_fields[field] = field_value if field_value.present?
30
+
31
+ field_key
32
+ end
33
+
34
+ def like_fields
35
+ params_filters[FIELD_SUFFIX] ||= {}
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end