praxis 2.0.pre.29 → 2.0.pre.30

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.ruby-version +1 -1
  4. data/CHANGELOG.md +24 -0
  5. data/SELECTOR_NOTES.txt +0 -0
  6. data/lib/praxis/application.rb +4 -0
  7. data/lib/praxis/blueprint.rb +13 -1
  8. data/lib/praxis/blueprint_attribute_group.rb +29 -0
  9. data/lib/praxis/docs/open_api/schema_object.rb +8 -7
  10. data/lib/praxis/endpoint_definition.rb +1 -1
  11. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +11 -11
  12. data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +0 -1
  13. data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +1 -1
  14. data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +54 -4
  15. data/lib/praxis/extensions/pagination/ordering_params.rb +38 -10
  16. data/lib/praxis/extensions/pagination/pagination_handler.rb +3 -3
  17. data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +1 -1
  18. data/lib/praxis/mapper/resource.rb +155 -14
  19. data/lib/praxis/mapper/selector_generator.rb +248 -46
  20. data/lib/praxis/media_type_identifier.rb +1 -1
  21. data/lib/praxis/multipart/part.rb +2 -2
  22. data/lib/praxis/plugins/mapper_plugin.rb +4 -3
  23. data/lib/praxis/renderer.rb +1 -1
  24. data/lib/praxis/routing_config.rb +1 -1
  25. data/lib/praxis/tasks/console.rb +21 -26
  26. data/lib/praxis/types/multipart_array.rb +1 -1
  27. data/lib/praxis/version.rb +1 -1
  28. data/lib/praxis.rb +1 -0
  29. data/praxis.gemspec +1 -1
  30. data/spec/functional_library_spec.rb +187 -0
  31. data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +11 -1
  32. data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +16 -4
  33. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +0 -2
  34. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +0 -2
  35. data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +111 -25
  36. data/spec/praxis/extensions/pagination/ordering_params_spec.rb +70 -0
  37. data/spec/praxis/mapper/resource_spec.rb +40 -4
  38. data/spec/praxis/mapper/selector_generator_spec.rb +979 -296
  39. data/spec/praxis/request_stages/action_spec.rb +1 -1
  40. data/spec/spec_app/app/controllers/authors.rb +37 -0
  41. data/spec/spec_app/app/controllers/books.rb +31 -0
  42. data/spec/spec_app/app/resources/author.rb +21 -0
  43. data/spec/spec_app/app/resources/base.rb +14 -0
  44. data/spec/spec_app/app/resources/book.rb +43 -0
  45. data/spec/spec_app/app/resources/tag.rb +9 -0
  46. data/spec/spec_app/app/resources/tagging.rb +9 -0
  47. data/spec/spec_app/config/environment.rb +16 -1
  48. data/spec/spec_app/design/media_types/author.rb +13 -0
  49. data/spec/spec_app/design/media_types/book.rb +22 -0
  50. data/spec/spec_app/design/media_types/tag.rb +11 -0
  51. data/spec/spec_app/design/media_types/tagging.rb +10 -0
  52. data/spec/spec_app/design/resources/authors.rb +35 -0
  53. data/spec/spec_app/design/resources/books.rb +39 -0
  54. data/spec/spec_helper.rb +0 -1
  55. data/spec/support/spec_resources.rb +20 -7
  56. data/spec/{praxis/extensions/support → support}/spec_resources_active_model.rb +14 -0
  57. metadata +24 -7
  58. /data/spec/{functional_spec.rb → functional_cloud_spec.rb} +0 -0
  59. /data/spec/{praxis/extensions/support → support}/spec_resources_sequel.rb +0 -0
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require_relative '../../../support/spec_media_types'
5
+
6
+ describe Praxis::Extensions::Pagination::OrderingParams do
7
+ let(:blog_ordering_type) { Praxis::Types::OrderingParams.for(Blog) }
8
+
9
+ context '#validate' do
10
+ context 'full enforcement and nested relationships' do
11
+ let(:order_attr) do
12
+ Attributor::Attribute.new(blog_ordering_type) do
13
+ by_fields :id, 'recent_posts.title', 'recent_posts.author.first'
14
+ enforce_for :all
15
+ end
16
+ end
17
+ it 'works for allowed fields' do
18
+ ['id', 'recent_posts.title', 'recent_posts.author.first'].each do |str|
19
+ expect(order_attr.load(str).validate).to be_empty
20
+ end
21
+ end
22
+ it 'enforces all components' do
23
+ # Allowed at any position
24
+ expect(order_attr.load('recent_posts.title,id').validate).to be_empty
25
+ # Not allowed even in second position if it isn't on the list
26
+ expect(order_attr.load('recent_posts.title,name').validate).to_not be_empty
27
+ end
28
+ it 'fails for valid but unallowed fields' do
29
+ ['name', 'recent_posts.id'].each do |str|
30
+ expect(order_attr.load(str).validate).to_not be_empty
31
+ end
32
+ end
33
+ it 'fails for invalid fields' do
34
+ ['nothing', 'badassoc.none'].each do |str|
35
+ expect(order_attr.load(str).validate).to_not be_empty
36
+ end
37
+ end
38
+ end
39
+ context 'first-only enforcement' do
40
+ let(:order_attr) do
41
+ Attributor::Attribute.new(blog_ordering_type) do
42
+ by_fields :id, 'recent_posts.title'
43
+ enforce_for :first
44
+ end
45
+ end
46
+ it 'enforces only first components' do
47
+ # It does not allow 'name' if it is in the first position
48
+ expect(order_attr.load('name,recent_posts.title').validate).to_not be_empty
49
+ # Allows 'name' if it is not in the first position
50
+ expect(order_attr.load('recent_posts.title,name').validate).to be_empty
51
+ end
52
+ end
53
+ context 'default enforcement' do
54
+ let(:order_attr) do
55
+ Attributor::Attribute.new(blog_ordering_type)
56
+ end
57
+ it 'allows any attribute of the mediatype' do
58
+ %w[id name href description].each do |str|
59
+ expect(order_attr.load(str).validate).to be_empty
60
+ end
61
+ end
62
+ it 'enforces only first components' do
63
+ # It allows non-defined field in second position
64
+ expect(order_attr.load('name,recent_posts.title').validate).to be_empty
65
+ # It does not allow non-defined field in first position
66
+ expect(order_attr.load('recent_posts.title,name').validate).to_not be_empty
67
+ end
68
+ end
69
+ end
70
+ end
@@ -18,19 +18,55 @@ describe Praxis::Mapper::Resource do
18
18
  subject(:properties) { resource.properties }
