eluvia-base 3.37.1

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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +225 -0
  4. data/lib/eluvia/active_model/amount_attribute.rb +108 -0
  5. data/lib/eluvia/active_model/array_attribute.rb +134 -0
  6. data/lib/eluvia/active_model/attributes.rb +417 -0
  7. data/lib/eluvia/active_model/enum_array_attribute.rb +60 -0
  8. data/lib/eluvia/active_model/enum_attribute.rb +174 -0
  9. data/lib/eluvia/active_model/object_attribute.rb +143 -0
  10. data/lib/eluvia/active_model/range_attribute.rb +74 -0
  11. data/lib/eluvia/active_record/file_array_attribute.rb +116 -0
  12. data/lib/eluvia/active_record/file_attribute.rb +158 -0
  13. data/lib/eluvia/active_record/filtering.rb +680 -0
  14. data/lib/eluvia/active_record/filters_model.rb +58 -0
  15. data/lib/eluvia/active_record/ordering.rb +110 -0
  16. data/lib/eluvia/base/config.rb +38 -0
  17. data/lib/eluvia/base/version.rb +12 -0
  18. data/lib/eluvia/errors/bad_request.rb +17 -0
  19. data/lib/eluvia/errors/forbidden.rb +17 -0
  20. data/lib/eluvia/errors/not_found.rb +17 -0
  21. data/lib/eluvia/errors/service_unavailable.rb +17 -0
  22. data/lib/eluvia/errors/standard_error.rb +33 -0
  23. data/lib/eluvia/errors/unauthorized.rb +17 -0
  24. data/lib/eluvia/errors/unprocessable_entity.rb +26 -0
  25. data/lib/eluvia/fieldset/rest_field.rb +83 -0
  26. data/lib/eluvia/fieldset/rest_fieldset.rb +158 -0
  27. data/lib/eluvia/fieldset.rb +12 -0
  28. data/lib/eluvia/handlers/error_handler.rb +60 -0
  29. data/lib/eluvia/handlers/pagination_handler.rb +25 -0
  30. data/lib/eluvia/handlers/params_handler.rb +43 -0
  31. data/lib/eluvia/helpers/attachment_helper.rb +37 -0
  32. data/lib/eluvia/integrations/eluvia_integration.rb +281 -0
  33. data/lib/eluvia/models/file.rb +44 -0
  34. data/lib/eluvia/models/file_wrapper.rb +38 -0
  35. data/lib/eluvia/models/image_wrapper.rb +38 -0
  36. data/lib/eluvia/serializers/error_serializer.rb +21 -0
  37. data/lib/eluvia/serializers/fieldset_serializer.rb +63 -0
  38. data/lib/eluvia/services/health_service.rb +56 -0
  39. data/lib/eluvia/uploads.rb +13 -0
  40. data/lib/eluvia/utils/hash_and_array.rb +98 -0
  41. data/lib/eluvia/utils/jwt.rb +36 -0
  42. data/lib/eluvia/utils/string.rb +31 -0
  43. data/lib/eluvia/utils/uuid.rb +21 -0
  44. data/lib/eluvia-base.rb +79 -0
  45. metadata +186 -0
