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