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

Commit 956b7b2

Browse files
committed
v3 service plan returns cost as a top level property
[#172146647](https://www.pivotaltracker.com/story/show/172146647)
1 parent 2c9b7d9 commit 956b7b2

6 files changed

Lines changed: 237 additions & 23 deletions

File tree

app/presenters/v3/service_plan_presenter.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def to_hash
1616
available: service_plan.active?,
1717
name: service_plan.name,
1818
free: service_plan.free,
19+
costs: costs,
1920
description: service_plan.description,
2021
maintenance_info: maintenance_info,
2122
broker_catalog: {
@@ -53,6 +54,26 @@ def metadata
5354
parse(service_plan.extra)
5455
end
5556

57+
def costs
58+
cost_result = []
59+
if metadata[:costs]
60+
validation_errors = JSON::Validator.fully_validate(costs_schema, metadata[:costs])
61+
return cost_result unless validation_errors.none?
62+
63+
metadata[:costs].each do |cost|
64+
unit = cost[:unit].to_s
65+
cost[:amount].each do |currency, amount|
66+
cost_result << {
67+
currency: currency.to_s.upcase,
68+
amount: amount.to_f,
69+
unit: unit || ''
70+
}
71+
end
72+
end
73+
end
74+
cost_result
75+
end
76+
5677
def maintenance_info
5778
service_plan.maintenance_info || {}
5879
end
@@ -109,6 +130,28 @@ def links
109130

110131
links
111132
end
133+
134+
def costs_schema
135+
{
136+
'$schema' => 'http://json-schema.org/draft-04/schema#',
137+
'type' => 'array',
138+
'items' => {
139+
'type' => 'object',
140+
'required' => %w(amount unit),
141+
'properties' => {
142+
'amount' => {
143+
'type' => 'object',
144+
'additionalProperties' => {
145+
'type' => 'number'
146+
}
147+
},
148+
'unit' => {
149+
'type' => 'string'
150+
}
151+
}
152+
}
153+
}
154+
end
112155
end
113156
end
114157
end

docs/v3/source/includes/api_resources/_service_plans.erb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
"visibility_type": "public",
77
"available": true,
88
"free": false,
9+
"costs": [
10+
{
11+
"currency": "USD",
12+
"amount": 199.99,
13+
"unit": "Monthly"
14+
}
15+
],
916
"created_at": "2019-11-28T13:44:02Z",
1017
"updated_at": "2019-11-28T13:44:02Z",
1118
"maintenance_info": {
@@ -93,6 +100,13 @@
93100
"visibility_type": "organization",
94101
"available": true,
95102
"free": false,
103+
"costs": [
104+
{
105+
"currency": "USD",
106+
"amount": 199.99,
107+
"unit": "Monthly"
108+
}
109+
],
96110
"created_at": "2019-11-28T13:44:02Z",
97111
"updated_at": "2019-11-28T13:44:02Z",
98112
"maintenance_info": {
@@ -159,7 +173,7 @@
159173
"description": "Provides another service plan",
160174
"visibility_type": "admin",
161175
"available": true,
162-
"free": false,
176+
"free": true,
163177
"created_at": "2019-11-29T16:44:02Z",
164178
"updated_at": "2019-11-29T16:44:02Z",
165179
"maintenance_info": {},

docs/v3/source/includes/experimental_resources/service_offerings/_object.md.erb

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,22 @@ Name | Type | Description
1818
**created_at** | _datetime_ | The time with zone when the object was created.
1919
**updated_at** | _datetime_ | The time with zone when the object was last updated.
2020
**shareable** | _boolean_ | Whether or not Service Instances of this Service Offering can be shared across Organizations and Spaces.
21-
**documentation_url** | _string_ | Url that points to a documentation page for the Service Offering, if provided by the Service Broker as part of the metadata field as specified by [OSBAPI](https://github.com/openservicebrokerapi/servicebroker/blob/master/profile.md#service-metadata)
22-
**broker_catalog** | _object_ | This object contains information obtained from the Service Broker Catalog.
23-
**broker_catalog.id** | _string_ | The identifier that the Service Broker provided for this Service Offering.
24-
**broker_catalog.metadata** | _object_ | Additional information provided by the Service Broker.
25-
**broker_catalog.features.plan_updateable** | _boolean_ | Whether the Service Offering supports upgrade/downgrade for Service Plans by default
26-
**broker_catalog.features.bindable** | _boolean_ | Specifies whether Service Instances of the service can be bound to applications.
27-
**broker_catalog.features.instances_retrievable** | _boolean_ | Specifies whether the Fetching a Service Instance endpoint is supported for all Service Plans.
28-
**broker_catalog.features.bindings_retrievable** | _boolean_ | Specifies whether the Fetching a Service Binding endpoint is supported for all Service Plans.
29-
**broker_catalog.features.allow_context_updates** | _boolean_ | Specifies whether Service Instance updates relating only to context are propagated to the Service Broker.
21+
**documentation_url** | _string_ | Url that points to a documentation page for the Service Offering, if provided by the Service Broker as part of the metadata field
22+
**broker_catalog** | _[broker catalog object](#the-service-offering-broker-catalog)_ | This object contains information obtained from the Service Broker Catalog.
3023
**relationships.service_broker** | [_to-one relationship_](#to-one-relationships) | The Service Broker that provides this Service Offering.
3124
**metadata.labels** | [_label object_](#labels) | Labels applied to the Service Offering.
3225
**metadata.annotations** | [_annotation object_](#annotations) | Annotations added to the Service Offering.
3326
**links** | [_links object_](#links) | Links to related resources.
27+
28+
#### The service offering broker catalog
29+
30+
Name | Type | Description
31+
---- | ---- | -----------
32+
**id** | _string_ | The identifier that the Service Broker provided for this Service Offering.
33+
**metadata** | _object_ | Additional information provided by the Service Broker as specified by [OSBAPI](https://github.com/openservicebrokerapi/servicebroker/blob/master/profile.md#service-metadata-fields).
34+
**features.plan_updateable** | _boolean_ | Whether the Service Offering supports upgrade/downgrade for Service Plans by default
35+
**features.bindable** | _boolean_ | Specifies whether Service Instances of the service can be bound to applications.
36+
**features.instances_retrievable** | _boolean_ | Specifies whether the Fetching a Service Instance endpoint is supported for all Service Plans.
37+
**features.bindings_retrievable** | _boolean_ | Specifies whether the Fetching a Service Binding endpoint is supported for all Service Plans.
38+
**features.allow_context_updates** | _boolean_ | Specifies whether Service Instance updates relating only to context are propagated to the Service Broker.
39+

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

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,54 @@ Name | Type | Description
1515
**available** | _boolean_ | Whether or not the Service Plan is available.
1616
**visibility_type** | _string_ | Denotes the visibility of the plan. Can be `public`, `admin`, `organization`, `space`. See [_list of visibility types_](#list-of-visibility-types).
1717
**free** | _boolean_ | Whether or not the Service Plan is free of charge.
18+
**costs** | _array of [cost objects](#the-service-plan-cost)_ | The cost of the Service Plan as obtained from the Service Broker Catalog.
1819
**created_at** | _datetime_ | The time with zone when the object was created.
1920
**updated_at** | _datetime_ | The time with zone when the object was last updated.
2021
**maintenance_info** | _[maintenance_info object](#the-maintenance-info-object-for-service-plans)_ | Information about the version of this service instance.
21-
**broker_catalog** | _object_ | This object contains information obtained from the Service Broker Catalog.
22-
**broker_catalog.id** | _string_ | The identifier that the Service Broker provided for this Service Plan.
23-
**broker_catalog.metadata** | _object_ | Additional information provided by the Service Broker.
24-
**broker_catalog.maximum_polling_duration** | _integer_ | The maximum number of seconds that Cloud Foundry will wait for an asynchronous Service Broker operation.
25-
**broker_catalog.features.plan_updateable** | _boolean_ | Whether the Service Plan supports upgrade/downgrade for Service Plans by default
26-
**broker_catalog.features.bindable** | _boolean_ | Specifies whether Service Instances of the service can be bound to applications.
27-
**schemas** | _object_ | Schema definitions for Service Instances and Service Bindings for the Service Plan.
28-
**schemas.service_instance.create** | _object_ | Schema definition for Service Instance creation.
29-
**schemas.service_instance.create.parameters** | _JSON Schema object_ | The schema definition for the input parameters. Each input parameter is expressed as a property within a JSON object.
30-
**schemas.service_instance.update** | _object_ | Schema definition for Service Instance update.
31-
**schemas.service_instance.update.parameters** | _JSON Schema object_ | The schema definition for the input parameters. Each input parameter is expressed as a property within a JSON object.
32-
**schemas.service_binding.create** | _object_ | Schema definition for Service Binding creation.
33-
**schemas.service_binding.create.parameters** | _JSON Schema object_ | The schema definition for the input parameters. Each input parameter is expressed as a property within a JSON object.
22+
**broker_catalog** | _[broker catalog object](#the-service-plan-broker-catalog)_ | This object contains information obtained from the Service Broker Catalog.
23+
**schemas** | _[schemas object](#the-service-plan-schemas)_ | Schema definitions for Service Instances and Service Bindings for the Service Plan.
3424
**relationships.service_offering** | [_to-one relationship_](#to-one-relationships) | The Service Offering that this Service Plan relates to.
3525
**relationships.space** | [_to-one relationship_](#to-one-relationships) | The Space of the Service Broker, if this Service Plan is from a Space-Scoped Service Broker.
3626
**metadata.labels** | [_label object_](#labels) | Labels applied to the Service Plan.
3727
**metadata.annotations** | [_annotation object_](#annotations) | Annotations added to the Service Plan.
3828
**links** | [_links object_](#links) | Links to related resources.
3929

4030

31+
#### The service plan cost
32+
33+
Name | Type | Description
34+
---- | ---- | -----------
35+
**amount** | _float_ | Pricing amount.
36+
**currency** | _string_ | Currency code for the pricing amount, e.g. USD, GBP.
37+
**unit** | _string_ | Display name for type of cost, e.g. Monthly, Hourly, Request, GB.
38+
39+
4140
#### The maintenance info object for service plans
4241

4342
Name | Type | Description
4443
---- | ---- | -----------
4544
**version** | _string_ | The current semantic version of the Service Plan. Comparing this version with that of a Service Instance can be used to determine whether or not the Service Instance is up to date with this Service Plan.
4645
**description** | _string_ | A textual explanation associated with this version.
4746

47+
48+
#### The service plan broker catalog
49+
50+
Name | Type | Description
51+
---- | ---- | -----------
52+
**id** | _string_ | The identifier that the Service Broker provided for this Service Plan.
53+
**metadata** | _object_ | Additional information provided by the Service Broker as specified by [OSBAPI](https://github.com/openservicebrokerapi/servicebroker/blob/master/profile.md#plan-metadata-fields)
54+
**maximum_polling_duration** | _integer_ | The maximum number of seconds that Cloud Foundry will wait for an asynchronous Service Broker operation.
55+
**features.plan_updateable** | _boolean_ | Whether the Service Plan supports upgrade/downgrade for Service Plans by default
56+
**features.bindable** | _boolean_ | Specifies whether Service Instances of the service can be bound to applications.
57+
58+
59+
#### The service plan schemas
60+
61+
Name | Type | Description
62+
---- | ---- | -----------
63+
**service_instance.create** | _object_ | Schema definition for Service Instance creation.
64+
**service_instance.create.parameters** | _JSON Schema object_ | The schema definition for the input parameters. Each input parameter is expressed as a property within a JSON object.
65+
**service_instance.update** | _object_ | Schema definition for Service Instance update.
66+
**service_instance.update.parameters** | _JSON Schema object_ | The schema definition for the input parameters. Each input parameter is expressed as a property within a JSON object.
67+
**service_binding.create** | _object_ | Schema definition for Service Binding creation.
68+
**service_binding.create.parameters** | _JSON Schema object_ | The schema definition for the input parameters. Each input parameter is expressed as a property within a JSON object.

spec/request/service_plans_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ def create_plan_json(service_plan, labels: {}, annotations: {}, maintenance_info
758758
available: match(boolean),
759759
name: service_plan.name,
760760
free: match(boolean),
761+
costs: [],
761762
description: service_plan.description,
762763
broker_catalog: {
763764
id: service_plan.unique_id,

spec/unit/presenters/v3/service_plan_presenter_spec.rb

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
available: true,
4343
name: service_plan.name,
4444
free: false,
45+
costs: [],
4546
description: service_plan.description,
4647
maintenance_info: {
4748
version: '1.0.0',
@@ -154,6 +155,134 @@
154155
end
155156
end
156157

158+
context 'when plan has costs' do
159+
it 'flattens different currencies in the same unit' do
160+
service_plan =
161+
VCAP::CloudController::ServicePlan.make(extra: '{"costs": [
162+
{
163+
"amount": {
164+
"usd": 649.0,
165+
"gbp": 600.015454
166+
},
167+
"unit": "MONTHLY"
168+
}
169+
]}')
170+
171+
result = described_class.new(service_plan).to_hash.deep_symbolize_keys
172+
173+
expect(result[:costs][0][:amount]).to eq(649.0)
174+
expect(result[:costs][0][:currency]).to eq('USD')
175+
expect(result[:costs][0][:unit]).to eq('MONTHLY')
176+
177+
expect(result[:costs][1][:amount]).to eq(600.015454)
178+
expect(result[:costs][1][:currency]).to eq('GBP')
179+
expect(result[:costs][1][:unit]).to eq('MONTHLY')
180+
end
181+
182+
it 'handles currency symbols' do
183+
service_plan =
184+
VCAP::CloudController::ServicePlan.make(extra: '{"costs": [
185+
{
186+
"amount": {
187+
"$": 0.06
188+
},
189+
"unit": "Daily"
190+
}
191+
]}')
192+
193+
result = described_class.new(service_plan).to_hash.deep_symbolize_keys
194+
195+
expect(result[:costs][0][:amount]).to eq(0.06)
196+
expect(result[:costs][0][:currency]).to eq('$')
197+
expect(result[:costs][0][:unit]).to eq('Daily')
198+
end
199+
end
200+
201+
context 'when plan has no cost' do
202+
let(:service_plan) do
203+
VCAP::CloudController::ServicePlan.make
204+
end
205+
206+
it 'presents the service plan with cost' do
207+
expect(result[:costs]).to eq([])
208+
end
209+
end
210+
211+
context 'when plan cost is not valid array' do
212+
[
213+
['cost is not an array',
214+
'{
215+
"costs":
216+
{
217+
"amount": {
218+
"usd": 649.0,
219+
"gbp": 600.015454
220+
},
221+
"unit": "MONTHLY"
222+
}
223+
}'
224+
],
225+
['amount missing',
226+
'{
227+
"costs": [
228+
{
229+
"unit": "Weekly"
230+
},
231+
{
232+
"amount": {
233+
"usd": 649.0,
234+
"gbp": 600.015454
235+
},
236+
"unit": "Weekly"
237+
}
238+
]
239+
}'
240+
],
241+
['unit is missing',
242+
'{
243+
"costs": [
244+
{
245+
"amount": {
246+
"usd": 649.0,
247+
"gbp": 600.015454
248+
}
249+
}
250+
]
251+
}'
252+
],
253+
['amount is empty object',
254+
'{
255+
"costs": [
256+
{
257+
"amount": {},
258+
"unit": "Daily"
259+
}
260+
]
261+
}'
262+
],
263+
['amount is not a valid string:float key value pair',
264+
'{
265+
"costs": [
266+
{
267+
"amount": {
268+
"usd": "649.0"
269+
},
270+
"unit": "Weekly"
271+
}
272+
]
273+
}'
274+
]
275+
].each do |scenario, extra|
276+
it "returns empty cost array when #{scenario}" do
277+
service_plan = VCAP::CloudController::ServicePlan.make(extra: extra)
278+
279+
result = described_class.new(service_plan).to_hash.deep_symbolize_keys
280+
281+
expect(result[:costs]).to eq([])
282+
end
283+
end
284+
end
285+
157286
context 'when plan has no `maintenance_info`' do
158287
let(:service_plan) do
159288
VCAP::CloudController::ServicePlan.make

0 commit comments

Comments
 (0)