@@ -0,0 +1,25 @@
1
+ module Eluvia
2
+ module PaginationHandler
3
+ extend ::ActiveSupport::Concern
4
+
5
+ def set_pagination(default_limit: 20, default_offset: 0, default_order_by: 'created_at:asc', fallback_order_by: 'created_at:asc', force_order_by: false)
6
+
7
+ # Pagination
8
+ @limit = params[:limit] ? params[:limit].to_i : default_limit
9
+ @offset = params[:offset] ? params[:offset].to_i : default_offset
10
+ @page = (@offset / @limit) + 1
11
+ @padding = @offset % @limit
12
+
13
+ # Order by
14
+ @order_by = {}
15
+ order_by_param = Eluvia::Base::Config.api_case == 'camel_case' ? :orderBy : :order_by
16
+ order_by = "#{!force_order_by && params.key?(order_by_param) ? params[order_by_param] : default_order_by},#{fallback_order_by}"
17
+ order_by.to_s.split(',').each do |o|
18
+ s = o.to_s.split(':')
19
+ @order_by[s.first] = s.length >= 2 && s[1].downcase == 'desc' ? :desc : :asc
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ module Eluvia
2
+ module ParamsHandler
3
+ extend ::ActiveSupport::Concern
4
+
5
+ def parse_json_param(value)
6
+ unless value.blank?
7
+ if value.is_a?(String)
8
+ JSON.parse(value) # This can produce JSON::ParserError which is translated to Eluvia::Errors::StandardError
9
+ elsif value.is_a?(ActionController::Parameters)
10
+ value.to_unsafe_h
11
+ elsif value.is_a?(Array)
12
+ result = []
13
+ value.each do |item|
14
+ if item.is_a?(String)
15
+ result << (JSON.parse(item) rescue item) # This can produce JSON::ParserError which is translated to Eluvia::Errors::StandardError
16
+ elsif item.is_a?(ActionController::Parameters)
17
+ result << item.to_unsafe_h
18
+ end
19
+ end
20
+ result
21
+ end
22
+ end
23
+ end
24
+
25
+ def disassemble_filters(params)
26
+ result = {}
27
+ params.each do |key, value|
28
+ split_key = key.to_s.split('__')
29
+ result_component = result
30
+ split_key.each_with_index do |key_component, index|
31
+ if index + 1 == split_key.length # last component
32
+ result_component[key_component.to_sym] = value
33
+ else
34
+ result_component[key_component.to_sym] = {} if result_component[key_component.to_sym].nil?
35
+ result_component = result_component[key_component.to_sym]
36
+ end
37
+ end
38
+ end
39
+ result
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ module Eluvia
2
+ module AttachmentHelper
3
+ extend ::ActiveSupport::Concern
4
+
5
+ def attachment_url(attachment, skip_check: false, expires_in: nil)
6
+ unless skip_check
7
+ return nil unless attachment.attached?
8
+ end
9
+ if Rails.application.config.active_storage.service == :local
10
+ Rails.application.routes.url_helpers.rails_blob_path(attachment, only_path: true)
11
+ elsif expires_in
12
+ attachment.url(expires_in: expires_in)
13
+ else
14
+ attachment.url
15
+ end
16
+ # NOTE: class method can't be called from instance method because of controller / view helpers, so we need to
17
+ # duplicate the code here.
18
+ end
19
+
20
+ class_methods do
21
+
22
+ def attachment_url(attachment, skip_check: false, expires_in: nil)
23
+ unless skip_check
24
+ return nil unless attachment.attached?
25
+ end
26
+ if Rails.application.config.active_storage.service == :local
27
+ Rails.application.routes.url_helpers.rails_blob_path(attachment, only_path: true)
28
+ elsif expires_in
29
+ attachment.url(expires_in: expires_in)
30
+ else
31
+ attachment.url
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,281 @@
1
+ module Eluvia
2
+ module EluviaIntegration
3
+ extend ::ActiveSupport::Concern
4
+
5
+ protected
6
+
7
+ def compose_url(endpoint)
8
+ raise Eluvia::Errors::StandardError.new('External service URL not defined.') if @service_url.blank?
9
+ "#{@service_url.to_s.rtrim('/')}/#{endpoint.trim('/')}"
10
+ end
11
+
12
+ def compose_headers_private(params = nil)
13
+ if Eluvia::Base::Config.authorization_method == 'jwt'
14
+ {
15
+ params: params,
16
+ 'x-authorization' => "Private #{Eluvia::Jwt.encode_private_jwt_token}",
17
+ 'content-type' => 'application/json'
18
+ }
19
+ elsif Eluvia::Base::Config.authorization_method == 'session_id'
20
+ {
21
+ params: params,
22
+ 'x-private-api-key' => Eluvia::Base::Config.private_api_key,
23
+ 'content-type' => 'application/json'
24
+ }
25
+ else
26
+ raise Eluvia::Errors::StandardError.new('Unknown authorization method.')
27
+ end
28
+ end
29
+
30
+ def compose_headers_public(session_id_or_user, params = nil)
31
+ if Eluvia::Base::Config.authorization_method == 'jwt'
32
+ {
33
+ params: params,
34
+ 'x-authorization' => "Bearer #{Eluvia::Jwt.encode_public_jwt_token(session_id_or_user)}",
35
+ 'content-type' => 'application/json'
36
+ }
37
+ elsif Eluvia::Base::Config.authorization_method == 'session_id'
38
+ {
39
+ params: params,
40
+ 'x-public-api-key' => Eluvia::Base::Config.public_api_key,
41
+ 'session-id' => session_id_or_user,
42
+ 'content-type' => 'application/json'
43
+ }
44
+ else
45
+ raise Eluvia::Errors::StandardError.new('Unknown authorization method.')
46
+ end
47
+ end
48
+
49
+ def compose_headers_both(session_id_or_user, params = nil)
50
+ if Eluvia::Base::Config.authorization_method == 'jwt'
51
+ compose_headers_public(session_id_or_user, params)
52
+ elsif Eluvia::Base::Config.authorization_method == 'session_id'
53
+ compose_headers_private(params).merge(compose_headers_public(session_id_or_user, params))
54
+ else
55
+ raise Eluvia::Errors::StandardError.new('Unknown authorization method.')
56
+ end
57
+ end
58
+
59
+ def perform_get_request(url, headers)
60
+ RestClient::Request.execute(
61
+ method: :get,
62
+ url: url,
63
+ headers: headers
64
+ )
65
+ rescue RestClient::NotFound => e
66
+ m = parse_error_message(e)
67
+ raise Eluvia::Errors::NotFound.new(m || 'Not found response from external service.')
68
+ rescue RestClient::BadRequest => e
69
+ m = parse_error_message(e)
70
+ raise Eluvia::Errors::BadRequest.new(m || 'Bad response from external service.')
71
+ rescue RestClient::UnprocessableEntity => e
72
+ raise Eluvia::Errors::UnprocessableEntity.new(parse_error_messages(e))
73
+ rescue RestClient::Exception => e
74
+ m = parse_error_message(e)
75
+ raise Eluvia::Errors::StandardError.new(m || 'Standard error from external service.')
76
+ end
77
+
78
+ def parse_get_request(url, headers)
79
+ response = perform_get_request(url, headers)
80
+ if response && response.code == 204
81
+ true
82
+ elsif response && response.code < 300
83
+ data = JSON.parse(response.body) rescue nil
84
+ raise Eluvia::Errors::StandardError.new('Unable to parse response from external service.') if data.nil?
85
+ normalize_data(data)
86
+ end
87
+ end
88
+
89
+ def perform_post_request(url, headers, data = nil)
90
+ RestClient::Request.execute(
91
+ method: :post,
92
+ url: url,
93
+ headers: headers,
94
+ payload: data.to_json
95
+ )
96
+ rescue RestClient::NotFound => e
97
+ m = parse_error_message(e)
98
+ raise Eluvia::Errors::NotFound.new(m || 'Not found response from external service.')
99
+ rescue RestClient::BadRequest => e
100
+ m = parse_error_message(e)
101
+ raise Eluvia::Errors::BadRequest.new(m || 'Bad response from external service.')
102
+ rescue RestClient::UnprocessableEntity => e
103
+ raise Eluvia::Errors::UnprocessableEntity.new(parse_error_messages(e))
104
+ rescue RestClient::Exception => e
105
+ m = parse_error_message(e)
106
+ raise Eluvia::Errors::StandardError.new(m || 'Standard error from external service.')
107
+ end
108
+
109
+ def parse_post_request(url, headers, data = nil)
110
+ response = perform_post_request(url, headers, data)
111
+ if response && response.code == 204
112
+ true
113
+ elsif response && response.code < 300
114
+ data = JSON.parse(response.body) rescue nil
115
+ raise Eluvia::Errors::StandardError.new('Unable to parse response from external service.') if data.nil?
116
+ normalize_data(data)
117
+ end
118
+ end
119
+
120
+ def perform_put_request(url, headers, data = nil)
121
+ RestClient::Request.execute(
122
+ method: :put,
123
+ url: url,
124
+ headers: headers,
125
+ payload: data.to_json
126
+ )
127
+ rescue RestClient::NotFound => e
128
+ m = parse_error_message(e)
129
+ raise Eluvia::Errors::NotFound.new(m || 'Not found response from external service.')
130
+ rescue RestClient::BadRequest => e
131
+ m = parse_error_message(e)
132
+ raise Eluvia::Errors::BadRequest.new(m || 'Bad response from external service.')
133
+ rescue RestClient::UnprocessableEntity => e
134
+ raise Eluvia::Errors::UnprocessableEntity.new(parse_error_messages(e))
135
+ rescue RestClient::Exception => e
136
+ m = parse_error_message(e)
137
+ raise Eluvia::Errors::StandardError.new(m || 'Standard error from external service.')
138
+ end
139
+
140
+ def parse_put_request(url, headers, data = nil)
141
+ response = perform_put_request(url, headers, data)
142
+ if response && response.code == 204
143
+ true
144
+ elsif response && response.code < 300
145
+ data = JSON.parse(response.body) rescue nil
146
+ raise Eluvia::Errors::StandardError.new('Unable to parse response from external service.') if data.nil?
147
+ normalize_data(data)
148
+ end
149
+ end
150
+
151
+ def perform_patch_request(url, headers, data = nil)
152
+ RestClient::Request.execute(
153
+ method: :patch,
154
+ url: url,
155
+ headers: headers,
156
+ payload: data.to_json
157
+ )
158
+ rescue RestClient::NotFound => e
159
+ m = parse_error_message(e)
160
+ raise Eluvia::Errors::NotFound.new(m || 'Not found response from external service.')
161
+ rescue RestClient::BadRequest => e
162
+ m = parse_error_message(e)
163
+ raise Eluvia::Errors::BadRequest.new(m || 'Bad response from external service.')
164
+ rescue RestClient::UnprocessableEntity => e
165
+ raise Eluvia::Errors::UnprocessableEntity.new(parse_error_messages(e))
166
+ rescue RestClient::Exception => e
167
+ m = parse_error_message(e)
168
+ raise Eluvia::Errors::StandardError.new(m || 'Standard error from external service.')
169
+ end
170
+
171
+ def parse_patch_request(url, headers, data = nil)
172
+ response = perform_patch_request(url, headers, data)
173
+ if response && response.code == 204
174
+ true
175
+ elsif response && response.code < 300
176
+ data = JSON.parse(response.body) rescue nil
177
+ raise Eluvia::Errors::StandardError.new('Unable to parse response from external service.') if data.nil?
178
+ normalize_data(data)
179
+ end
180
+ end
181
+
182
+ def perform_delete_request(url, headers)
183
+ RestClient::Request.execute(
184
+ method: :delete,
185
+ url: url,
186
+ headers: headers
187
+ )
188
+ rescue RestClient::NotFound => e
189
+ m = parse_error_message(e)
190
+ raise Eluvia::Errors::NotFound.new(m || 'Not found response from external service.')
191
+ rescue RestClient::BadRequest => e
192
+ m = parse_error_message(e)
193
+ raise Eluvia::Errors::BadRequest.new(m || 'Bad response from external service.')
194
+ rescue RestClient::UnprocessableEntity => e
195
+ raise Eluvia::Errors::UnprocessableEntity.new(parse_error_messages(e))
196
+ rescue RestClient::Exception => e
197
+ m = parse_error_message(e)
198
+ raise Eluvia::Errors::StandardError.new(m || 'Standard error from external service.')
199
+ end
200
+
201
+ def parse_delete_request(url, headers)
202
+ response = perform_delete_request(url, headers)
203
+ if response && response.code == 204
204
+ true
205
+ elsif response && response.code < 300
206
+ data = JSON.parse(response.body) rescue nil
207
+ raise Eluvia::Errors::StandardError.new('Unable to parse response from external service.') if data.nil?
208
+ normalize_data(data)
209
+ end
210
+ end
211
+
212
+ def parse_error_message(e)
213
+ response_data = JSON.parse(e.response.to_s) rescue nil
214
+ if response_data.is_a?(Hash) && response_data['errors']
215
+ response_data['errors'].first&.[]('message')
216
+ end
217
+ end
218
+
219
+ def parse_error_messages(e)
220
+ result = {}
221
+ response_data = JSON.parse(e.response.to_s) rescue nil
222
+ if response_data.is_a?(Hash) && response_data['errors']
223
+ response_data['errors'].each do |error|
224
+ result[error['source']] = error['message']
225
+ end
226
+ end
227
+ result
228
+ end
229
+
230
+ def paginate_with_offset(limit = 20, &block)
231
+ offset = 0
232
+ not_empty = true
233
+ result = []
234
+ while not_empty
235
+ data = block.call(limit, offset)
236
+ if data.is_a?(Array) && !data.empty?
237
+ result += data
238
+ offset += limit
239
+ else
240
+ not_empty = false
241
+ end
242
+ end
243
+ result
244
+ end
245
+
246
+ def paginate_with_page(&block)
247
+ page = 1
248
+ not_empty = true
249
+ result = []
250
+ while not_empty
251
+ data = block.call(page)
252
+ if data.is_a?(Array) && !data.empty?
253
+ result += data
254
+ page += 1
255
+ else
256
+ not_empty = false
257
+ end
258
+ end
259
+ result
260
+ end
261
+
262
+ def calculate_limit_and_offset(page, per)
263
+ page = 1 if page.to_i < 1
264
+ {
265
+ limit: per,
266
+ offset: (page.to_i - 1) * per.to_i
267
+ }
268
+ end
269
+
270
+ def normalize_data(data)
271
+ if data.is_a?(Hash)
272
+ return data.deep_symbolize_keys
273
+ end
274
+ if data.is_a?(Array)
275
+ return data.map { |item| normalize_data(item) }
276
+ end
277
+ data
278
+ end
279
+
280
+ end
281
+ end
@@ -0,0 +1,44 @@
1
+ module Eluvia
2
+ class File
3
+ include ::ActiveModel::Model
4
+ include Eluvia::ActiveModel::Attributes
5
+ include Eluvia::AttachmentHelper
6
+
7
+ # ***********************************************************************
8
+ # Attributes
9
+ # ***********************************************************************
10
+
11
+ # MIME type of the file
12
+ string_attr :type
13
+
14
+ # URL to access the file
15
+ string_attr :url
16
+
17
+ # File name with extension
18
+ string_attr :filename
19
+
20
+ # Size of the file in bytes
21
+ integer_attr :size
22
+
23
+ # Base64 encoded content of the file, used for uploads
24
+ string_attr :content
25
+
26
+ # Key identifying a previously uploaded (chunked/direct) temp object, used as an alternative to
27
+ # `content` for finalizing large uploads via `Eluvia::Uploads.finalizer`
28
+ string_attr :upload_key
29
+
30
+ # ***********************************************************************
31
+ # Constructor from attachment
32
+ # ***********************************************************************
33
+
34
+ def self.from_active_storage_attachment(attachment, **options)
35
+ new(
36
+ type: attachment.content_type,
37
+ url: attachment_url(attachment, **options),
38
+ filename: attachment.filename,
39
+ size: attachment.byte_size
40
+ )
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ module Eluvia
2
+ class FileWrapper
3
+ include ::ActiveModel::Model
4
+ include Eluvia::ActiveModel::Attributes
5
+ include Eluvia::ActiveRecord::FileAttribute
6
+
7
+ # ***********************************************************************
8
+ # Attributes
9
+ # ***********************************************************************
10
+
11
+ file_attr :file, nature: :file, declare: true
12
+
13
+ string_attr :id
14
+
15
+ def id
16
+ @id ||= (self.url ? Eluvia::Uuid.str_to_uuid("File-#{self.url}") : nil)
17
+ end
18
+
19
+ string_attr :type
20
+
21
+ def type
22
+ file&.type
23
+ end
24
+
25
+ string_attr :url
26
+
27
+ def url
28
+ file&.url
29
+ end
30
+
31
+ string_attr :filename
32
+
33
+ def filename
34
+ file&.filename
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ module Eluvia
2
+ class ImageWrapper
3
+ include ::ActiveModel::Model
4
+ include Eluvia::ActiveModel::Attributes
5
+ include Eluvia::ActiveRecord::FileAttribute
6
+
7
+ # ***********************************************************************
8
+ # Attributes
9
+ # ***********************************************************************
10
+
11
+ file_attr :file, nature: :image, declare: true
12
+
13
+ string_attr :id
14
+
15
+ def id
16
+ @id ||= (self.url ? Eluvia::Uuid.str_to_uuid("Image-#{self.url}") : nil)
17
+ end
18
+
19
+ string_attr :type
20
+
21
+ def type
22
+ file&.type
23
+ end
24
+
25
+ string_attr :url
26
+
27
+ def url
28
+ file&.url
29
+ end
30
+
31
+ string_attr :filename
32
+
33
+ def filename
34
+ file&.filename
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ module Eluvia
2
+ class ErrorSerializer
3
+
4
+ attr_reader :error
5
+
6
+ def initialize(error)
7
+ @error = error
8
+ end
9
+
10
+ def to_h
11
+ {
12
+ errors: Array.wrap(error.to_h).flatten
13
+ }
14
+ end
15
+
16
+ def to_json(_payload)
17
+ to_h.to_json
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,63 @@
1
+ module Eluvia
2
+ module Serializers
3
+ module FieldsetSerializer
4
+
5
+ def serialize(record, fields: nil)
6
+ read_fieldset(fields).to_h do |rest_field|
7
+ [rest_field.name, read_attribute(record, rest_field.name)]
8
+ end
9
+ end
10
+
11
+ def read_fieldset(fields = nil)
12
+ @read_fieldsets ||= {}
13
+ @read_fieldsets[fields] ||= resolve_read_fieldset(fields)
14
+ end
15
+
16
+ def readable_fields
17
+ raise NotImplementedError, "#{self.class} must implement #readable_fields"
18
+ end
19
+
20
+ def default_read_fields
21
+ nil
22
+ end
23
+
24
+ def read_attribute(record, field_name)
25
+ raise NotImplementedError, "#{self.class} must implement #read_attribute"
26
+ end
27
+
28
+ private
29
+
30
+ def resolve_read_fieldset(fields)
31
+ readable = readable_fields.map(&:to_s)
32
+ defaults = (default_read_fields || readable_fields).map(&:to_s)
33
+
34
+ requested =
35
+ if fields.present?
36
+ Eluvia::Fieldset::RFS.create_from_string(fields.to_s, default_field_fieldset_string: Eluvia::Fieldset::DEFAULT_FIELDS)
37
+ else
38
+ Eluvia::Fieldset::RFS.create_from_list(defaults, default_field_fieldset_string: Eluvia::Fieldset::DEFAULT_FIELDS)
39
+ end
40
+
41
+ if requested.include?(Eluvia::Fieldset::ALL_FIELDS)
42
+ # Wildcard `__all__` replaces the whole request - anything else next to it is discarded
43
+ requested = Eluvia::Fieldset::RFS.create_from_list(readable, default_field_fieldset_string: Eluvia::Fieldset::ALL_FIELDS)
44
+ elsif requested.include?(Eluvia::Fieldset::DEFAULT_FIELDS)
45
+ # Wildcard `__default__` is replaced by the default fields and joined with whatever else was requested
46
+ requested.delete(Eluvia::Fieldset::DEFAULT_FIELDS)
47
+ requested |= Eluvia::Fieldset::RFS.create_from_list(defaults, default_field_fieldset_string: Eluvia::Fieldset::DEFAULT_FIELDS)
48
+ end
49
+
50
+ invalid_fields = requested.reject { |rf| readable.include?(rf.name) }.map(&:name)
51
+ if invalid_fields.any?
52
+ messages = invalid_fields.to_h do |field_name|
53
+ [field_name, I18n.t('eluvia.errors.fieldset.unknown_field', default: 'Unknown field.')]
54
+ end
55
+ raise Eluvia::Errors::UnprocessableEntity.new(messages)
56
+ end
57
+
58
+ requested & Eluvia::Fieldset::RFS.create_from_list(readable)
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,56 @@
1
+ module Eluvia
2
+ class HealthService
3
+ include Singleton
4
+
5
+ CHECKS = [:databases].freeze
6
+ CHECKED_DATABASES = [:default].freeze
7
+
8
+ def check
9
+ result_content = {}
10
+ result_content[:checks] = {}
11
+ result_ok = true
12
+ CHECKS.each do |check|
13
+ if check == :databases
14
+ check_content, check_ok = check_databases
15
+ else
16
+ raise Eluvia::Errors::StandardError.new("Unknown check #{check}.")
17
+ end
18
+ result_content[:checks][check] = check_content
19
+ result_ok &&= check_ok
20
+ end
21
+ if result_ok
22
+ result_content[:message] = 'I am alive.'
23
+ else
24
+ result_content[:message] = 'One of the checks failed.'
25
+ end
26
+ [result_content, result_ok]
27
+ end
28
+
29
+ private
30
+
31
+ def check_databases
32
+ result_content = []
33
+ result_ok = true
34
+ CHECKED_DATABASES.each do |database|
35
+ ok = check_database(database)
36
+ result_ok &&= ok
37
+ result_content << {
38
+ database => {
39
+ ok: ok
40
+ }
41
+ }
42
+ end
43
+ [result_content, result_ok]
44
+ end
45
+
46
+ def check_database(_database)
47
+
48
+ ::ActiveRecord::Base.connection.execute('SELECT 1')
49
+ true
50
+ rescue StandardError
51
+ false
52
+
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,13 @@
1
+ module Eluvia
2
+ module Uploads
3
+
4
+ # Extension point for finalizing chunked/direct uploads (identified by `Eluvia::File#upload_key`)
5
+ # into the underlying ActiveStorage attachment. Left unset by default so that eluvia-base has no
6
+ # dependency on any specific storage provider (e.g. aws-sdk-s3) or on habarico.
7
+ #
8
+ # Consumers register a callable of the form:
9
+ # ->(attachment_record, upload_key, filename) { ... }
10
+ mattr_accessor :finalizer
11
+
12
+ end
13
+ end