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

Commit 0a3624f

Browse files
Derik Evangelistapivotal-marcela-campo
authored andcommitted
v3(route-bindings): update metadata
[#174252441](https://www.pivotaltracker.com/story/show/174252441)
1 parent 1d2fa76 commit 0a3624f

File tree

5 files changed

+207
-4
lines changed

5 files changed

+207
-4
lines changed

app/controllers/v3/service_route_bindings_controller.rb

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
require 'cloud_controller/paging/sequel_paginator'
1414

1515
class ServiceRouteBindingsController < ApplicationController
16-
before_action :set_route_binding, only: [:show, :parameters, :destroy]
16+
before_action :set_route_binding, except: [:create]
1717

1818
def index
1919
message = list_message
@@ -93,6 +93,19 @@ def parameters
9393
unprocessable!('There is an operation in progress for the service route binding.')
9494
end
9595

96+
def update
97+
route_binding_not_found! unless @route_binding.present? && can_read_space?(@route_binding.route.space)
98+
unauthorized! unless can_write_space?(@route_binding.route.space)
99+
100+
unprocessable!('The service route binding is being deleted') if delete_in_progress?(@route_binding)
101+
102+
message = MetadataUpdateMessage.new(hashed_params[:body])
103+
unprocessable!(message.errors.full_messages) unless message.valid?
104+
105+
updated_route_binding = TransactionalMetadataUpdate.update(@route_binding, message)
106+
render status: :ok, json: Presenters::V3::ServiceRouteBindingPresenter.new(updated_route_binding)
107+
end
108+
96109
private
97110

98111
AVAILABLE_DECORATORS = [
@@ -227,4 +240,8 @@ def already_exists!
227240
def set_route_binding
228241
@route_binding = RouteBinding.first(guid: hashed_params[:guid])
229242
end
243+
244+
def delete_in_progress?(binding)
245+
binding.operation_in_progress? && binding.last_operation.type == 'delete'
246+
end
230247
end

config/routes.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@
197197
# service_route_bindings
198198
resources :service_route_bindings,
199199
param: :guid,
200-
only: [:show, :create, :index, :destroy] do
200+
only: [:show, :create, :index, :update, :destroy] do
201201
member do
202202
get :parameters
203203
end
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
### Update a service route binding
2+
3+
```
4+
Example Request
5+
```
6+
7+
```shell
8+
curl "https://api.example.org/v3/service_route_bindings/[guid]" \
9+
-X PATCH \
10+
-H "Authorization: bearer [token]" \
11+
-H "Content-type: application/json" \
12+
-d '{
13+
"metadata": {
14+
"labels": {"key": "value"},
15+
"annotations": {"note": "detailed information"}
16+
}
17+
}'
18+
```
19+
20+
```
21+
Example Response
22+
```
23+
24+
```http
25+
HTTP/1.1 200 OK
26+
Content-Type: application/json
27+
28+
<%= yield_content :single_service_route_binding %>
29+
```
30+
31+
This endpoint updates a service route bindings with labels and annotations.
32+
33+
#### Definition
34+
`PATCH /v3/service_route_bindings/:guid`
35+
36+
#### Optional parameters
37+
38+
Name | Type | Description
39+
---- | ---- | -----------
40+
**metadata.labels** | [_label object_](#labels) | Labels applied to the service route binding
41+
**metadata.annotations** | [_annotation object_](#annotations) | Annotations applied to the service route binding
42+
43+
#### Permitted roles
44+
|
45+
--- | ---
46+
Admin |
47+
Space Developer (only for service plans from space-scoped brokers) |
48+

docs/v3/source/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,9 +370,10 @@ includes:
370370
- experimental_resources/service_credential_bindings/parameters
371371
- experimental_resources/service_route_bindings/header
372372
- experimental_resources/service_route_bindings/object
373-
- experimental_resources/service_route_bindings/create
374-
- experimental_resources/service_route_bindings/list
375373
- experimental_resources/service_route_bindings/get
374+
- experimental_resources/service_route_bindings/list
375+
- experimental_resources/service_route_bindings/create
376+
- experimental_resources/service_route_bindings/update
376377
- experimental_resources/service_route_bindings/delete
377378
- experimental_resources/service_route_bindings/parameters
378379
- experimental_resources/service_instances/header

spec/request/service_route_bindings_spec.rb

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,6 +1389,143 @@
13891389
end
13901390
end
13911391

1392+
describe 'PATCH /v3/service_plans/:guid' do
1393+
let(:offering) { VCAP::CloudController::Service.make(requires: ['route_forwarding'], bindings_retrievable: true) }
1394+
let(:plan) { VCAP::CloudController::ServicePlan.make(service: offering) }
1395+
let(:service_instance) { VCAP::CloudController::ManagedServiceInstance.make(space: space, service_plan: plan, route_service_url: route_service_url) }
1396+
let(:route) { VCAP::CloudController::Route.make(space: space) }
1397+
let(:app_model) { VCAP::CloudController::AppModel.make(space: space) }
1398+
let(:binding) { bind_service_to_route(service_instance, route) }
1399+
let(:guid) { binding.guid }
1400+
let(:labels) { { potato: 'sweet' } }
1401+
let(:annotations) { { style: 'mashed', amount: 'all' } }
1402+
let(:update_request_body) {
1403+
{
1404+
metadata: {
1405+
labels: labels,
1406+
annotations: annotations
1407+
}
1408+
}
1409+
}
1410+
1411+
let(:api_call) { lambda { |user_headers| patch "/v3/service_route_bindings/#{guid}", update_request_body.to_json, user_headers } }
1412+
1413+
it 'can update labels and annotations' do
1414+
api_call.call(admin_headers)
1415+
expect(last_response).to have_status_code(200)
1416+
expect(parsed_response.deep_symbolize_keys).to include(update_request_body)
1417+
end
1418+
1419+
context 'when some labels are invalid' do
1420+
let(:labels) { { potato: 'sweet invalid potato' } }
1421+
1422+
it 'returns a proper failure' do
1423+
api_call.call(admin_headers)
1424+
1425+
expect(last_response).to have_status_code(422)
1426+
expect(parsed_response['errors'][0]['detail']).to match(/Metadata [\w\s]+ error/)
1427+
end
1428+
end
1429+
1430+
context 'when some annotations are invalid' do
1431+
let(:annotations) { { '/style' => 'sweet invalid style' } }
1432+
1433+
it 'returns a proper failure' do
1434+
api_call.call(admin_headers)
1435+
1436+
expect(last_response).to have_status_code(422)
1437+
expect(parsed_response['errors'][0]['detail']).to match(/Metadata [\w\s]+ error/)
1438+
end
1439+
end
1440+
1441+
context 'when the route binding does not exist' do
1442+
let(:guid) { 'moonlight-sonata' }
1443+
1444+
it 'returns a not found error' do
1445+
api_call.call(admin_headers)
1446+
expect(last_response).to have_status_code(404)
1447+
end
1448+
end
1449+
1450+
context 'when the route binding is being created' do
1451+
before do
1452+
binding.save_with_new_operation(
1453+
{},
1454+
{ type: 'create', state: 'in progress', broker_provided_operation: 'some-info' }
1455+
)
1456+
end
1457+
1458+
before do
1459+
api_call.call(admin_headers)
1460+
expect(last_response).to have_status_code(200)
1461+
binding.reload
1462+
end
1463+
1464+
it 'can still update metadata' do
1465+
expect(binding).to have_labels({ prefix: nil, key: 'potato', value: 'sweet' })
1466+
expect(binding).to have_annotations({ prefix: nil, key: 'style', value: 'mashed' }, { prefix: nil, key: 'amount', value: 'all' })
1467+
end
1468+
1469+
it 'does not update last operation' do
1470+
expect(binding.last_operation.type).to eq('create')
1471+
expect(binding.last_operation.state).to eq('in progress')
1472+
expect(binding.last_operation.broker_provided_operation).to eq('some-info')
1473+
end
1474+
end
1475+
1476+
context 'when the route binding is being deleted' do
1477+
before do
1478+
binding.save_with_new_operation(
1479+
{},
1480+
{ type: 'delete', state: 'in progress', broker_provided_operation: 'some-info' }
1481+
)
1482+
end
1483+
1484+
it 'responds with a 422' do
1485+
api_call.call(admin_headers)
1486+
expect(last_response).to have_status_code(422)
1487+
end
1488+
1489+
it 'does not update last operation' do
1490+
api_call.call(admin_headers)
1491+
binding.reload
1492+
expect(binding.last_operation.type).to eq('delete')
1493+
expect(binding.last_operation.state).to eq('in progress')
1494+
expect(binding.last_operation.broker_provided_operation).to eq('some-info')
1495+
end
1496+
end
1497+
1498+
context 'permissions' do
1499+
let(:response_object) {
1500+
expected_json(
1501+
binding_guid: binding.guid,
1502+
route_service_url: route_service_url,
1503+
service_instance_guid: service_instance.guid,
1504+
route_guid: route.guid,
1505+
last_operation_type: 'create',
1506+
last_operation_state: 'successful',
1507+
include_params_link: service_instance.managed_instance?,
1508+
metadata: {
1509+
labels: labels,
1510+
annotations: annotations
1511+
}
1512+
)
1513+
}
1514+
1515+
let(:expected_codes_and_responses) do
1516+
Hash.new(code: 403).tap do |h|
1517+
h['admin'] = { code: 200, response_object: response_object }
1518+
h['space_developer'] = { code: 200, response_object: response_object }
1519+
h['no_role'] = { code: 404 }
1520+
h['org_auditor'] = { code: 404 }
1521+
h['org_billing_manager'] = { code: 404 }
1522+
end
1523+
end
1524+
1525+
it_behaves_like 'permissions for single object endpoint', ALL_PERMISSIONS
1526+
end
1527+
end
1528+
13921529
let(:user) { VCAP::CloudController::User.make }
13931530
let(:org) { VCAP::CloudController::Organization.make }
13941531
let(:space) { VCAP::CloudController::Space.make(organization: org) }

0 commit comments

Comments
 (0)