Skip to content

Commit 11a8beb

Browse files
committed
[service credential bindings] Implement create operation
Close #164
1 parent fb1dd74 commit 11a8beb

4 files changed

Lines changed: 163 additions & 4 deletions

File tree

main/cloudfoundry_client/v3/entities.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ def _patch(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None
143143

144144
def _delete(self, url: str) -> Optional[str]:
145145
response = self.client.delete(url)
146+
return self._location(response)
147+
148+
@staticmethod
149+
def _location(response):
146150
try:
147151
return response.headers["Location"]
148152
except (AttributeError, KeyError):
@@ -152,14 +156,19 @@ def _remove(self, resource_id: str, asynchronous: bool = True) -> Optional[str]:
152156
url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id)
153157
job_location = self._delete(url)
154158
if job_location is not None:
155-
job_url = urlparse(job_location)
156-
job_guid = job_url.path.rsplit("/", 1)[-1]
159+
job_guid = self._extract_job_guid(job_location)
157160
if not asynchronous:
158161
self.client.v3.jobs.wait_for_job_completion(job_guid)
159162
else:
160163
return job_guid
161164
return None
162165

166+
@staticmethod
167+
def _extract_job_guid(job_location):
168+
job_url = urlparse(job_location)
169+
job_guid = job_url.path.rsplit("/", 1)[-1]
170+
return job_guid
171+
163172
def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> PaginateEntities:
164173
url_requested = EntityManager._get_url_with_encoded_params("%s%s" % (self.target_endpoint, requested_path), **kwargs)
165174
for element in self._paginate(url_requested, entity_type):
Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from typing import TYPE_CHECKING
1+
from typing import TYPE_CHECKING, Optional, Union
22

3-
from cloudfoundry_client.v3.entities import EntityManager
3+
from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship
44

55
if TYPE_CHECKING:
66
from cloudfoundry_client.client import CloudFoundryClient
@@ -10,3 +10,42 @@ class ServiceCredentialBindingManager(EntityManager):
1010
def __init__(self, target_endpoint: str, client: "CloudFoundryClient"):
1111
super(ServiceCredentialBindingManager, self).__init__(target_endpoint, client,
1212
"/v3/service_credential_bindings")
13+
14+
def create(
15+
self,
16+
name: str,
17+
service_credential_binding_type: str,
18+
service_instance_guid: str,
19+
application_guid: Optional[str],
20+
parameters: Optional[dict],
21+
meta_labels: Optional[dict],
22+
meta_annotations: Optional[dict],
23+
asynchronous: bool = True,
24+
) -> Union[str, Entity, None]:
25+
data = {
26+
"name": name,
27+
"type": service_credential_binding_type,
28+
"relationships": {"service_instance": ToOneRelationship(service_instance_guid)},
29+
}
30+
if application_guid:
31+
data["relationships"]["app"] = ToOneRelationship(application_guid)
32+
if parameters:
33+
data["parameters"] = parameters
34+
if meta_labels or meta_annotations:
35+
metadata = dict()
36+
if meta_labels:
37+
metadata["labels"] = meta_labels
38+
if meta_annotations:
39+
metadata["annotations"] = meta_annotations
40+
data["metadata"] = metadata
41+
url = "%s%s" % (self.target_endpoint, self.entity_uri)
42+
response = self.client.post(url, json=data)
43+
location = super(ServiceCredentialBindingManager, self)._location(response)
44+
if location:
45+
job_guid = super(ServiceCredentialBindingManager, self)._extract_job_guid(location)
46+
if asynchronous:
47+
return job_guid
48+
else:
49+
self.client.v3.jobs.wait_for_job_completion(job_guid)
50+
return None
51+
return super(ServiceCredentialBindingManager, self)._read_response(response, None)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"guid": "dde5ad2a-d8f4-44dc-a56f-0452d744f1c3",
3+
"created_at": "2015-11-13T17:02:56Z",
4+
"updated_at": "2016-06-08T16:41:26Z",
5+
"name": "some-name",
6+
"type": "app",
7+
"last_operation": {
8+
"type": "create",
9+
"state": "succeeded",
10+
"created_at": "2015-11-13T17:02:56Z",
11+
"updated_at": "2016-06-08T16:41:26Z"
12+
},
13+
"metadata": {
14+
"annotations": {
15+
"foo": "bar"
16+
},
17+
"labels": {
18+
"baz": "qux"
19+
}
20+
},
21+
"relationships": {
22+
"app": {
23+
"data": {
24+
"guid": "74f7c078-0934-470f-9883-4fddss5b8f13"
25+
}
26+
},
27+
"service_instance": {
28+
"data": {
29+
"guid": "8bfe4c1b-9e18-45b1-83be-124163f31f9e"
30+
}
31+
}
32+
},
33+
"links": {
34+
"self": {
35+
"href": "https://api.example.org/v3/service_credential_bindings/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3"
36+
},
37+
"details": {
38+
"href": "https://api.example.org/v3/service_credential_bindings/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3/details"
39+
},
40+
"parameters": {
41+
"href": "https://api.example.org/v3/service_credential_bindings/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3/parameters"
42+
},
43+
"service_instance": {
44+
"href": "https://api.example.org/v3/service_instances/8bfe4c1b-9e18-45b1-83be-124163f31f9e"
45+
},
46+
"app": {
47+
"href": "https://api.example.org/v3/apps/74f7c078-0934-470f-9883-4fddss5b8f13"
48+
}
49+
}
50+
}

