habarico 7.3.0.pre.beta.10 → 7.3.0.pre.beta.12
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/VERSION +1 -1
- data/lib/generators/habarico/preferences/install_generator.rb +3 -0
- data/lib/habarico/config.rb +2 -2
- data/lib/habarico/generators/endpoint/field_options_generator.rb +2 -2
- data/lib/habarico/generators/endpoint/field_schema_generator.rb +17 -2
- data/lib/habarico/generators/resource/local_resource_schema_generator.rb +3 -3
- data/lib/habarico/helpers/reflection_helper.rb +5 -0
- data/lib/habarico/resolvers/file_rules_resolver.rb +30 -10
- data/lib/habarico/resolvers/record_params_resolver.rb +10 -4
- data/lib/habarico/schema/endpoint/field_options.rb +2 -2
- data/lib/habarico.rb +6 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4ddeb09d8eba0c8f227e02078ced119d7658696153db4fd3af5ab872cb50b6de
|
|
4
|
+
data.tar.gz: 2ebd161afbd8e260c57798e9b524b0f025a3baf61e9f912c18ab1f80ac797b03
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4bb6cd1b02539eaef781b87745d45d4070bede289de5756273c0cb6faa0bda27875f36c61dfcc1290dfb5a19b14b524196a755f1dcab05a72cb403cba9caeb0e
|
|
7
|
+
data.tar.gz: e6e3fe637deb6d0ce0e60124130b6ff901da11e5d4cdf2875a3ffc8fc1397812a635a0af6573afd4a8d4716614444573201a5d37537ce973a6b0d41556452485
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.3.0-beta.
|
|
1
|
+
7.3.0-beta.12
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
require "rails/generators"
|
|
2
2
|
require "rails/generators/migration"
|
|
3
|
+
# Defines ActiveRecord::Generators::Base, whose next_migration_number is reused below. Railties
|
|
4
|
+
# does not autoload it, so without this require the generator fails with a NameError.
|
|
5
|
+
require "rails/generators/active_record"
|
|
3
6
|
|
|
4
7
|
module Habarico
|
|
5
8
|
module Preferences
|
data/lib/habarico/config.rb
CHANGED
|
@@ -53,8 +53,8 @@ module Habarico
|
|
|
53
53
|
# Name of the method Habarico calls (via `send`) as its authentication before_action, on both
|
|
54
54
|
# Habarico::Controller and Habarico::ApplicationController. Point this at an existing no-argument
|
|
55
55
|
# auth method on your ApplicationController (e.g. `:authenticate_user!`) to reuse it directly. If
|
|
56
|
-
# your auth method
|
|
57
|
-
#
|
|
56
|
+
# your auth method takes arguments, define a one-line wrapper method instead and point this at
|
|
57
|
+
# that.
|
|
58
58
|
mattr_accessor :authenticate_method
|
|
59
59
|
@@authenticate_method = :authenticate_request
|
|
60
60
|
|
|
@@ -94,7 +94,7 @@ module Habarico
|
|
|
94
94
|
nil # not possible to generate dynamic render without FieldReference
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
# Largest file (bytes) this
|
|
97
|
+
# Largest file (bytes) this file field accepts, derived from its own
|
|
98
98
|
# `active_storage_validations` size declaration (HAB-966). `habarico_enterprise.uploads`, when
|
|
99
99
|
# installed, additionally folds in the project-wide upload size ceiling for a field a
|
|
100
100
|
# chunked-upload provider serves - see `Habarico::FileRulesResolver`.
|
|
@@ -105,7 +105,7 @@ module Habarico
|
|
|
105
105
|
file_rules(model, attr_name).max_size
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
-
# File types this
|
|
108
|
+
# File types this file field accepts, derived from its own `active_storage_validations`
|
|
109
109
|
# content-type declaration (HAB-966).
|
|
110
110
|
def generate_accept_at(model, attr_name)
|
|
111
111
|
file_rules(model, attr_name).accept
|
|
@@ -46,7 +46,7 @@ module Habarico
|
|
|
46
46
|
sort_by: generate_sort_by(model, field_name, attr_name, variant),
|
|
47
47
|
reference: Habarico::ResourceReference.build(generate_reference(model, field_name, attr_name, variant)),
|
|
48
48
|
enum: generate_enum(model, field_name, attr_name, variant),
|
|
49
|
-
items: generate_items(model, field_name, attr_name, variant, subfields),
|
|
49
|
+
items: generate_items(model, field_name, attr_name, variant, subfields, field_reference: field_reference),
|
|
50
50
|
properties: generate_properties(model, field_name, attr_name, variant, subfields),
|
|
51
51
|
options: generate_options(model, field_name, attr_name, variant),
|
|
52
52
|
)
|
|
@@ -301,10 +301,22 @@ module Habarico
|
|
|
301
301
|
nil
|
|
302
302
|
end
|
|
303
303
|
|
|
304
|
-
def generate_items(model, field_name, attr_name, _variant, subfields)
|
|
304
|
+
def generate_items(model, field_name, attr_name, _variant, subfields, field_reference: nil)
|
|
305
305
|
name = ''
|
|
306
306
|
label = generate_label(model, field_name, attr_name, nil)
|
|
307
307
|
|
|
308
|
+
# "Append only" file array attribute is a list of uploads, each item a file the frontend uploads
|
|
309
|
+
if Habarico::ReflectionHelper.is_append_only_file_array_attr?(model, attr_name)
|
|
310
|
+
return Habarico::FieldSchema.new(
|
|
311
|
+
name: name,
|
|
312
|
+
label: label,
|
|
313
|
+
type: 'object',
|
|
314
|
+
format: model.find_file_array_attr(attr_name).fetch(:nature) == :image ? 'base64-image' : 'base64-file',
|
|
315
|
+
properties: fields_schema_generator.generate(Eluvia::File, %i[type url filename size content]),
|
|
316
|
+
options: field_options_generator.generate(model, attr_name, field_reference),
|
|
317
|
+
)
|
|
318
|
+
end
|
|
319
|
+
|
|
308
320
|
# Manually declared "has many" reference is interpreted as array of relations, so we need to define items schema
|
|
309
321
|
if Habarico::ReflectionHelper.is_has_many_reference?(model, field_name)
|
|
310
322
|
reference = model.find_reference(field_name)
|
|
@@ -374,6 +386,9 @@ module Habarico
|
|
|
374
386
|
end
|
|
375
387
|
|
|
376
388
|
def generate_options(model, _field_name, attr_name, _variant)
|
|
389
|
+
# The upload options of an append-only file array are on its items, where the frontend reads them
|
|
390
|
+
return nil if Habarico::ReflectionHelper.is_append_only_file_array_attr?(model, attr_name)
|
|
391
|
+
|
|
377
392
|
field_options_generator.generate(model, attr_name, nil)
|
|
378
393
|
end
|
|
379
394
|
|
|
@@ -89,9 +89,9 @@ module Habarico
|
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
def generate_endpoints(_context, endpoint_parameters)
|
|
92
|
-
# Determine endpoint methods to be included
|
|
93
|
-
#
|
|
94
|
-
methods = endpoint_parameters.select { |e| e.
|
|
92
|
+
# Determine endpoint methods to be included from the endpoint parameters of the actions and views that address
|
|
93
|
+
# the resource itself (no reference; an action name always comes with one). Additionally, always include GET.
|
|
94
|
+
methods = endpoint_parameters.select { |e| e.reference.blank? }.map { |e| e.method.to_sym }
|
|
95
95
|
methods << :get # Ensure GET is always present
|
|
96
96
|
methods = methods.uniq
|
|
97
97
|
|
|
@@ -21,6 +21,11 @@ module Habarico
|
|
|
21
21
|
model_class.respond_to?(:has_reference?) && model_class.has_reference?(field_name, type: :has_many)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
def self.is_append_only_file_array_attr?(model_class, field_name)
|
|
25
|
+
model_class.respond_to?(:has_file_array_attr?) && model_class.has_file_array_attr?(field_name) &&
|
|
26
|
+
model_class.find_file_array_attr(field_name).fetch(:append_only)
|
|
27
|
+
end
|
|
28
|
+
|
|
24
29
|
# *************************************************************************
|
|
25
30
|
# Active model reflection methods
|
|
26
31
|
# *************************************************************************
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'marcel'
|
|
2
2
|
|
|
3
3
|
module Habarico
|
|
4
|
-
# The file rules of one `file_attr`: the allowed content types and the largest size its
|
|
4
|
+
# The file rules of one `file_attr` or `file_array_attr`: the allowed content types and the largest size its
|
|
5
5
|
# `active_storage_validations` declarations admit - the rules a value is measured against, never
|
|
6
6
|
# a value's own claimed properties. `allowed_content_types` is nil when the field declares no
|
|
7
7
|
# content-type restriction; `max_size` is nil when it declares no upper bound.
|
|
@@ -22,12 +22,13 @@ module Habarico
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
# No declarations at all - what an unknown model, a
|
|
26
|
-
# without `active_storage_validations` resolves to.
|
|
25
|
+
# No declarations at all - what an unknown model, an attribute that is not a file field, or a
|
|
26
|
+
# host without `active_storage_validations` resolves to.
|
|
27
27
|
FileRules::NONE = FileRules.new(allowed_content_types: nil, max_size: nil).freeze
|
|
28
28
|
|
|
29
|
-
# Resolves the `FileRules` of a model's `file_attr` from the host's
|
|
30
|
-
# declarations on its `underlying_attachment
|
|
29
|
+
# Resolves the `FileRules` of a model's `file_attr` or `file_array_attr` from the host's
|
|
30
|
+
# `active_storage_validations` declarations on its `underlying_attachment` /
|
|
31
|
+
# `underlying_attachments`. Mirrors the Python package's
|
|
31
32
|
# `habarico.validators.content_types` / `habarico.validators.files`.
|
|
32
33
|
#
|
|
33
34
|
# `active_storage_validations` is deliberately NOT a dependency of this gem: a host that does not
|
|
@@ -38,22 +39,41 @@ module Habarico
|
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
# The `FileRules` of `attr_name`; `FileRules::NONE` when there is nothing to derive: no model,
|
|
41
|
-
# the attribute is
|
|
42
|
-
# `active_storage_validations` is not installed.
|
|
42
|
+
# the attribute is neither file field or has no underlying attachment, or
|
|
43
|
+
# `active_storage_validations` is not installed. An image `file_array_attr` keeps only the
|
|
44
|
+
# image types of its attachment's declaration, which may be shared with a file array filtered
|
|
45
|
+
# to the other types.
|
|
43
46
|
def resolve(attr_name)
|
|
44
47
|
return FileRules::NONE if @model.nil? || attr_name.blank?
|
|
45
|
-
return FileRules::NONE unless @model.respond_to?(:has_file_attr?) && @model.has_file_attr?(attr_name)
|
|
46
48
|
return FileRules::NONE unless @model.respond_to?(:validators_on)
|
|
47
49
|
|
|
48
|
-
attachment =
|
|
50
|
+
attachment, images_only = underlying_attachment(attr_name)
|
|
49
51
|
return FileRules::NONE if attachment.blank?
|
|
50
52
|
|
|
51
53
|
validators = @model.validators_on(attachment)
|
|
52
|
-
|
|
54
|
+
content_types = content_types_from(validators)
|
|
55
|
+
content_types = image_content_types(content_types) if images_only
|
|
56
|
+
FileRules.new(allowed_content_types: content_types, max_size: max_size_from(validators))
|
|
53
57
|
end
|
|
54
58
|
|
|
55
59
|
private
|
|
56
60
|
|
|
61
|
+
# The attachment name behind a file attribute, and whether only its image types apply.
|
|
62
|
+
def underlying_attachment(attr_name)
|
|
63
|
+
if @model.respond_to?(:has_file_attr?) && @model.has_file_attr?(attr_name)
|
|
64
|
+
[@model.find_file_attr(attr_name)&.fetch(:underlying_attachment, nil), false]
|
|
65
|
+
elsif @model.respond_to?(:has_file_array_attr?) && @model.has_file_array_attr?(attr_name)
|
|
66
|
+
spec = @model.find_file_array_attr(attr_name)
|
|
67
|
+
[spec[:underlying_attachments], spec[:nature] == :image]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def image_content_types(content_types)
|
|
72
|
+
return nil if content_types.nil?
|
|
73
|
+
|
|
74
|
+
content_types.select { |type| type.is_a?(::Regexp) || type.to_s.start_with?('image/') }.presence
|
|
75
|
+
end
|
|
76
|
+
|
|
57
77
|
# The allowed content types of an `ActiveStorageValidations::ContentTypeValidator`, normalized
|
|
58
78
|
# through Marcel exactly as the validator itself does at save time (`:png` and `'png'` both
|
|
59
79
|
# become `'image/png'`), or nil when the field declares none.
|
|
@@ -9,7 +9,7 @@ module Habarico
|
|
|
9
9
|
# `params` narrowed to the resource's `fields` and `write_only_fields`.
|
|
10
10
|
def resolve(params)
|
|
11
11
|
write_fields = (@definition.fields + @definition.write_only_fields).uniq
|
|
12
|
-
params.permit(*write_fields.map { |field| permit_spec(field) })
|
|
12
|
+
params.permit(*write_fields.map { |field| permit_spec(field) }.uniq)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
# A 422 with each error of `record` under every name its attribute is written by (an amount as
|
|
@@ -26,16 +26,22 @@ module Habarico
|
|
|
26
26
|
private
|
|
27
27
|
|
|
28
28
|
def permit_spec(field)
|
|
29
|
+
# Normalize Symbol/String/Hash/FieldReference entries so `permit` never receives a FieldReference.
|
|
30
|
+
name = Habarico::AttributesHelper.field_name(field)
|
|
31
|
+
model = @definition.model
|
|
32
|
+
|
|
33
|
+
# An append-only `file_array_attr` takes a list of base64 `{filename:, content:}` uploads,
|
|
34
|
+
# whatever subfields its declaration lists for reading.
|
|
35
|
+
return { name => %i[filename content] } if Habarico::ReflectionHelper.is_append_only_file_array_attr?(model, name)
|
|
36
|
+
|
|
29
37
|
# Hash entries (`{ name => subfields }`) are already a nested permit spec - keep intact.
|
|
30
38
|
return field if field.is_a?(::Hash)
|
|
31
39
|
|
|
32
|
-
# Normalize Symbol/String/FieldReference entries so `permit` never receives a FieldReference.
|
|
33
|
-
name = Habarico::AttributesHelper.field_name(field)
|
|
34
40
|
# `ActionController::Parameters#permit` only allows scalar values for a bare field name -
|
|
35
41
|
# a Hash value (e.g. a `file_attr` field's base64 `{filename:, content:}` or chunked-upload
|
|
36
42
|
# `{upload_key:}` payload) is silently dropped unless its nested keys are explicitly
|
|
37
43
|
# permitted. Expand file_attr fields into a nested permit spec; leave every other field as-is.
|
|
38
|
-
if
|
|
44
|
+
if model.respond_to?(:has_file_attr?) && model.has_file_attr?(name)
|
|
39
45
|
{ name => %i[filename content upload_key] }
|
|
40
46
|
else
|
|
41
47
|
name
|
|
@@ -42,13 +42,13 @@ module Habarico
|
|
|
42
42
|
has_one_attr :ai, 'Habarico::AiFieldOptions'
|
|
43
43
|
|
|
44
44
|
# Largest file (bytes) the frontend accepts for this field, checked before the upload starts.
|
|
45
|
-
# Derived from a
|
|
45
|
+
# Derived from a file field's own `active_storage_validations` size declaration (HAB-966); see
|
|
46
46
|
# `Habarico::FileRulesResolver`. `habarico_enterprise.uploads` additionally folds in its
|
|
47
47
|
# project-wide `Config.max_upload_size` ceiling for a field a chunked-upload provider serves.
|
|
48
48
|
integer_attr :max_size
|
|
49
49
|
|
|
50
50
|
# File types the frontend accepts for this field, checked before the upload starts. Derived
|
|
51
|
-
# from a
|
|
51
|
+
# from a file field's own `active_storage_validations` content-type declaration (HAB-966); see
|
|
52
52
|
# `Habarico::FileRulesResolver`.
|
|
53
53
|
has_many_attr :accept, 'String'
|
|
54
54
|
|
data/lib/habarico.rb
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
require "eluvia-base"
|
|
2
2
|
|
|
3
|
+
# Runtime dependencies of the gemspec. Bundler.require in a host app only requires what that app
|
|
4
|
+
# lists itself, so without these a host gets `undefined method 'page'` on its first list request
|
|
5
|
+
# (kaminari) or an uninitialised constant from a permission callable (pundit).
|
|
6
|
+
require "kaminari"
|
|
7
|
+
require "pundit"
|
|
8
|
+
|
|
3
9
|
# *****************************************************************************
|
|
4
10
|
# Basic library
|
|
5
11
|
# *****************************************************************************
|