Skip to content
This repository was archived by the owner on Jun 2, 2021. It is now read-only.

Commit a5d2d96

Browse files
authored
v3(services): all bindings should have last_operation (cloudfoundry#1863)
When V3 bindings work is complete all binding types should have a last_operation. However bindings created in V2 will not have this. To make the output JSON for V3 GET endpoints consistent, we should patch up missing last_operation objects, rather than present `null` which can be harder to parse. [#171843320](https://www.pivotaltracker.com/story/show/171843320)
1 parent f93e4ea commit a5d2d96

6 files changed

Lines changed: 129 additions & 51 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module VCAP::CloudController::Presenters::Mixins
2+
module LastOperationHelper
3+
def last_operation(resource)
4+
last_operation = resource.try(:last_operation)
5+
6+
if last_operation
7+
last_operation.to_hash
8+
else
9+
# Bindings created using V2 may not have a last operation, so we make
10+
# a best attempt so that the output JSON can be parsed consistently
11+
{
12+
type: 'create',
13+
state: 'succeeded',
14+
description: '',
15+
created_at: resource.try(:created_at),
16+
updated_at: resource.try(:updated_at),
17+
}
18+
end
19+
end
20+
end
21+
end

app/presenters/v3/service_credential_binding_presenter.rb

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,40 @@
11
require_relative 'base_presenter'
2+
require 'presenters/mixins/last_operation_helper'
23

34
module VCAP
45
module CloudController
56
module Presenters
67
module V3
78
class ServiceCredentialBindingPresenter < BasePresenter
9+
include VCAP::CloudController::Presenters::Mixins::LastOperationHelper
10+
811
def to_hash
912
base_hash.merge(extra).merge(decorations)
1013
end
1114

1215
private
1316

17+
def binding
18+
@resource
19+
end
20+
1421
def base_hash
1522
{
16-
guid: @resource.guid,
17-
created_at: @resource.created_at,
18-
updated_at: @resource.updated_at,
19-
name: @resource.name,
20-
type: type
23+
guid: binding.guid,
24+
created_at: binding.created_at,
25+
updated_at: binding.updated_at,
26+
name: binding.name,
27+
type: type,
28+
last_operation: last_operation(binding),
2129
}
2230
end
2331

2432
def decorations
25-
@decorators.reduce({}) { |memo, d| d.decorate(memo, [@resource]) }
33+
@decorators.reduce({}) { |memo, d| d.decorate(memo, [binding]) }
2634
end
2735

2836
def type
29-
case @resource
37+
case binding
3038
when VCAP::CloudController::ServiceKey
3139
'key'
3240
when VCAP::CloudController::ServiceBinding
@@ -38,65 +46,49 @@ def extra
3846
case type
3947
when 'app'
4048
{
41-
last_operation: last_operation,
4249
relationships: base_relationships.merge(app_relationship),
4350
links: base_links.merge(app_link)
4451
}
4552
when 'key'
4653
{
47-
last_operation: nil,
4854
relationships: base_relationships,
4955
links: base_links
5056
}
5157
end
5258
end
5359

54-
def last_operation
55-
return nil if @resource.last_operation.blank?
56-
57-
last_operation = @resource.last_operation
58-
59-
{
60-
type: last_operation.type,
61-
state: last_operation.state,
62-
description: last_operation.description,
63-
created_at: last_operation.created_at,
64-
updated_at: last_operation.updated_at
65-
}
66-
end
67-
6860
def base_links
69-
parameters = { parameters: "#{path_to_self}/parameters" } unless @resource.service_instance.user_provided_instance?
61+
parameters = { parameters: "#{path_to_self}/parameters" } unless binding.service_instance.user_provided_instance?
7062

7163
{
7264
self: path_to_self,
7365
details: "#{path_to_self}/details",
74-
service_instance: "/v3/service_instances/#{@resource.service_instance_guid}"
66+
service_instance: "/v3/service_instances/#{binding.service_instance_guid}"
7567
}.merge(parameters || {}).transform_values do |path|
7668
hrefify(path)
7769
end
7870
end
7971

8072
def path_to_self
81-
"/v3/service_credential_bindings/#{@resource.guid}"
73+
"/v3/service_credential_bindings/#{binding.guid}"
8274
end
8375

8476
def base_relationships
8577
{
86-
service_instance: { data: { guid: @resource.service_instance_guid } }
78+
service_instance: { data: { guid: binding.service_instance_guid } }
8779
}
8880
end
8981

9082
def app_relationship
91-
return {} if @resource.app_guid.blank?
83+
return {} if binding.app_guid.blank?
9284

93-
{ app: { data: { guid: @resource.app_guid } } }
85+
{ app: { data: { guid: binding.app_guid } } }
9486
end
9587

9688
def app_link
97-
return {} if @resource.app_guid.blank?
89+
return {} if binding.app_guid.blank?
9890

99-
{ app: hrefify("/v3/apps/#{@resource.app_guid}") }
91+
{ app: hrefify("/v3/apps/#{binding.app_guid}") }
10092
end
10193

10294
def hrefify(path)

app/presenters/v3/service_route_binding_presenter.rb

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
require_relative 'base_presenter'
2+
require 'presenters/mixins/last_operation_helper'
23

34
module VCAP
45
module CloudController
56
module Presenters
67
module V3
78
class ServiceRouteBindingPresenter < BasePresenter
9+
include VCAP::CloudController::Presenters::Mixins::LastOperationHelper
10+
811
def to_hash
912
base.merge(decorations)
1013
end
@@ -17,7 +20,7 @@ def base
1720
route_service_url: binding.route_service_url,
1821
created_at: binding.created_at,
1922
updated_at: binding.updated_at,
20-
last_operation: last_operation,
23+
last_operation: last_operation(binding),
2124
relationships: relationships,
2225
links: links
2326
}
@@ -31,20 +34,6 @@ def binding
3134
@resource
3235
end
3336

34-
def last_operation
35-
return nil if binding.last_operation.blank?
36-
37-
last_operation = binding.last_operation
38-
39-
{
40-
type: last_operation.type,
41-
state: last_operation.state,
42-
description: last_operation.description,
43-
created_at: last_operation.created_at,
44-
updated_at: last_operation.updated_at
45-
}
46-
end
47-
4837
def links
4938
{
5039
self: {

spec/request/service_credential_bindings_spec.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,7 +1296,7 @@ def expected_json(binding)
12961296
created_at: iso8601,
12971297
updated_at: iso8601,
12981298
name: binding.name,
1299-
last_operation: nil,
1299+
last_operation: last_operation(binding),
13001300
relationships: {
13011301
service_instance: {
13021302
data: {
@@ -1350,14 +1350,22 @@ def extra(binding)
13501350
end
13511351

13521352
def last_operation(binding)
1353-
if binding.last_operation.present?
1353+
if binding.try(:last_operation)
13541354
{
13551355
type: binding.last_operation.type,
13561356
state: binding.last_operation.state,
13571357
description: binding.last_operation.description,
13581358
created_at: iso8601,
13591359
updated_at: iso8601
13601360
}
1361+
else
1362+
{
1363+
type: 'create',
1364+
state: 'succeeded',
1365+
description: '',
1366+
created_at: iso8601,
1367+
updated_at: iso8601
1368+
}
13611369
end
13621370
end
13631371

spec/unit/presenters/v3/service_credential_binding_presenter_spec.rb

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ module CloudController
2222

2323
it 'should include the binding fields plus links and relationships' do
2424
presenter = described_class.new(credential_binding)
25-
expect(presenter.to_hash).to match(
25+
expect(presenter.to_hash.with_indifferent_access).to match(
2626
{
2727
guid: 'some-guid',
2828
type: 'app',
@@ -78,6 +78,25 @@ module CloudController
7878
expect(presenter.to_hash[:name]).to be_nil
7979
end
8080
end
81+
82+
context 'no last_operation' do
83+
let(:credential_binding) do
84+
ServiceBinding.make(name: 'some-name', guid: 'some-guid', app: app, service_instance: instance)
85+
end
86+
87+
it 'still displays the last operation' do
88+
presenter = described_class.new(credential_binding)
89+
expect(presenter.to_hash[:last_operation]).to match(
90+
{
91+
type: 'create',
92+
state: 'succeeded',
93+
description: '',
94+
updated_at: credential_binding.updated_at,
95+
created_at: credential_binding.created_at
96+
}
97+
)
98+
end
99+
end
81100
end
82101

83102
describe 'key bindings' do
@@ -94,7 +113,13 @@ module CloudController
94113
name: 'some-name',
95114
created_at: credential_binding.created_at,
96115
updated_at: credential_binding.updated_at,
97-
last_operation: nil,
116+
last_operation: {
117+
type: 'create',
118+
state: 'succeeded',
119+
description: '',
120+
updated_at: credential_binding.updated_at,
121+
created_at: credential_binding.created_at
122+
},
98123
relationships: {
99124
service_instance: {
100125
data: {
@@ -119,6 +144,25 @@ module CloudController
119144
}
120145
)
121146
end
147+
148+
context 'no last_operation' do
149+
let(:credential_binding) do
150+
ServiceKey.make(name: 'some-name', guid: 'some-guid', service_instance: instance)
151+
end
152+
153+
it 'still displays the last operation' do
154+
presenter = described_class.new(credential_binding)
155+
expect(presenter.to_hash[:last_operation]).to match(
156+
{
157+
type: 'create',
158+
state: 'succeeded',
159+
description: '',
160+
updated_at: credential_binding.updated_at,
161+
created_at: credential_binding.created_at
162+
}
163+
)
164+
end
165+
end
122166
end
123167

124168
describe 'for user provided service instances' do

spec/unit/presenters/v3/service_route_binding_presenter_spec.rb

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ module CloudController
2727

2828
it 'presents the correct object' do
2929
presenter = described_class.new(binding)
30-
expect(presenter.to_hash).to match(
30+
expect(presenter.to_hash.with_indifferent_access).to match(
3131
{
3232
guid: guid,
3333
created_at: binding.created_at,
@@ -67,6 +67,30 @@ module CloudController
6767
)
6868
end
6969

70+
context 'no last_operation' do
71+
let(:binding) do
72+
RouteBinding.make(
73+
guid: guid,
74+
service_instance: service_instance,
75+
route: route,
76+
route_service_url: route_service_url,
77+
)
78+
end
79+
80+
it 'still displays the last operation' do
81+
presenter = described_class.new(binding)
82+
expect(presenter.to_hash[:last_operation]).to match(
83+
{
84+
type: 'create',
85+
state: 'succeeded',
86+
description: '',
87+
updated_at: binding.updated_at,
88+
created_at: binding.created_at
89+
}
90+
)
91+
end
92+
end
93+
7094
describe 'decorators' do
7195
let(:decorator1) { double('FakeDecorator', decorate: { foo: 'bar' }) }
7296
let(:decorator2) { double('FakeDecorator', decorate: { xyzzy: 'omg' }) }

0 commit comments

Comments
 (0)