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

Commit 9d8269f

Browse files
Get guid,name of service broker in service plan list request
[#172379944](https://www.pivotaltracker.com/story/show/172379944)
1 parent 60a874d commit 9d8269f

8 files changed

Lines changed: 170 additions & 9 deletions

File tree

app/controllers/v3/service_plans_controller.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
require 'actions/transactional_metadata_update'
99
require 'decorators/include_service_plan_space_organization_decorator'
1010
require 'decorators/include_service_plan_service_offering_decorator'
11+
require 'decorators/field_service_plan_service_broker_decorator'
1112

1213
class ServicePlansController < ApplicationController
1314
include ServicePermissions
@@ -44,6 +45,7 @@ def index
4445
decorators = []
4546
decorators << IncludeServicePlanSpaceOrganizationDecorator if IncludeServicePlanSpaceOrganizationDecorator.match?(message.include)
4647
decorators << IncludeServicePlanServiceOfferingDecorator if IncludeServicePlanServiceOfferingDecorator.match?(message.include)
48+
decorators << FieldServicePlanServiceBrokerDecorator.new(message.fields) if FieldServicePlanServiceBrokerDecorator.match?(message.fields)
4749

4850
presenter = Presenters::V3::PaginatedListPresenter.new(
4951
presenter: Presenters::V3::ServicePlanPresenter,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module VCAP::CloudController
2+
class FieldServicePlanServiceBrokerDecorator
3+
def self.allowed
4+
Set['name', 'guid']
5+
end
6+
7+
def self.match?(fields)
8+
fields.is_a?(Hash) && fields[:'service_offering.service_broker']&.to_set&.intersect?(self.allowed)
9+
end
10+
11+
def initialize(fields)
12+
@fields = fields[:'service_offering.service_broker'].to_set.intersection(self.class.allowed)
13+
end
14+
15+
def decorate(hash, service_plans)
16+
hash[:included] ||= {}
17+
service_offerings = service_plans.map(&:service).uniq
18+
service_brokers = service_offerings.map(&:service_broker).uniq
19+
hash[:included][:service_brokers] = service_brokers.sort_by(&:created_at).map do |broker|
20+
broker_view = {}
21+
broker_view[:name] = broker.name if @fields.include?('name')
22+
broker_view[:guid] = broker.guid if @fields.include?('guid')
23+
broker_view
24+
end
25+
26+
hash
27+
end
28+
end
29+
end

app/messages/service_plans_list_message.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class ServicePlansListMessage < MetadataListMessage
1717
]
1818
@single_keys = [
1919
:available,
20+
:fields
2021
]
2122

2223
register_allowed_keys(@single_keys + @array_keys)
@@ -25,8 +26,14 @@ class ServicePlansListMessage < MetadataListMessage
2526
validates_with IncludeParamValidator, valid_values: %w(space.organization service_offering)
2627
validates :available, inclusion: { in: %w(true false), message: "only accepts values 'true' or 'false'" }, allow_nil: true
2728

29+
validates :fields, allow_nil: true, fields: {
30+
allowed: {
31+
'service_offering.service_broker' => ['guid', 'name']
32+
}
33+
}
34+
2835
def self.from_params(params)
29-
super(params, @array_keys.map(&:to_s))
36+
super(params, @array_keys.map(&:to_s), fields: %w(fields))
3037
end
3138

3239
def available?

docs/v3/source/includes/experimental_resources/service_plans/_list.md.erb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,21 @@ Name | Type | Description
4242
**service_instance_guids** | _list of strings_ | Comma-delimited list of Service Instance GUIDs to filter by.
4343
**label_selector** | _string_ | **Experimental** - A query string containing a list of [label selector](#labels-and-selectors) requirements.
4444
**include** | _list of strings_ | Optionally include a list of unique related resources in the response. <br>Valid values are `space.organization` and `service_offering`.
45+
**fields** | [_fields parameter_](#fields-parameter) | **Experimental** [_Allowed values_](#service-plans-list-fields)
4546

4647

4748
The **organization_guids** and **space_guids** filters do not filter plans that are public.
4849
They both act on plans that are restricted to certain Organizations, and to plans from Space-Scoped
4950
Service Brokers.
5051

52+
53+
##### Service Plans List Fields
54+
55+
Resource | Allowed Keys
56+
------------------- | ----
57+
service_offering.service_broker| `guid`, `name`
58+
59+
5160
#### Permitted roles
5261
|
5362
--- | ---
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
RSpec.shared_examples 'field decorator match?' do |resource, keys|
2+
keys.each do |key|
3+
it "matches hashes containing resource symbol `#{resource}` and value `#{key}`" do
4+
expect(described_class.match?({ "#{resource}": [key.to_s], other: ['bar'] })).to be_truthy
5+
end
6+
end
7+
8+
it "matches hashes containing resource symbol `#{resource}` and all valid keys" do
9+
expect(described_class.match?({ "#{resource}": keys, other: ['bar'] })).to be_truthy
10+
end
11+
12+
it 'does not match other values for a valid resource' do
13+
expect(described_class.match?({ "#{resource}": ['foo'] })).to be_falsey
14+
end
15+
16+
it 'does not match other resource values' do
17+
expect(described_class.match?({ other: ['bar'] })).to be_falsey
18+
end
19+
20+
it 'does not match non-hashes' do
21+
expect(described_class.match?('foo')).to be_falsey
22+
end
23+
end

spec/request/service_plans_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153
page: 2,
154154
order_by: 'updated_at',
155155
label_selector: 'foo==bar',
156+
fields: { 'service_offering.service_broker' => 'name' }
156157
}
157158
end
158159
end
@@ -476,6 +477,22 @@
476477
expect(parsed_response['included']['service_offerings'][1]['guid']).to eq(offering_2.guid)
477478
end
478479
end
480+
481+
describe 'fields' do
482+
let!(:plan_1) { VCAP::CloudController::ServicePlan.make }
483+
let!(:plan_2) { VCAP::CloudController::ServicePlan.make }
484+
485+
it 'can include service broker name and guid' do
486+
get '/v3/service_plans?fields[service_offering.service_broker]=name,guid', nil, admin_headers
487+
expect(last_response).to have_status_code(200)
488+
489+
expect(parsed_response['included']['service_brokers']).to have(2).elements
490+
expect(parsed_response['included']['service_brokers'][0]['guid']).to eq(plan_1.service.service_broker.guid)
491+
expect(parsed_response['included']['service_brokers'][0]['name']).to eq(plan_1.service.service_broker.name)
492+
expect(parsed_response['included']['service_brokers'][1]['guid']).to eq(plan_2.service.service_broker.guid)
493+
expect(parsed_response['included']['service_brokers'][1]['name']).to eq(plan_2.service.service_broker.name)
494+
end
495+
end
479496
end
480497

481498
describe 'DELETE /v3/service_plans/:guid' do
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
require 'spec_helper'
2+
require 'decorators/field_service_plan_service_broker_decorator'
3+
require 'field_decorator_spec_shared_examples'
4+
5+
module VCAP::CloudController
6+
RSpec.describe FieldServicePlanServiceBrokerDecorator do
7+
describe '.decorate' do
8+
let(:offering1) { Service.make }
9+
let(:offering2) { Service.make }
10+
11+
let(:plan1) { ServicePlan.make(service: offering1) }
12+
let(:plan2) { ServicePlan.make(service: offering2) }
13+
14+
it 'decorated the given hash with broker name and guid' do
15+
undecorated_hash = { foo: 'bar', included: { monkeys: %w(zach greg) } }
16+
decorator = described_class.new({ 'service_offering.service_broker': ['name', 'guid'] })
17+
18+
hash = decorator.decorate(undecorated_hash, [plan1, plan2])
19+
20+
expect(hash).to match({
21+
foo: 'bar',
22+
included: {
23+
monkeys: %w(zach greg),
24+
service_brokers: [
25+
{
26+
guid: plan1.service.service_broker.guid,
27+
name: plan1.service.service_broker.name
28+
},
29+
{
30+
guid: plan2.service.service_broker.guid,
31+
name: plan2.service.service_broker.name
32+
}
33+
]
34+
}
35+
})
36+
end
37+
38+
context 'when plans are from the same broker' do
39+
let(:plan3) { ServicePlan.make(service: offering1) }
40+
41+
it 'does not duplicate the broker' do
42+
decorator = described_class.new({ 'service_offering.service_broker': ['name'] })
43+
hash = decorator.decorate({}, [plan1, plan3])
44+
expect(hash[:included][:service_brokers]).to have(1).element
45+
end
46+
end
47+
end
48+
49+
describe '.match?' do
50+
it_behaves_like 'field decorator match?', 'service_offering.service_broker', ['name', 'guid']
51+
end
52+
end
53+
end

spec/unit/messages/service_plans_list_message_spec.rb

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'spec_helper'
22
require 'messages/service_plans_list_message'
3+
require 'field_message_spec_shared_examples'
34

45
module VCAP::CloudController
56
RSpec.describe ServicePlansListMessage do
@@ -17,11 +18,12 @@ module VCAP::CloudController
1718
'service_offering_guids' => 'offering_guid_1,offering_guid_2',
1819
'service_offering_names' => 'offering_name_1,offering_name_2',
1920
'space_guids' => 'space_guid_1,space_guid_2',
21+
'fields' => { 'service_offering.service_broker' => 'guid,name' },
2022
}.with_indifferent_access
2123
end
2224

2325
it 'returns the correct ServicePlansListMessage' do
24-
message = ServicePlansListMessage.from_params(params)
26+
message = described_class.from_params(params)
2527

2628
expect(message).to be_valid
2729
expect(message).to be_a(ServicePlansListMessage)
@@ -36,6 +38,7 @@ module VCAP::CloudController
3638
expect(message.service_offering_guids).to contain_exactly('offering_guid_1', 'offering_guid_2')
3739
expect(message.service_offering_names).to contain_exactly('offering_name_1', 'offering_name_2')
3840
expect(message.space_guids).to contain_exactly('space_guid_1', 'space_guid_2')
41+
expect(message.fields).to match({ 'service_offering.service_broker': ['guid', 'name'] })
3942
end
4043

4144
it 'converts requested keys to symbols' do
@@ -55,55 +58,73 @@ module VCAP::CloudController
5558
end
5659

5760
it 'accepts an empty set' do
58-
message = ServicePlansListMessage.from_params({})
61+
message = described_class.from_params({})
5962
expect(message).to be_valid
6063
end
6164

6265
it 'does not accept arbitrary fields' do
63-
message = ServicePlansListMessage.from_params({ foobar: 'pants' }.with_indifferent_access)
66+
message = described_class.from_params({ foobar: 'pants' }.with_indifferent_access)
6467

6568
expect(message).not_to be_valid
6669
expect(message.errors[:base][0]).to include("Unknown query parameter(s): 'foobar'")
6770
end
6871

6972
describe 'available' do
7073
it 'accepts `true`' do
71-
message = ServicePlansListMessage.from_params({ available: 'true' }.with_indifferent_access)
74+
message = described_class.from_params({ available: 'true' }.with_indifferent_access)
7275
expect(message).to be_valid
7376
expect(message.requested?(:available)).to be_truthy
7477
expect(message.available).to eq('true')
7578
expect(message.available?).to be(true)
7679
end
7780

7881
it 'accepts `false`' do
79-
message = ServicePlansListMessage.from_params({ available: 'false' }.with_indifferent_access)
82+
message = described_class.from_params({ available: 'false' }.with_indifferent_access)
8083
expect(message).to be_valid
8184
expect(message.requested?(:available)).to be_truthy
8285
expect(message.available).to eq('false')
8386
expect(message.available?).to be(false)
8487
end
8588

8689
it 'is false by default' do
87-
message = ServicePlansListMessage.from_params({})
90+
message = described_class.from_params({})
8891
expect(message).to be_valid
8992
expect(message.requested?(:available)).to be_falsey
9093
expect(message.available?).to be(false)
9194
end
9295

9396
it 'does not accept other values' do
94-
message = ServicePlansListMessage.from_params({ available: 'nope' }.with_indifferent_access)
97+
message = described_class.from_params({ available: 'nope' }.with_indifferent_access)
9598
expect(message).not_to be_valid
9699
expect(message.errors[:available]).to include("only accepts values 'true' or 'false'")
97100
end
98101
end
99102

100103
describe 'include' do
101104
it 'does not accept other values' do
102-
message = ServicePlansListMessage.from_params({ include: 'space' }.with_indifferent_access)
105+
message = described_class.from_params({ include: 'space' }.with_indifferent_access)
103106
expect(message).not_to be_valid
104107
expect(message.errors[:base]).to include(include("Invalid included resource: 'space'"))
105108
end
106109
end
110+
111+
context 'fields' do
112+
it 'validates `fields` is a hash' do
113+
message = described_class.from_params({ 'fields' => 'foo' }.with_indifferent_access)
114+
expect(message).not_to be_valid
115+
expect(message.errors[:fields][0]).to include('must be an object')
116+
end
117+
118+
it_behaves_like 'field query parameter', 'service_offering.service_broker', 'guid,name'
119+
120+
it 'does not accept fields resources that are not allowed' do
121+
message = described_class.from_params({ 'fields' => { 'space.foo': 'name' } })
122+
expect(message).not_to be_valid
123+
expect(message.errors[:fields]).to include(
124+
"[space.foo] valid resources are: 'service_offering.service_broker'"
125+
)
126+
end
127+
end
107128
end
108129
end
109130
end

0 commit comments

Comments
 (0)