test/v3/test_service_credential_bindings.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,67 @@ def setUpClass(cls):
1313
def setUp(self):
1414
self.build_client()
1515

16+
def test_create_managed_service_instance(self):
17+
self.client.post.return_value = self.mock_response(
18+
"/v3/service_credential_bindings", HTTPStatus.ACCEPTED,
19+
dict(Location="https://api.example.org/v3/jobs/af5c57f6-8769-41fa-a499-2c84ed896788")
20+
)
21+
location = self.client.v3.service_credential_bindings.create("some-binding-name", "app",
22+
"7304bc3c-7010-11ea-8840-48bf6bec2d78",
23+
"e0e4417c-74ee-11ea-a604-48bf6bec2d78",
24+
parameters=dict(key1="value1", key2="value2"),
25+
meta_labels=dict(foo="bar"),
26+
meta_annotations=dict(baz="qux"))
27+
28+
self.assertEqual("af5c57f6-8769-41fa-a499-2c84ed896788", location)
29+
self.client.post.assert_called_with(
30+
self.client.post.return_value.url,
31+
json={
32+
"type": "app",
33+
"name": "some-binding-name",
34+
"relationships": {
35+
"service_instance": {
36+
"data": {"guid": "7304bc3c-7010-11ea-8840-48bf6bec2d78"}
37+
},
38+
"app": {
39+
"data": {"guid": "e0e4417c-74ee-11ea-a604-48bf6bec2d78"}
40+
}
41+
},
42+
"parameters": {"key1": "value1", "key2": "value2"},
43+
"metadata": {"labels": {"foo": "bar"}, "annotations": {"baz": "qux"}},
44+
},
45+
)
46+
47+
def test_create_user_provided_service_instance(self):
48+
self.client.post.return_value = self.mock_response(
49+
"/v3/service_credential_bindings", HTTPStatus.ACCEPTED,
50+
None,
51+
"v3", "service_credential_bindings", "POST_response.json"
52+
)
53+
result = self.client.v3.service_credential_bindings.create("some-binding-name", "key",
54+
"7304bc3c-7010-11ea-8840-48bf6bec2d78",
55+
None,
56+
parameters=dict(key1="value1", key2="value2"),
57+
meta_labels=dict(foo="bar"),
58+
meta_annotations=dict(baz="qux"))
59+
self.assertIsNotNone(result)
60+
self.assertEqual("some-name", result["name"])
61+
self.assertIsInstance(result, Entity)
62+
self.client.post.assert_called_with(
63+
self.client.post.return_value.url,
64+
json={
65+
"type": "key",
66+
"name": "some-binding-name",
67+
"relationships": {
68+
"service_instance": {
69+
"data": {"guid": "7304bc3c-7010-11ea-8840-48bf6bec2d78"}
70+
}
71+
},
72+
"parameters": {"key1": "value1", "key2": "value2"},
73+
"metadata": {"labels": {"foo": "bar"}, "annotations": {"baz": "qux"}},
74+
},
75+
)
76+
1677
def test_list(self):
1778
self.client.get.return_value = self.mock_response(
1879
"/v3/service_credential_bindings", HTTPStatus.OK, None, "v3", "service_credential_bindings",

0 commit comments

Comments
 (0)