praxis 0.13.0 → 0.14.0
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/.ruby-version +1 -1
- data/.travis.yml +15 -2
- data/CHANGELOG.md +54 -1
- data/bin/praxis +49 -2
- data/lib/api_browser/Gruntfile.js +247 -90
- data/lib/api_browser/app/bower_components/angular-mocks/.bower.json +19 -0
- data/lib/api_browser/app/bower_components/angular-mocks/README.md +57 -0
- data/lib/api_browser/app/bower_components/angular-mocks/angular-mocks.js +2193 -0
- data/lib/api_browser/app/bower_components/angular-mocks/bower.json +9 -0
- data/lib/api_browser/app/bower_components/angular-mocks/package.json +27 -0
- data/lib/api_browser/app/bower_components/angular/.bower.json +6 -5
- data/lib/api_browser/app/bower_components/angular/README.md +23 -4
- data/lib/api_browser/app/bower_components/angular/angular-csp.css +6 -0
- data/lib/api_browser/app/bower_components/angular/angular.js +2287 -1597
- data/lib/api_browser/app/bower_components/angular/angular.min.js +212 -205
- data/lib/api_browser/app/bower_components/angular/angular.min.js.gzip +0 -0
- data/lib/api_browser/app/bower_components/angular/angular.min.js.map +3 -3
- data/lib/api_browser/app/bower_components/angular/bower.json +2 -1
- data/lib/api_browser/app/bower_components/angular/package.json +25 -0
- data/lib/api_browser/app/bower_components/showdown/.bower.json +39 -0
- data/lib/api_browser/app/bower_components/showdown/.jshintignore +2 -0
- data/lib/api_browser/app/bower_components/showdown/.travis.yml +8 -0
- data/lib/api_browser/app/bower_components/showdown/Gruntfile.js +100 -0
- data/lib/api_browser/app/bower_components/showdown/README.md +317 -0
- data/lib/api_browser/app/bower_components/showdown/bower.json +26 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js +1606 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js.map +1 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.min.js +2 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js +2 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js.map +1 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js +2 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js.map +1 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js +2 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js.map +1 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js +2 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js.map +1 -0
- data/lib/api_browser/app/bower_components/showdown/license.txt +34 -0
- data/lib/api_browser/app/bower_components/showdown/package.json +47 -0
- data/lib/api_browser/app/bower_components/showdown/src/extensions/github.js +25 -0
- data/lib/api_browser/app/bower_components/showdown/src/extensions/prettify.js +29 -0
- data/lib/api_browser/app/bower_components/showdown/src/extensions/table.js +106 -0
- data/lib/api_browser/app/bower_components/showdown/src/extensions/twitter.js +42 -0
- data/lib/api_browser/app/bower_components/showdown/src/ng-showdown.js +150 -0
- data/lib/api_browser/app/bower_components/showdown/src/showdown.js +1454 -0
- data/lib/api_browser/app/index.html +6 -4
- data/lib/api_browser/app/js/app.js +1 -2
- data/lib/api_browser/app/js/controllers/action.js +4 -4
- data/lib/api_browser/app/js/controllers/controller.js +1 -1
- data/lib/api_browser/app/js/controllers/menu.js +5 -3
- data/lib/api_browser/app/js/controllers/type.js +5 -5
- data/lib/api_browser/app/js/directives/attribute_description.js +5 -5
- data/lib/api_browser/app/js/directives/attribute_table.js +1 -1
- data/lib/api_browser/app/js/directives/attribute_table_row.js +2 -2
- data/lib/api_browser/app/js/directives/no_container.js +1 -1
- data/lib/api_browser/app/js/directives/request_body.js +5 -5
- data/lib/api_browser/app/js/directives/request_headers.js +3 -6
- data/lib/api_browser/app/js/directives/request_parameters.js +3 -6
- data/lib/api_browser/app/js/directives/type_label.js +4 -5
- data/lib/api_browser/app/js/factories/Documentation.js +4 -4
- data/lib/api_browser/app/js/factories/PayloadTemplates.js +2 -2
- data/lib/api_browser/app/js/factories/TypeTemplates.js +3 -3
- data/lib/api_browser/app/js/filters/markdown.js +6 -0
- data/lib/api_browser/app/js/filters/resource_name.js +2 -2
- data/lib/api_browser/app/sass/modules/_header.scss +2 -7
- data/lib/api_browser/app/sass/{main.scss → praxis.scss} +0 -0
- data/lib/api_browser/app/sass/variables/_bootstrap-variables.scss +370 -367
- data/lib/api_browser/app/views/action.html +2 -2
- data/lib/api_browser/app/views/controller.html +2 -2
- data/lib/api_browser/app/views/directives/attribute_description.html +1 -1
- data/lib/api_browser/app/views/layout.html +2 -11
- data/lib/api_browser/app/views/navbar.html +9 -0
- data/lib/api_browser/app/views/resource/_actions.html +1 -1
- data/lib/api_browser/app/views/type.html +2 -2
- data/lib/api_browser/app/views/type/_details.html +2 -1
- data/lib/api_browser/bower.json +5 -0
- data/lib/api_browser/package.json +18 -7
- data/lib/praxis.rb +8 -3
- data/lib/praxis/action_definition.rb +28 -6
- data/lib/praxis/api_definition.rb +30 -2
- data/lib/praxis/api_general_info.rb +36 -0
- data/lib/praxis/bootloader.rb +1 -0
- data/lib/praxis/collection.rb +34 -0
- data/lib/praxis/controller.rb +7 -0
- data/lib/praxis/dispatcher.rb +3 -0
- data/lib/praxis/links.rb +2 -8
- data/lib/praxis/media_type.rb +6 -24
- data/lib/praxis/media_type_collection.rb +6 -2
- data/lib/praxis/plugin_concern.rb +2 -1
- data/lib/praxis/request.rb +24 -15
- data/lib/praxis/request_stages/request_stage.rb +19 -4
- data/lib/praxis/request_stages/validate_params_and_headers.rb +1 -1
- data/lib/praxis/request_stages/validate_payload.rb +1 -1
- data/lib/praxis/resource_definition.rb +45 -10
- data/lib/praxis/response_definition.rb +46 -27
- data/lib/praxis/restful_doc_generator.rb +94 -7
- data/lib/praxis/simple_media_type.rb +2 -9
- data/lib/praxis/stage.rb +1 -4
- data/lib/praxis/tasks/api_docs.rb +51 -19
- data/lib/praxis/tasks/routes.rb +19 -15
- data/lib/praxis/types/media_type_common.rb +31 -0
- data/lib/praxis/types/multipart.rb +4 -4
- data/lib/praxis/version.rb +1 -1
- data/praxis.gemspec +2 -2
- data/spec/api_browser/factories/documentation_spec.js +50 -0
- data/spec/api_browser/filters/attribute_name_spec.js +23 -0
- data/spec/functional_spec.rb +62 -10
- data/spec/praxis/action_definition_spec.rb +12 -4
- data/spec/praxis/api_definition_spec.rb +159 -0
- data/spec/praxis/api_general_info_spec.rb +36 -0
- data/spec/praxis/bootloader_spec.rb +10 -1
- data/spec/praxis/media_type_collection_spec.rb +46 -53
- data/spec/praxis/media_type_spec.rb +6 -6
- data/spec/praxis/request_stage_spec.rb +7 -2
- data/spec/praxis/request_stages_validate_spec.rb +12 -7
- data/spec/praxis/resource_definition_spec.rb +62 -0
- data/spec/praxis/response_definition_spec.rb +26 -16
- data/spec/praxis/stage_spec.rb +4 -8
- data/spec/praxis/types/collection_spec.rb +144 -0
- data/spec/spec_app/app/controllers/instances.rb +8 -2
- data/spec/spec_app/design/api.rb +11 -0
- data/spec/spec_app/design/media_types/instance.rb +12 -0
- data/spec/spec_app/design/media_types/volume.rb +9 -2
- data/spec/spec_app/design/media_types/volume_snapshot.rb +9 -6
- data/spec/spec_app/design/resources/instances.rb +25 -10
- data/spec/support/spec_media_types.rb +1 -1
- data/spec/support/spec_resource_definitions.rb +2 -0
- data/tasks/thor/app.rb +15 -10
- data/tasks/thor/example.rb +115 -115
- data/tasks/thor/templates/generator/empty_app/.gitignore +2 -0
- data/tasks/thor/templates/generator/empty_app/docs/app.js +1 -0
- data/tasks/thor/templates/generator/empty_app/docs/styles.scss +3 -0
- metadata +50 -9
- data/lib/api_browser/app/css/main.css +0 -4511
- data/lib/praxis/types/collection.rb +0 -17
@@ -33,7 +33,7 @@ describe Praxis::MediaType do
|
|
33
33
|
its(:description) { should be_kind_of(String) }
|
34
34
|
|
35
35
|
context 'links' do
|
36
|
-
context 'with
|
36
|
+
context 'with a custom links attribute' do
|
37
37
|
subject(:person) { Person.new(owner_resource) }
|
38
38
|
|
39
39
|
its(:links) { should be_kind_of(Array) }
|
@@ -62,15 +62,15 @@ describe Praxis::MediaType do
|
|
62
62
|
its([:owner]) { should eq(Person.dump(owner_resource, view: :link)) }
|
63
63
|
its([:super]) { should eq(Person.dump(manager_resource, view: :link)) }
|
64
64
|
|
65
|
-
context 'for a collection' do
|
65
|
+
context 'for a collection summary' do
|
66
66
|
let(:volume) { Volume.example }
|
67
|
-
let(:
|
67
|
+
let(:snapshots_summary) { volume.snapshots_summary }
|
68
68
|
let(:output) { volume.render(:default) }
|
69
69
|
subject { links[:snapshots] }
|
70
70
|
|
71
|
-
its([:name]) { should eq(
|
72
|
-
its([:size]) { should eq(
|
73
|
-
its([:href]) { should eq(
|
71
|
+
its([:name]) { should eq(snapshots_summary.name) }
|
72
|
+
its([:size]) { should eq(snapshots_summary.size) }
|
73
|
+
its([:href]) { should eq(snapshots_summary.href) }
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
@@ -50,6 +50,13 @@ describe Praxis::RequestStages::RequestStage do
|
|
50
50
|
context 'execute_with_around' do
|
51
51
|
end
|
52
52
|
|
53
|
+
context '#setup!' do
|
54
|
+
it 'sets up the deferred callbacks' do
|
55
|
+
expect(stage).to receive(:setup_deferred_callbacks!).once
|
56
|
+
stage.setup!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
53
60
|
context "#execute" do
|
54
61
|
before do
|
55
62
|
stage.stages.push(substage_1, substage_2, substage_3)
|
@@ -107,8 +114,6 @@ describe Praxis::RequestStages::RequestStage do
|
|
107
114
|
end
|
108
115
|
|
109
116
|
it "sets up and executes callbacks" do
|
110
|
-
expect(stage).to receive(:setup!)
|
111
|
-
expect(stage).to receive(:setup_deferred_callbacks!)
|
112
117
|
expect(stage).to receive(:execute)
|
113
118
|
expect(stage).to receive(:execute_callbacks).once.with(before_callbacks)
|
114
119
|
expect(stage).to receive(:execute_callbacks).once.with(after_callbacks)
|
@@ -9,14 +9,19 @@ describe Praxis::RequestStages::Validate do
|
|
9
9
|
|
10
10
|
let(:action) { controller.definition.actions[:show] }
|
11
11
|
|
12
|
+
let(:env) do
|
13
|
+
e = Rack::MockRequest.env_for('/instances/1?cloud_id=1&api_version=1.0')
|
14
|
+
e['rack.input'] = StringIO.new('something=given')
|
15
|
+
e['HTTP_VERSION'] = 'HTTP/1.1'
|
16
|
+
e['HTTP_HOST'] = 'rightscale'
|
17
|
+
e
|
18
|
+
end
|
19
|
+
|
12
20
|
let(:request) do
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
request = Praxis::Request.new(env)
|
18
|
-
request.action = action
|
19
|
-
request
|
21
|
+
r = Praxis::Request.new(env)
|
22
|
+
r.route_params = {id: 1}
|
23
|
+
r.action = action
|
24
|
+
r
|
20
25
|
end
|
21
26
|
|
22
27
|
context 'given a request' do
|
@@ -34,6 +34,16 @@ describe Praxis::ResourceDefinition do
|
|
34
34
|
expect(index).to be_kind_of(Praxis::ActionDefinition)
|
35
35
|
expect(index.description).to eq("index description")
|
36
36
|
end
|
37
|
+
|
38
|
+
it 'complains if action names are not symbols' do
|
39
|
+
expect do
|
40
|
+
Class.new do
|
41
|
+
include Praxis::ResourceDefinition
|
42
|
+
action "foo" do
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end.to raise_error(ArgumentError,/Action names must be defined using symbols/)
|
46
|
+
end
|
37
47
|
end
|
38
48
|
|
39
49
|
|
@@ -113,4 +123,56 @@ describe Praxis::ResourceDefinition do
|
|
113
123
|
|
114
124
|
end
|
115
125
|
|
126
|
+
context '#canonical_path' do
|
127
|
+
context 'setting the action' do
|
128
|
+
it 'reads the specified action' do
|
129
|
+
expect(subject.canonical_path).to eq(subject.actions[:show])
|
130
|
+
end
|
131
|
+
it 'cannot be done if already been defined' do
|
132
|
+
expect{
|
133
|
+
resource_definition.canonical_path :reset
|
134
|
+
}.to raise_error(/'canonical_path' can only be defined once./)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
context 'if none specified' do
|
138
|
+
subject(:resource_definition) do
|
139
|
+
Class.new do
|
140
|
+
include Praxis::ResourceDefinition
|
141
|
+
action :show do
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
it 'defaults to the :show action' do
|
146
|
+
expect(subject.canonical_path).to eq(subject.actions[:show])
|
147
|
+
end
|
148
|
+
end
|
149
|
+
context 'with an undefined action' do
|
150
|
+
subject(:resource_definition) do
|
151
|
+
Class.new do
|
152
|
+
include Praxis::ResourceDefinition
|
153
|
+
canonical_path :non_existent
|
154
|
+
end
|
155
|
+
end
|
156
|
+
it 'raises an error' do
|
157
|
+
expect{
|
158
|
+
subject.canonical_path
|
159
|
+
}.to raise_error(/Action 'non_existent' does not exist/)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context '#to_href' do
|
165
|
+
it 'accesses the path expansion functions of the primary route' do
|
166
|
+
expect(subject.to_href( id: 1)).to eq("/people/1")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
context '#parse_href' do
|
170
|
+
let(:parsed){ resource_definition.parse_href("/people/1") }
|
171
|
+
it 'accesses the path expansion functions of the primary route' do
|
172
|
+
expect(parsed).to have_key(:id)
|
173
|
+
end
|
174
|
+
it 'coerces the types as specified in the resource definition' do
|
175
|
+
expect(parsed[:id]).to be_kind_of(Integer)
|
176
|
+
end
|
177
|
+
end
|
116
178
|
end
|
@@ -365,7 +365,16 @@ describe Praxis::ResponseDefinition do
|
|
365
365
|
|
366
366
|
context 'when content type matches the mediatype of the spec' do
|
367
367
|
let(:response_headers) { {'Content-Type' => content_type } }
|
368
|
-
it '
|
368
|
+
it 'validates successfully' do
|
369
|
+
expect {
|
370
|
+
response_definition.validate_content_type!(response)
|
371
|
+
}.to_not raise_error
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
context 'when content type includes a parameter' do
|
376
|
+
let(:response_headers) { {'Content-Type' => "#{content_type};collection=true" } }
|
377
|
+
it 'validates successfully' do
|
369
378
|
expect {
|
370
379
|
response_definition.validate_content_type!(response)
|
371
380
|
}.to_not raise_error
|
@@ -405,7 +414,7 @@ describe Praxis::ResponseDefinition do
|
|
405
414
|
it 'validates each part' do
|
406
415
|
response_definition.parts
|
407
416
|
expect {
|
408
|
-
response_definition.validate_parts!(response)
|
417
|
+
response_definition.validate_parts!(response)
|
409
418
|
}.to_not raise_error
|
410
419
|
end
|
411
420
|
|
@@ -425,6 +434,7 @@ describe Praxis::ResponseDefinition do
|
|
425
434
|
|
426
435
|
end
|
427
436
|
|
437
|
+
|
428
438
|
context 'with invalid definitions' do
|
429
439
|
it 'raises an error if status code is not part of the definition' do
|
430
440
|
expect do
|
@@ -442,19 +452,19 @@ describe Praxis::ResponseDefinition do
|
|
442
452
|
let(:parts) { nil }
|
443
453
|
|
444
454
|
let(:response) do
|
445
|
-
|
446
|
-
|
447
|
-
|
455
|
+
Praxis::ResponseDefinition.new(:custom) do
|
456
|
+
status 300
|
457
|
+
end
|
448
458
|
end
|
449
459
|
subject(:output) { response.describe }
|
450
|
-
|
460
|
+
|
451
461
|
before do
|
452
462
|
response.description(description) if description
|
453
463
|
response.location(location) if location
|
454
|
-
response.parts(parts) if parts
|
464
|
+
response.parts(parts) if parts
|
455
465
|
response.headers(headers) if headers
|
456
466
|
end
|
457
|
-
|
467
|
+
|
458
468
|
context 'for a definition without parts' do
|
459
469
|
it{ should be_kind_of(::Hash) }
|
460
470
|
its([:description]){ should be(description) }
|
@@ -465,33 +475,33 @@ describe Praxis::ResponseDefinition do
|
|
465
475
|
expect( output[:headers]['Header1'] ).to eq({value: 'Value1' ,type: :string })
|
466
476
|
end
|
467
477
|
end
|
468
|
-
|
478
|
+
|
469
479
|
context 'for a definition with (homogeneous) parts' do
|
470
480
|
subject(:described_parts){ output[:parts_like] }
|
471
481
|
context 'using :like' do
|
472
482
|
let(:parts) { {like: :ok, media_type: 'foobar'} }
|
473
|
-
|
483
|
+
|
474
484
|
it 'should contain a parts_like key with a hash' do
|
475
485
|
expect( output ).to have_key(:parts_like)
|
476
486
|
end
|
477
|
-
|
478
|
-
it{ should be_kind_of(::Hash) }
|
487
|
+
|
488
|
+
it{ should be_kind_of(::Hash) }
|
479
489
|
its([:media_type]){ should == { identifier: 'foobar'} }
|
480
490
|
its([:status]){ should == 200 }
|
481
491
|
end
|
482
492
|
context 'using a full response definition block' do
|
483
493
|
let(:parts) do
|
484
|
-
Proc.new do
|
494
|
+
Proc.new do
|
485
495
|
status 234
|
486
496
|
media_type 'custom_media'
|
487
497
|
end
|
488
498
|
end
|
489
|
-
|
499
|
+
|
490
500
|
it 'should contain a parts_like key with a hash' do
|
491
501
|
expect( output ).to have_key(:parts_like)
|
492
502
|
end
|
493
|
-
|
494
|
-
it{ should be_kind_of(::Hash) }
|
503
|
+
|
504
|
+
it{ should be_kind_of(::Hash) }
|
495
505
|
its([:media_type]){ should == { identifier: 'custom_media'} }
|
496
506
|
its([:status]){ should == 234 }
|
497
507
|
end
|
data/spec/praxis/stage_spec.rb
CHANGED
@@ -13,8 +13,6 @@ describe Praxis::Stage do
|
|
13
13
|
|
14
14
|
context ".run" do
|
15
15
|
it "sets up and execute callbacks" do
|
16
|
-
expect(stage).to receive('setup!')
|
17
|
-
expect(stage).to receive('setup_deferred_callbacks!')
|
18
16
|
expect(stage).to receive('execute')
|
19
17
|
expect(stage).to receive('execute_callbacks').twice
|
20
18
|
stage.run
|
@@ -22,7 +20,10 @@ describe Praxis::Stage do
|
|
22
20
|
end
|
23
21
|
|
24
22
|
context ".setup!" do
|
25
|
-
it
|
23
|
+
it 'should call setup_deferred_callbacks' do
|
24
|
+
expect(stage).to receive('setup_deferred_callbacks!')
|
25
|
+
stage.setup!
|
26
|
+
end
|
26
27
|
end
|
27
28
|
|
28
29
|
context ".setup_deferred_callbacks!" do
|
@@ -40,11 +41,6 @@ describe Praxis::Stage do
|
|
40
41
|
end
|
41
42
|
|
42
43
|
context ".execute" do
|
43
|
-
it "raises error when @stages is empty" do
|
44
|
-
error_msg = 'Subclass must implement Stage#execute'
|
45
|
-
expect{stage.execute}.to raise_error(NotImplementedError, error_msg)
|
46
|
-
end
|
47
|
-
|
48
44
|
it "runs all the stages" do
|
49
45
|
double_stage = double("stage")
|
50
46
|
expect(double_stage).to receive('run')
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Praxis::Collection do
|
4
|
+
|
5
|
+
let(:type) { Volume }
|
6
|
+
let(:example) { Volume.example('example-volume') }
|
7
|
+
|
8
|
+
let(:snapshots) { example.snapshots }
|
9
|
+
|
10
|
+
subject(:media_type_collection) do
|
11
|
+
Volume.attributes[:snapshots].type
|
12
|
+
end
|
13
|
+
|
14
|
+
context '.of' do
|
15
|
+
let(:media_type) do
|
16
|
+
Class.new(Praxis::MediaType) do
|
17
|
+
identifier 'an-awesome-type'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
subject!(:collection) do
|
22
|
+
Praxis::Collection.of(media_type)
|
23
|
+
end
|
24
|
+
|
25
|
+
its(:identifier) { should eq 'an-awesome-type;type=collection' }
|
26
|
+
|
27
|
+
it 'sets the collection on the media type' do
|
28
|
+
expect(media_type::Collection).to be(collection)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns an existing Collection type' do
|
32
|
+
expect(Praxis::Collection.of(media_type)).to be(collection)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'works with explicitly-defined collections' do
|
36
|
+
expect(Praxis::Collection.of(Volume)).to be(Volume::Collection)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'defined explicitly' do
|
41
|
+
subject(:type) { Volume::Collection }
|
42
|
+
its(:member_type) { should be Volume }
|
43
|
+
its(:identifier) { should eq 'application/vnd.acme.volumes' }
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
context '.member_type' do
|
48
|
+
its(:member_type){ should be(VolumeSnapshot) }
|
49
|
+
its(:member_attribute){ should be_kind_of(Attributor::Attribute) }
|
50
|
+
its('member_attribute.type'){ should be(VolumeSnapshot) }
|
51
|
+
end
|
52
|
+
|
53
|
+
context '.load' do
|
54
|
+
let(:volume_data) do
|
55
|
+
{
|
56
|
+
id: 1,
|
57
|
+
name: 'bob',
|
58
|
+
snapshots: snapshots_data
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
let(:snapshots_data) {
|
63
|
+
nil
|
64
|
+
}
|
65
|
+
|
66
|
+
|
67
|
+
context 'loading an array' do
|
68
|
+
let(:snapshots_data) do
|
69
|
+
[{id: 1, name: 'snapshot-1'},
|
70
|
+
{id: 2, name: 'snapshot-2'}]
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:volume) { Volume.load(volume_data) }
|
74
|
+
subject(:snapshots) { volume.snapshots }
|
75
|
+
|
76
|
+
it 'sets the collection members' do
|
77
|
+
expect(snapshots).to have(2).items
|
78
|
+
|
79
|
+
expect(snapshots[0].id).to eq(1)
|
80
|
+
expect(snapshots[0].name).to eq('snapshot-1')
|
81
|
+
expect(snapshots[1].id).to eq(2)
|
82
|
+
expect(snapshots[1].name).to eq('snapshot-2')
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
context '#render' do
|
90
|
+
|
91
|
+
|
92
|
+
context 'for members' do
|
93
|
+
let(:volume_output) { example.render(:default) }
|
94
|
+
|
95
|
+
subject(:output) { volume_output[:snapshots] }
|
96
|
+
|
97
|
+
it { should eq(snapshots.collect(&:render)) }
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
context '#validate' do
|
104
|
+
let(:volume_data) do
|
105
|
+
{
|
106
|
+
id: 1,
|
107
|
+
name: 'bob',
|
108
|
+
snapshots: snapshots_data
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
let(:snapshots_data) {
|
113
|
+
nil
|
114
|
+
}
|
115
|
+
|
116
|
+
|
117
|
+
context 'for an array' do
|
118
|
+
let(:snapshots_data) do
|
119
|
+
[{id: 1, name: 'snapshot-1'},
|
120
|
+
{id: 2, name: 'snapshot-2'}]
|
121
|
+
end
|
122
|
+
|
123
|
+
let(:volume) { Volume.load(volume_data) }
|
124
|
+
let(:snapshots) { volume.snapshots }
|
125
|
+
|
126
|
+
it 'validates' do
|
127
|
+
expect(volume.validate).to be_empty
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'with invalid members' do
|
131
|
+
let(:snapshots_data) do
|
132
|
+
[{id: 1, name: 'invalid-1'},
|
133
|
+
{id: 2, name: 'snapshot-2'}]
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'returns the error' do
|
137
|
+
expect(volume.validate).to have(1).item
|
138
|
+
expect(volume.validate[0]).to match(/value \(invalid-1\) does not match regexp/)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
@@ -54,14 +54,14 @@ class Instances < BaseClass
|
|
54
54
|
def bulk_create(cloud_id:)
|
55
55
|
self.response = BulkResponse.new
|
56
56
|
|
57
|
+
|
57
58
|
request.payload.each do |instance_id,instance|
|
58
59
|
part_body = JSON.pretty_generate(key: instance_id, value: instance.render(:create))
|
59
60
|
headers = {
|
60
61
|
'Status' => '201',
|
61
62
|
'Content-Type' => Instance.identifier,
|
62
|
-
'Location' =>
|
63
|
+
'Location' => definition.to_href(cloud_id: cloud_id, id: instance.id)
|
63
64
|
}
|
64
|
-
|
65
65
|
part = Praxis::MultipartPart.new(part_body, headers)
|
66
66
|
|
67
67
|
response.add_part(instance_id, part)
|
@@ -98,4 +98,10 @@ class Instances < BaseClass
|
|
98
98
|
response
|
99
99
|
end
|
100
100
|
|
101
|
+
def update(id:, cloud_id:)
|
102
|
+
response.body = JSON.pretty_generate(request.payload.dump)
|
103
|
+
response.headers['Content-Type'] = 'application/vnd.acme.instance'
|
104
|
+
response
|
105
|
+
end
|
106
|
+
|
101
107
|
end
|