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

Sign up to get free protection for your applications and to get access to all the features.
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