19
19
 
20
20
  it 'includes directly-set properties' do
21
- expect(properties[:other_resource]).to eq(dependencies: [:other_model], through: nil, as: :other_resource)
21
+ expect(properties[:other_resource]).to eq(dependencies: [:other_model])
22
22
  end
23
23
 
24
24
  it 'includes aliases as well if different from name' do
25
- expect(properties[:aliased_association]).to eq(dependencies: [:name], through: nil, as: :other_model)
25
+ expect(properties[:aliased_association]).to eq(dependencies: nil, as: :other_model)
26
26
  end
27
27
 
28
28
  it 'inherits from a superclass' do
29
- expect(properties[:href]).to eq(dependencies: [:id], through: nil, as: :href)
29
+ expect(properties[:href]).to eq(dependencies: [:id])
30
30
  end
31
31
 
32
32
  it 'properly overrides a property from the parent' do
33
- expect(properties[:name]).to eq(dependencies: [:simple_name], through: nil, as: :name)
33
+ expect(properties[:name]).to eq(dependencies: [:nested_name])
34
+ end
35
+ end
36
+
37
+ context 'using strings for names' do
38
+ let(:bad_resource) do
39
+ Class.new(Praxis::Mapper::Resource) do
40
+ property 'iamastring', dependencies: %i[foo]
41
+ end
42
+ end
43
+ it 'complains with the proper message right at definition time' do
44
+ expect { bad_resource }.to raise_error(
45
+ RuntimeError,
46
+ /Error defining property 'iamastring'.*Property names must be symbols, not strings/
47
+ )
48
+ end
49
+ end
50
+ context 'detect_invalid_properties' do
51
+ subject { resource.detect_invalid_properties }
52
+ let(:resource) do
53
+ Class.new(Praxis::Mapper::Resource) do
54
+ model SimpleModel
55
+ property :parent
56
+ property :other_model, dependencies: %i[foo bar]
57
+ def other_model
58
+ _something = foo && bar
59
+ record.other_model
60
+ end
61
+ end
62
+ end
63
+ # Validates that defining properties that are direct associations shouldn't be done unless
64
+ # there is an overriden method that takes over (and therefore might need the dependencies defined for what it needs)
65
+ it 'detects the invalid ones' do
66
+ # Parent is defined, but it is already an association (and no overriden method for it)
67
+ expect(subject).to match(/Bad definition of property 'parent'/)
68
+ # Other model is an association, but it is overriden, so that's a valid way to specify properties
69
+ expect(subject).to_not match(/Bad definition of property 'other_model'/)
34
70
  end
35
71
  end
36
72
  end