praxis 2.0.pre.9 → 2.0.pre.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/CHANGELOG.md +11 -0
- data/TODO.md +1 -4
- data/bin/praxis +11 -13
- data/lib/praxis.rb +10 -3
- data/lib/praxis/action_definition.rb +15 -13
- data/lib/praxis/action_definition/headers_dsl_compiler.rb +0 -7
- data/lib/praxis/api_general_info.rb +1 -1
- data/lib/praxis/application.rb +6 -2
- data/lib/praxis/blueprint.rb +357 -0
- data/lib/praxis/bootloader.rb +9 -3
- data/lib/praxis/bootloader_stages/environment.rb +15 -13
- data/lib/praxis/collection.rb +1 -11
- data/lib/praxis/config_hash.rb +44 -0
- data/lib/praxis/docs/{openapi → open_api}/info_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/media_type_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/operation_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/parameter_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/paths_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/request_body_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/response_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/responses_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/schema_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/server_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/tag_object.rb +0 -0
- data/lib/praxis/docs/open_api_generator.rb +90 -5
- data/lib/praxis/endpoint_definition.rb +273 -0
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +1 -1
- data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +1 -1
- data/lib/praxis/extensions/field_expansion.rb +3 -36
- data/lib/praxis/extensions/pagination.rb +2 -2
- data/lib/praxis/extensions/pagination/ordering_params.rb +1 -1
- data/lib/praxis/extensions/pagination/pagination_params.rb +6 -4
- data/lib/praxis/field_expander.rb +90 -0
- data/lib/praxis/finalizable.rb +34 -0
- data/lib/praxis/mapper/selector_generator.rb +1 -1
- data/lib/praxis/media_type.rb +3 -68
- data/lib/praxis/plugin_concern.rb +1 -1
- data/lib/praxis/plugins/mapper_plugin.rb +7 -7
- data/lib/praxis/renderer.rb +88 -0
- data/lib/praxis/request.rb +1 -1
- data/lib/praxis/resource_definition.rb +2 -311
- data/lib/praxis/response_definition.rb +2 -10
- data/lib/praxis/response_template.rb +3 -3
- data/lib/praxis/router.rb +2 -2
- data/lib/praxis/routing_config.rb +1 -1
- data/lib/praxis/tasks/api_docs.rb +13 -63
- data/lib/praxis/tasks/routes.rb +1 -1
- data/lib/praxis/types/media_type_common.rb +1 -11
- data/lib/praxis/version.rb +1 -1
- data/praxis.gemspec +0 -1
- data/spec/functional_spec.rb +5 -9
- data/spec/praxis/action_definition_spec.rb +12 -20
- data/spec/praxis/blueprint_spec.rb +373 -0
- data/spec/praxis/bootloader_spec.rb +10 -2
- data/spec/praxis/collection_spec.rb +0 -13
- data/spec/praxis/config_hash_spec.rb +64 -0
- data/spec/praxis/{resource_definition_spec.rb → endpoint_definition_spec.rb} +37 -64
- data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +4 -6
- data/spec/praxis/extensions/field_expansion_spec.rb +5 -24
- data/spec/praxis/field_expander_spec.rb +149 -0
- data/spec/praxis/media_type_identifier_spec.rb +5 -4
- data/spec/praxis/media_type_spec.rb +4 -93
- data/spec/praxis/renderer_spec.rb +188 -0
- data/spec/praxis/response_definition_spec.rb +0 -31
- data/spec/praxis/response_spec.rb +1 -1
- data/spec/praxis/router_spec.rb +8 -8
- data/spec/praxis/routing_config_spec.rb +3 -3
- data/spec/spec_app/app/controllers/instances.rb +13 -7
- data/spec/spec_app/design/media_types/instance.rb +1 -19
- data/spec/spec_app/design/media_types/volume.rb +1 -1
- data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -14
- data/spec/spec_app/design/resources/instances.rb +5 -8
- data/spec/spec_app/design/resources/volume_snapshots.rb +1 -1
- data/spec/spec_app/design/resources/volumes.rb +1 -1
- data/spec/support/spec_authorization_plugin.rb +1 -1
- data/spec/support/spec_blueprints.rb +72 -0
- data/spec/support/{spec_resource_definitions.rb → spec_endpoint_definitions.rb} +2 -2
- data/spec/support/spec_media_types.rb +6 -26
- data/tasks/thor/app.rb +8 -34
- data/tasks/thor/example.rb +45 -285
- data/tasks/thor/templates/generator/empty_app/.gitignore +0 -1
- data/tasks/thor/templates/generator/empty_app/Gemfile +7 -23
- data/tasks/thor/templates/generator/empty_app/README.md +1 -1
- data/tasks/thor/templates/generator/empty_app/Rakefile +4 -13
- data/tasks/thor/templates/generator/empty_app/{design/response_templates → app/v1/resources}/.empty_directory +0 -0
- data/tasks/thor/templates/generator/empty_app/{design/response_templates → app/v1/resources}/.gitkeep +0 -0
- data/tasks/thor/templates/generator/empty_app/config/environment.rb +25 -17
- data/tasks/thor/templates/generator/empty_app/{design/v1/resources → config/initializers}/.empty_directory +0 -0
- data/tasks/thor/templates/generator/empty_app/{design/v1/resources → config/initializers}/.gitkeep +0 -0
- data/tasks/thor/templates/generator/empty_app/design/v1/endpoints/.empty_directory +0 -0
- data/tasks/thor/templates/generator/empty_app/design/v1/endpoints/.gitkeep +0 -0
- data/tasks/thor/templates/generator/empty_app/docs/.empty_directory +0 -0
- data/tasks/thor/templates/generator/empty_app/docs/.gitkeep +0 -0
- data/tasks/thor/templates/generator/empty_app/spec/spec_helper.rb +14 -9
- data/tasks/thor/templates/generator/example_app/.gitignore +1 -0
- data/tasks/thor/templates/generator/example_app/Gemfile +19 -0
- data/tasks/thor/templates/generator/example_app/Rakefile +54 -0
- data/tasks/thor/templates/generator/example_app/app/models/user.rb +6 -0
- data/tasks/thor/templates/generator/example_app/app/v1/controllers/users.rb +17 -0
- data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +46 -0
- data/tasks/thor/templates/generator/example_app/config.ru +31 -0
- data/tasks/thor/templates/generator/example_app/config/environment.rb +40 -0
- data/tasks/thor/templates/generator/example_app/db/migrate/20201010101010_create_users_table.rb +11 -0
- data/tasks/thor/templates/generator/example_app/design/api.rb +18 -0
- data/tasks/thor/templates/generator/example_app/design/v1/endpoints/users.rb +37 -0
- data/tasks/thor/templates/generator/example_app/design/v1/media_types/user.rb +26 -0
- data/tasks/thor/templates/generator/example_app/spec/helpers/database_helper.rb +18 -0
- data/tasks/thor/templates/generator/example_app/spec/spec_helper.rb +42 -0
- data/tasks/thor/templates/generator/example_app/spec/v1/controllers/users_spec.rb +37 -0
- metadata +49 -135
- data/lib/api_browser/.bowerrc +0 -3
- data/lib/api_browser/.editorconfig +0 -21
- data/lib/api_browser/Gruntfile.js +0 -581
- data/lib/api_browser/app/index.html +0 -59
- data/lib/api_browser/app/js/app.js +0 -48
- data/lib/api_browser/app/js/controllers/action.js +0 -47
- data/lib/api_browser/app/js/controllers/controller.js +0 -10
- data/lib/api_browser/app/js/controllers/menu.js +0 -93
- data/lib/api_browser/app/js/controllers/trait.js +0 -10
- data/lib/api_browser/app/js/controllers/type.js +0 -24
- data/lib/api_browser/app/js/directives/attribute_description.js +0 -56
- data/lib/api_browser/app/js/directives/attribute_table.js +0 -28
- data/lib/api_browser/app/js/directives/conditional_requirements.js +0 -13
- data/lib/api_browser/app/js/directives/fixed_if_fits.js +0 -38
- data/lib/api_browser/app/js/directives/highlight.js +0 -14
- data/lib/api_browser/app/js/directives/menu_item.js +0 -59
- data/lib/api_browser/app/js/directives/no_container.js +0 -8
- data/lib/api_browser/app/js/directives/readable_list.js +0 -87
- data/lib/api_browser/app/js/directives/request_examples.js +0 -31
- data/lib/api_browser/app/js/directives/type_placeholder.js +0 -30
- data/lib/api_browser/app/js/directives/url.js +0 -15
- data/lib/api_browser/app/js/factories/Configuration.js +0 -12
- data/lib/api_browser/app/js/factories/Documentation.js +0 -61
- data/lib/api_browser/app/js/factories/Example.js +0 -51
- data/lib/api_browser/app/js/factories/PageInfo.js +0 -9
- data/lib/api_browser/app/js/factories/normalize_attributes.js +0 -20
- data/lib/api_browser/app/js/factories/prepare_template.js +0 -15
- data/lib/api_browser/app/js/factories/template_for.js +0 -128
- data/lib/api_browser/app/js/filters/attribute_name.js +0 -10
- data/lib/api_browser/app/js/filters/friendly_json.js +0 -5
- data/lib/api_browser/app/js/filters/has_requirement.js +0 -14
- data/lib/api_browser/app/js/filters/header_info.js +0 -9
- data/lib/api_browser/app/js/filters/is_empty.js +0 -8
- data/lib/api_browser/app/js/filters/markdown.js +0 -6
- data/lib/api_browser/app/js/filters/resource_name.js +0 -5
- data/lib/api_browser/app/js/filters/tag_requirement.js +0 -13
- data/lib/api_browser/app/sass/modules/_body.scss +0 -40
- data/lib/api_browser/app/sass/modules/_cloke.scss +0 -8
- data/lib/api_browser/app/sass/modules/_header.scss +0 -10
- data/lib/api_browser/app/sass/modules/_nav.scss +0 -7
- data/lib/api_browser/app/sass/modules/_sidebar.scss +0 -134
- data/lib/api_browser/app/sass/modules/_switch.scss +0 -55
- data/lib/api_browser/app/sass/modules/_table.scss +0 -13
- data/lib/api_browser/app/sass/praxis.scss +0 -70
- data/lib/api_browser/app/sass/variables/_bootstrap-variables.scss +0 -774
- data/lib/api_browser/app/views/action.html +0 -97
- data/lib/api_browser/app/views/builtin/field-selector.html +0 -24
- data/lib/api_browser/app/views/controller.html +0 -55
- data/lib/api_browser/app/views/directives/attribute_description.html +0 -2
- data/lib/api_browser/app/views/directives/attribute_description/default.html +0 -2
- data/lib/api_browser/app/views/directives/attribute_description/example.html +0 -13
- data/lib/api_browser/app/views/directives/attribute_description/headers.html +0 -8
- data/lib/api_browser/app/views/directives/attribute_description/member_options.html +0 -4
- data/lib/api_browser/app/views/directives/attribute_description/values.html +0 -14
- data/lib/api_browser/app/views/directives/attribute_table.html +0 -17
- data/lib/api_browser/app/views/directives/menu_item.html +0 -8
- data/lib/api_browser/app/views/directives/url.html +0 -3
- data/lib/api_browser/app/views/examples/general.html +0 -26
- data/lib/api_browser/app/views/home.html +0 -5
- data/lib/api_browser/app/views/layout.html +0 -8
- data/lib/api_browser/app/views/menu.html +0 -42
- data/lib/api_browser/app/views/navbar.html +0 -9
- data/lib/api_browser/app/views/trait.html +0 -13
- data/lib/api_browser/app/views/type.html +0 -6
- data/lib/api_browser/app/views/type/details.html +0 -33
- data/lib/api_browser/app/views/types/embedded/array.html +0 -2
- data/lib/api_browser/app/views/types/embedded/default.html +0 -12
- data/lib/api_browser/app/views/types/embedded/field-selector.html +0 -13
- data/lib/api_browser/app/views/types/embedded/links.html +0 -11
- data/lib/api_browser/app/views/types/embedded/requirements.html +0 -6
- data/lib/api_browser/app/views/types/embedded/single_req.html +0 -9
- data/lib/api_browser/app/views/types/embedded/struct.html +0 -14
- data/lib/api_browser/app/views/types/label/link.html +0 -1
- data/lib/api_browser/app/views/types/label/primitive.html +0 -1
- data/lib/api_browser/app/views/types/label/primitive_collection.html +0 -1
- data/lib/api_browser/app/views/types/label/type.html +0 -1
- data/lib/api_browser/app/views/types/label/type_collection.html +0 -1
- data/lib/api_browser/app/views/types/main/array.html +0 -22
- data/lib/api_browser/app/views/types/main/default.html +0 -23
- data/lib/api_browser/app/views/types/main/hash.html +0 -23
- data/lib/api_browser/app/views/types/standalone/array.html +0 -3
- data/lib/api_browser/app/views/types/standalone/default.html +0 -18
- data/lib/api_browser/app/views/types/standalone/struct.html +0 -2
- data/lib/api_browser/bower_template.json +0 -41
- data/lib/api_browser/package-lock.json +0 -7110
- data/lib/api_browser/package.json +0 -43
- data/lib/praxis/docs/generator.rb +0 -243
- data/lib/praxis/docs/link_builder.rb +0 -30
- data/lib/praxis/links.rb +0 -135
- data/lib/praxis/types/multipart.rb +0 -109
- data/spec/api_browser/directives/type_placeholder_spec.js +0 -134
- data/spec/api_browser/factories/configuration_spec.js +0 -32
- data/spec/api_browser/factories/documentation_spec.js +0 -100
- data/spec/api_browser/factories/normalize_attributes_spec.js +0 -92
- data/spec/api_browser/factories/template_for_spec.js +0 -67
- data/spec/api_browser/filters/attribute_name_spec.js +0 -23
- data/spec/praxis/types/multipart_spec.rb +0 -112
- data/tasks/thor/templates/generator/empty_app/.rspec +0 -1
- data/tasks/thor/templates/generator/empty_app/Guardfile +0 -3
- data/tasks/thor/templates/generator/empty_app/config/rainbows.rb +0 -57
- data/tasks/thor/templates/generator/empty_app/docs/app.js +0 -1
- data/tasks/thor/templates/generator/empty_app/docs/styles.scss +0 -3
@@ -42,10 +42,11 @@ describe Praxis::MediaTypeIdentifier do
|
|
42
42
|
expect(described_class.new(unquoted_example).parameters['encoding']).to eq('martian')
|
43
43
|
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
# TODO: Maybe that's something we want to support in the future? seems a big hammer for such a thing
|
46
|
+
# it 'handles quoted semicolons in values' do
|
47
|
+
# pending("need to stop using a regexp to do a context-free parser's job")
|
48
|
+
# expect(described_class.new(tricky_example).parameters['sauce']).to eq('yes; absolutely')
|
49
|
+
# end
|
49
50
|
|
50
51
|
|
51
52
|
end
|
@@ -22,7 +22,6 @@ describe Praxis::MediaType do
|
|
22
22
|
|
23
23
|
subject(:address) { Address.new(resource) }
|
24
24
|
|
25
|
-
|
26
25
|
context 'attributes' do
|
27
26
|
its(:id) { should eq(1) }
|
28
27
|
its(:name) { should eq('Home') }
|
@@ -30,112 +29,24 @@ describe Praxis::MediaType do
|
|
30
29
|
end
|
31
30
|
|
32
31
|
|
33
|
-
context 'loading' do
|
34
|
-
it do
|
35
|
-
Person.load({id: 1})
|
36
|
-
Person.load(owner_resource)
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
|
42
32
|
context 'accessor methods' do
|
43
33
|
subject(:address_klass) { address.class }
|
44
34
|
|
45
35
|
context '#identifier' do
|
46
|
-
|
47
|
-
|
48
|
-
pending('interface-breaking change') if Praxis::VERSION =~ /^0/
|
49
|
-
expect(subject.identifier).to be_kind_of(Praxis::MediaTypeIdentifier)
|
50
|
-
end
|
36
|
+
it 'should be a kind of Praxis::MediaTypeIdentifier' do
|
37
|
+
expect(subject.identifier).to be_kind_of(Praxis::MediaTypeIdentifier)
|
51
38
|
end
|
52
39
|
end
|
53
40
|
|
54
41
|
its(:description) { should be_kind_of(String) }
|
55
|
-
|
56
42
|
end
|
57
43
|
|
58
44
|
context "rendering" do
|
59
|
-
subject(:output) { address.render
|
45
|
+
subject(:output) { address.render }
|
60
46
|
|
61
47
|
its([:id]) { should eq(address.id) }
|
62
48
|
its([:name]) { should eq(address.name) }
|
63
|
-
its([:owner]) { should eq(Person.dump(owner_resource
|
49
|
+
its([:owner]) { should eq(Person.dump(owner_resource)) }
|
64
50
|
its([:fields]) { should eq(address.fields.dump ) }
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
context 'describing' do
|
69
|
-
|
70
|
-
subject(:described){ Address.describe }
|
71
|
-
|
72
|
-
its(:keys) { should match_array( [:attributes, :description, :display_name, :family, :id, :identifier, :key, :name, :views, :requirements] ) }
|
73
|
-
its([:attributes]) { should be_kind_of(::Hash) }
|
74
|
-
its([:description]) { should be_kind_of(::String) }
|
75
|
-
its([:display_name]) { should be_kind_of(::String) }
|
76
|
-
its([:family]) { should be_kind_of(::String) }
|
77
|
-
its([:id]) { should be_kind_of(::String) }
|
78
|
-
its([:name]) { should be_kind_of(::String) }
|
79
|
-
its([:identifier]) { should be_kind_of(::String) }
|
80
|
-
its([:key]) { should be_kind_of(::Hash) }
|
81
|
-
its([:views]) { should be_kind_of(::Hash) }
|
82
|
-
|
83
|
-
its([:description]) { should eq(Address.description) }
|
84
|
-
its([:display_name]) { should eq(Address.display_name) }
|
85
|
-
its([:family]) { should eq(Address.family) }
|
86
|
-
its([:id]) { should eq(Address.id) }
|
87
|
-
its([:name]) { should eq(Address.name) }
|
88
|
-
its([:identifier]) { should eq(Address.identifier.to_s) }
|
89
|
-
it 'should include the defined views' do
|
90
|
-
expect( subject[:views].keys ).to match_array([:default, :master])
|
91
|
-
end
|
92
|
-
it 'should include the defined attributes' do
|
93
|
-
expect( subject[:attributes].keys ).to match_array([:id, :name, :owner, :custodian, :residents, :residents_summary, :fields])
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
context 'using blueprint caching' do
|
98
|
-
it 'has specs'
|
99
|
-
end
|
100
|
-
|
101
|
-
context Praxis::MediaType::FieldResolver do
|
102
|
-
let(:expander) { Praxis::FieldExpander }
|
103
|
-
let(:user_view) { User.views[:default] }
|
104
|
-
|
105
|
-
let(:fields) { expander.expand(user_view) }
|
106
|
-
|
107
|
-
let(:field_resolver) { Praxis::MediaType::FieldResolver }
|
108
|
-
|
109
|
-
subject(:output) { field_resolver.resolve(User,fields) }
|
110
|
-
|
111
|
-
context 'resolving collections' do
|
112
|
-
let(:fields) { {:id=>true, :posts=>[{:href=>true}]}}
|
113
|
-
it 'strips arrays from the incoming fields' do
|
114
|
-
expect(output).to eq(id: true, posts: {href: true})
|
115
|
-
end
|
116
|
-
|
117
|
-
it 'supports multi-dimensional collections' do
|
118
|
-
fields = {
|
119
|
-
id: true,
|
120
|
-
post_matrix:[[{title: true, href: true}]]
|
121
|
-
}
|
122
|
-
output = field_resolver.resolve(User,fields)
|
123
|
-
expect(output).to eq(id: true, post_matrix:{href: true, title: true})
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'supports nesting structs and arrays collections' do
|
127
|
-
fields = {
|
128
|
-
id: true,
|
129
|
-
daily_posts: [
|
130
|
-
{day: true, posts: [{id: true}]}
|
131
|
-
]
|
132
|
-
}
|
133
|
-
output = field_resolver.resolve(User,fields)
|
134
|
-
expect(output).to eq(id: true, daily_posts:{day: true, posts: {id:true}})
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
51
|
end
|
139
|
-
|
140
|
-
|
141
52
|
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative '../spec_helper'
|
3
|
+
|
4
|
+
describe Praxis::Renderer do
|
5
|
+
let(:address) { AddressBlueprint.example }
|
6
|
+
let(:prior_addresses) { Array.new(2) { AddressBlueprint.example } }
|
7
|
+
let(:alias_one) { FullName.example }
|
8
|
+
let(:alias_two) { FullName.example }
|
9
|
+
let(:aliases) { [alias_one, alias_two] }
|
10
|
+
let(:metadata_hash) { { something: 'here' } }
|
11
|
+
let(:metadata) { Attributor::Hash.load(metadata_hash) }
|
12
|
+
|
13
|
+
let(:person) do
|
14
|
+
PersonBlueprint.example(
|
15
|
+
address: address,
|
16
|
+
email: nil,
|
17
|
+
prior_addresses: prior_addresses,
|
18
|
+
alive: false,
|
19
|
+
work_address: nil,
|
20
|
+
aliases: aliases,
|
21
|
+
metadata: metadata
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:fields) do
|
26
|
+
{
|
27
|
+
name: true,
|
28
|
+
email: true,
|
29
|
+
full_name: { first: true, last: true },
|
30
|
+
address: {
|
31
|
+
state: true,
|
32
|
+
street: true,
|
33
|
+
resident: { name: true }
|
34
|
+
},
|
35
|
+
prior_addresses: { name: true },
|
36
|
+
work_address: true,
|
37
|
+
alive: true,
|
38
|
+
metadata: true,
|
39
|
+
aliases: true
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:renderer) { Praxis::Renderer.new }
|
44
|
+
|
45
|
+
subject(:output) { renderer.render(person, fields) }
|
46
|
+
|
47
|
+
it 'renders existing attributes' do
|
48
|
+
expect(output.keys).to match_array([:name, :full_name, :alive, :address, :prior_addresses, :metadata, :aliases])
|
49
|
+
|
50
|
+
expect(output[:name]).to eq person.name
|
51
|
+
expect(output[:full_name]).to eq(first: person.full_name.first, last: person.full_name.last)
|
52
|
+
expect(output[:alive]).to be false
|
53
|
+
|
54
|
+
expect(output[:address]).to eq(state: person.address.state,
|
55
|
+
street: person.address.street,
|
56
|
+
resident: { name: person.address.resident.name })
|
57
|
+
|
58
|
+
expected_prior_addresses = prior_addresses.collect { |addr| { name: addr.name } }
|
59
|
+
expect(output[:prior_addresses]).to match_array(expected_prior_addresses)
|
60
|
+
|
61
|
+
expect(output[:aliases]).to match_array(aliases.collect(&:dump))
|
62
|
+
expect(output[:metadata]).to eq(metadata.dump)
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'calls dump for non-Blueprint, but still Dumpable instances' do
|
66
|
+
it 'when rendering them in full as array members' do
|
67
|
+
expect(alias_one).to receive(:dump).and_call_original
|
68
|
+
expect(output[:aliases].first).to eq(first: alias_one.first, last: alias_one.last)
|
69
|
+
end
|
70
|
+
it 'when rendering them in full as leaf object' do
|
71
|
+
expect(metadata).to receive(:dump).and_call_original
|
72
|
+
expect(output[:metadata]).to eq(metadata_hash)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'does not render attributes with nil values' do
|
77
|
+
expect(output).to_not have_key(:email)
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'with include_nil: true' do
|
81
|
+
let(:renderer) { Praxis::Renderer.new(include_nil: true) }
|
82
|
+
let(:address) { nil }
|
83
|
+
|
84
|
+
it 'renders attributes with nil values' do
|
85
|
+
expect(output).to have_key :email
|
86
|
+
expect(output[:email]).to be_nil
|
87
|
+
|
88
|
+
expect(output).to have_key :work_address
|
89
|
+
expect(output[:work_address]).to be_nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'renders nil directly for nil subobjects' do
|
93
|
+
expect(output).to have_key :address
|
94
|
+
expect(output[:address]).to be_nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'rendering a two-dimmensional collection' do
|
99
|
+
let(:names) { Array.new(9) { |i| AddressBlueprint.example(i.to_s, name: i.to_s) } }
|
100
|
+
let(:matrix_type) do
|
101
|
+
Attributor::Collection.of(Attributor::Collection.of(AddressBlueprint))
|
102
|
+
end
|
103
|
+
|
104
|
+
let(:matrix) { matrix_type.load(names.each_slice(3).collect { |slice| slice }) }
|
105
|
+
|
106
|
+
let(:fields) { { name: true } }
|
107
|
+
|
108
|
+
it 'renders with render_collection and per-element field spec' do
|
109
|
+
rendered = renderer.render(matrix, fields)
|
110
|
+
expect(rendered.flatten.collect { |r| r[:name] }).to eq((0..8).collect(&:to_s))
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'renders with render and proper field spec' do
|
114
|
+
rendered = renderer.render(matrix, fields)
|
115
|
+
expect(rendered.flatten.collect { |r| r[:name] }).to eq((0..8).collect(&:to_s))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'rendering stuff that breaks badly' do
|
120
|
+
it 'does not break badly' do
|
121
|
+
expect{renderer.render(person, {tags: true})}.to_not raise_error
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'caching rendered objects' do
|
126
|
+
let(:fields) { {full_name: true} }
|
127
|
+
it 'caches and returns identical results for the same field objects' do
|
128
|
+
expect(person).to receive(:full_name).once.and_call_original
|
129
|
+
|
130
|
+
render_1 = renderer.render(person, fields)
|
131
|
+
render_2 = renderer.render(person, fields)
|
132
|
+
expect(render_1).to be(render_2)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'rendering hashes' do
|
137
|
+
let(:fields) do
|
138
|
+
{
|
139
|
+
id: true,
|
140
|
+
hash: true
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
let(:data) { { id: 10, hash: { foo: 'bar' } } }
|
145
|
+
let(:object) { SimpleHash.load(data) }
|
146
|
+
let(:renderer) { Praxis::Renderer.new }
|
147
|
+
|
148
|
+
subject(:output) { renderer.render(object, fields) }
|
149
|
+
|
150
|
+
its([:id]) { should eq data[:id] }
|
151
|
+
its([:hash]) { should eq data[:hash] }
|
152
|
+
its([:hash]) { should be_kind_of(Hash) }
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'rendering collections of hashes' do
|
156
|
+
let(:fields) do
|
157
|
+
{
|
158
|
+
id: true,
|
159
|
+
hash_collection: true
|
160
|
+
}
|
161
|
+
end
|
162
|
+
|
163
|
+
let(:data) { { id: 10, hash_collection: [{ foo: 'bar' }] } }
|
164
|
+
let(:object) { SimpleHashCollection.load(data) }
|
165
|
+
let(:renderer) { Praxis::Renderer.new }
|
166
|
+
|
167
|
+
subject(:output) { renderer.render(object, fields) }
|
168
|
+
|
169
|
+
its([:id]) { should eq data[:id] }
|
170
|
+
its([:hash_collection]) { should eq data[:hash_collection] }
|
171
|
+
its([:hash_collection]) { should be_kind_of(Array) }
|
172
|
+
|
173
|
+
it 'renders the hashes' do
|
174
|
+
expect(output[:hash_collection].first).to be_kind_of(Hash)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'rendering a Blueprint with fields true' do
|
179
|
+
let(:fields) do
|
180
|
+
{
|
181
|
+
name: true,
|
182
|
+
address: true
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
its([:address]) { should eq person.address.dump }
|
187
|
+
end
|
188
|
+
end
|
@@ -443,37 +443,6 @@ describe Praxis::ResponseDefinition do
|
|
443
443
|
end
|
444
444
|
end
|
445
445
|
end
|
446
|
-
|
447
|
-
describe '#validate_parts!' do
|
448
|
-
context 'with legacy multipart response' do
|
449
|
-
subject(:response) { Praxis::Responses::Ok.new(status: response_status, headers: response_headers) }
|
450
|
-
|
451
|
-
let(:part) { Praxis::MultipartPart.new('done', {'Status' => 200, 'Content-Type' => 'application/special'},**{}) }
|
452
|
-
|
453
|
-
before do
|
454
|
-
response_definition.parts like: :ok, media_type: 'application/special'
|
455
|
-
response.add_part(part)
|
456
|
-
end
|
457
|
-
|
458
|
-
it 'validates each part' do
|
459
|
-
response_definition.parts
|
460
|
-
expect {
|
461
|
-
response_definition.validate_parts!(response)
|
462
|
-
}.to_not raise_error
|
463
|
-
end
|
464
|
-
|
465
|
-
context 'with invalid part' do
|
466
|
-
let(:part) { Praxis::MultipartPart.new('done', {'Status' => 200, "Location" => "somewhere"}) }
|
467
|
-
|
468
|
-
it 'validates' do
|
469
|
-
expect {
|
470
|
-
response_definition.validate_parts!(response)
|
471
|
-
}.to raise_error(Praxis::Exceptions::Validation)
|
472
|
-
end
|
473
|
-
|
474
|
-
end
|
475
|
-
end
|
476
|
-
end
|
477
446
|
end
|
478
447
|
|
479
448
|
end
|
data/spec/praxis/router_spec.rb
CHANGED
@@ -2,8 +2,8 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Praxis::Router do
|
4
4
|
describe Praxis::Router::VersionMatcher do
|
5
|
-
let(:
|
6
|
-
let(:action){ double("action",
|
5
|
+
let(:endpoint_definition){ double("endpoint_definition", version_options: { using: [:header, :params] }) }
|
6
|
+
let(:action){ double("action", endpoint_definition: endpoint_definition ) }
|
7
7
|
let(:target){ double("target", action: action ) }
|
8
8
|
let(:args){ {version: "1.0"} }
|
9
9
|
subject(:matcher){ Praxis::Router::VersionMatcher.new(target,**args) }
|
@@ -80,8 +80,8 @@ describe Praxis::Router do
|
|
80
80
|
let(:target){ double('target') }
|
81
81
|
let(:verb_router){ double('verb_router') }
|
82
82
|
|
83
|
-
let(:
|
84
|
-
let(:action) { double('action',
|
83
|
+
let(:endpoint_definition) { double('endpoint_definition', version_options: {using: [:header, :params]} ) }
|
84
|
+
let(:action) { double('action', endpoint_definition: endpoint_definition) }
|
85
85
|
let(:target) { double('target', action: action) }
|
86
86
|
|
87
87
|
|
@@ -111,8 +111,8 @@ describe Praxis::Router do
|
|
111
111
|
let(:post_target_router){ double("POST target", call: router_response_for_post) }
|
112
112
|
let(:any_target_router){ double("ANY target", call: router_response_for_wildcard) }
|
113
113
|
|
114
|
-
let(:
|
115
|
-
let(:action) { double('action',
|
114
|
+
let(:endpoint_definition) { double('endpoint_definition', version_options: {using: [:header, :params]} ) }
|
115
|
+
let(:action) { double('action', endpoint_definition: endpoint_definition) }
|
116
116
|
let(:target) { double('target', action: action) }
|
117
117
|
|
118
118
|
before do
|
@@ -142,8 +142,8 @@ describe Praxis::Router do
|
|
142
142
|
end
|
143
143
|
|
144
144
|
context 'for routes with wildcards (a POST and a * route)' do
|
145
|
-
let(:
|
146
|
-
let(:action) { double('action',
|
145
|
+
let(:endpoint_definition) { double('endpoint_definition', version_options: {using: [:header, :params]} ) }
|
146
|
+
let(:action) { double('action', endpoint_definition: endpoint_definition) }
|
147
147
|
let(:target) { double('target', action: action) }
|
148
148
|
|
149
149
|
before do
|
@@ -2,16 +2,16 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Praxis::RoutingConfig do
|
4
4
|
|
5
|
-
let(:
|
5
|
+
let(:endpoint_definition) do
|
6
6
|
Class.new do
|
7
|
-
include Praxis::
|
7
|
+
include Praxis::EndpointDefinition
|
8
8
|
def self.name; 'MyResource'; end
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
12
|
let(:routing_block) { Proc.new{} }
|
13
13
|
let(:base_path){ '' }
|
14
|
-
let(:default_route_prefix) { "/" +
|
14
|
+
let(:default_route_prefix) { "/" + endpoint_definition.name.split("::").last.underscore }
|
15
15
|
|
16
16
|
subject(:routing_config){ Praxis::RoutingConfig.new(base: base_path, &routing_block) }
|
17
17
|
|
@@ -68,7 +68,7 @@ class Instances < BaseClass
|
|
68
68
|
|
69
69
|
part_body = {
|
70
70
|
key: instance_id,
|
71
|
-
value: instance.render(
|
71
|
+
value: instance.render(fields: {id: true, name: true})
|
72
72
|
}
|
73
73
|
|
74
74
|
headers = {
|
@@ -78,7 +78,6 @@ class Instances < BaseClass
|
|
78
78
|
}
|
79
79
|
|
80
80
|
part = Praxis::MultipartPart.new(part_body, headers, name: instance_id)
|
81
|
-
|
82
81
|
response.body.push(part)
|
83
82
|
end
|
84
83
|
|
@@ -89,13 +88,20 @@ class Instances < BaseClass
|
|
89
88
|
def attach_file(id:, cloud_id:)
|
90
89
|
response.headers['Content-Type'] = 'application/json'
|
91
90
|
|
92
|
-
destination_path = request.payload
|
93
|
-
file = request.payload
|
94
|
-
|
91
|
+
destination_path = request.payload.part('destination_path').payload
|
92
|
+
file = request.payload.part('file')
|
93
|
+
file.payload.rewind # Ensure contents is at the beggining.
|
94
|
+
extra_part_names = request.payload.map(&:name) - ['destination_path', 'file']
|
95
|
+
extra_parts = extra_part_names.each_with_object({}) do |pname, hash|
|
96
|
+
hash[pname] = request.payload.part(pname).body
|
97
|
+
end
|
95
98
|
result = {
|
96
99
|
destination_path: destination_path,
|
97
|
-
|
98
|
-
|
100
|
+
name: file.name,
|
101
|
+
filename: file.filename,
|
102
|
+
type: file.content_type.to_s,
|
103
|
+
contents: file.payload.read,
|
104
|
+
options: extra_parts
|
99
105
|
}
|
100
106
|
|
101
107
|
response.body = JSON.pretty_generate(result)
|