Skip to content

Commit 13f3a59

Browse files
authored
feat(Resource Controller): regen to add cancel_lastop_resource_instance method (#165)
* New method added: `cancel_lastop_resource_instance` * Support for credentials redaction added - `Credentials` class updated accordingly
1 parent a51baca commit 13f3a59

File tree

4 files changed

+160
-2
lines changed

4 files changed

+160
-2
lines changed

examples/test_resource_controller_v2_examples.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,8 @@ def test_get_resource_binding_example(self):
364364
resource_binding = resource_controller_service.get_resource_binding(
365365
id=binding_guid
366366
).get_result()
367-
367+
if resource_binding.get('credentials') and resource_binding.get('credentials').get('REDACTED'):
368+
print("Credentials are redacted with code:", resource_binding.get('credentials').get('REDACTED'), ".The User doesn't have the correct access to view the credentials. Refer to the API documentation for additional details.")
368369
print(json.dumps(resource_binding, indent=2))
369370

370371
# end-get_resource_binding
@@ -477,6 +478,8 @@ def test_get_resource_key_example(self):
477478
resource_key = resource_controller_service.get_resource_key(
478479
id=instance_key_guid
479480
).get_result()
481+
if resource_key.get('credentials') and resource_key.get('credentials').get('REDACTED'):
482+
print("Credentials are redacted with code:", resource_key.get('credentials').get('REDACTED'), ".The User doesn't have the correct access to view the credentials. Refer to the API documentation for additional details.")
480483

481484
print(json.dumps(resource_key, indent=2))
482485

@@ -721,6 +724,28 @@ def test_run_reclamation_action_example(self):
721724
except ApiException as e:
722725
pytest.fail(str(e))
723726

727+
@needscredentials
728+
def test_cancel_lastop_resource_instance_example(self):
729+
"""
730+
cancel_lastop_resource_instance request example
731+
"""
732+
try:
733+
print('\ncancel_lastop_resource_instance() result:')
734+
# begin-cancel_lastop_resource_instance
735+
736+
resource_instance = resource_controller_service.cancel_lastop_resource_instance(
737+
id=instance_guid
738+
).get_result()
739+
print(json.dumps(resource_instance, indent=2))
740+
741+
# end-cancel_lastop_resource_instance
742+
743+
except ApiException as e:
744+
if e.message == "The instance is not cancelable.":
745+
print("The instance is not cancelable")
746+
else:
747+
pytest.fail(str(e))
748+
724749
# endregion
725750
##############################################################################
726751
# End of Examples for Service: ResourceControllerV2

ibm_platform_services/resource_controller_v2.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,44 @@ def unlock_resource_instance(self,
598598
response = self.send(request)
599599
return response
600600

601+
def cancel_lastop_resource_instance(self,
602+
id: str,
603+
**kwargs
604+
) -> DetailedResponse:
605+
"""
606+
Cancel the in progress last operation of the resource instance.
607+
608+
Cancel the in progress last operation of the resource instance. After successful
609+
cancellation, the resource instance is removed.
610+
611+
:param str id: The resource instance URL-encoded CRN or GUID.
612+
:param dict headers: A `dict` containing the request headers
613+
:return: A `DetailedResponse` containing the result, headers and HTTP status code.
614+
:rtype: DetailedResponse with `dict` result representing a `ResourceInstance` object
615+
"""
616+
617+
if id is None:
618+
raise ValueError('id must be provided')
619+
headers = {}
620+
sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME,
621+
service_version='V2',
622+
operation_id='cancel_lastop_resource_instance')
623+
headers.update(sdk_headers)
624+
625+
if 'headers' in kwargs:
626+
headers.update(kwargs.get('headers'))
627+
headers['Accept'] = 'application/json'
628+
629+
path_param_keys = ['id']
630+
path_param_values = self.encode_path_vars(id)
631+
path_param_dict = dict(zip(path_param_keys, path_param_values))
632+
url = '/v2/resource_instances/{id}/last_operation'.format(**path_param_dict)
633+
request = self.prepare_request(method='DELETE',
634+
url=url,
635+
headers=headers)
636+
637+
response = self.send(request)
638+
return response
601639
#########################
602640
# Resource Keys
603641
#########################
@@ -1592,6 +1630,11 @@ class Credentials():
15921630
"""
15931631
The credentials for a resource.
15941632
1633+
:attr str redacted: (optional) If present, the user doesn't have the correct
1634+
access to view the credentials and the details are redacted. The string value
1635+
identifies the level of access that's required to view the credential. For
1636+
additional information, see [viewing a
1637+
credential](https://cloud.ibm.com/docs/account?topic=account-service_credentials&interface=ui#viewing-credentials-ui).
15951638
:attr str apikey: (optional) The API key for the credentials.
15961639
:attr str iam_apikey_description: (optional) The optional description of the API
15971640
key.
@@ -1603,10 +1646,11 @@ class Credentials():
16031646
"""
16041647

16051648
# The set of defined properties for the class
1606-
_properties = frozenset(['apikey', 'iam_apikey_description', 'iam_apikey_name', 'iam_role_crn', 'iam_serviceid_crn'])
1649+
_properties = frozenset(['REDACTED', 'apikey', 'iam_apikey_description', 'iam_apikey_name', 'iam_role_crn', 'iam_serviceid_crn'])
16071650

16081651
def __init__(self,
16091652
*,
1653+
redacted: str = None,
16101654
apikey: str = None,
16111655
iam_apikey_description: str = None,
16121656
iam_apikey_name: str = None,
@@ -1616,6 +1660,11 @@ def __init__(self,
16161660
"""
16171661
Initialize a Credentials object.
16181662
1663+
:param str redacted: (optional) If present, the user doesn't have the
1664+
correct access to view the credentials and the details are redacted. The
1665+
string value identifies the level of access that's required to view the
1666+
credential. For additional information, see [viewing a
1667+
credential](https://cloud.ibm.com/docs/account?topic=account-service_credentials&interface=ui#viewing-credentials-ui).
16191668
:param str apikey: (optional) The API key for the credentials.
16201669
:param str iam_apikey_description: (optional) The optional description of
16211670
the API key.
@@ -1626,6 +1675,7 @@ def __init__(self,
16261675
service ID of the credentials.
16271676
:param **kwargs: (optional) Any additional properties.
16281677
"""
1678+
self.redacted = redacted
16291679
self.apikey = apikey
16301680
self.iam_apikey_description = iam_apikey_description
16311681
self.iam_apikey_name = iam_apikey_name
@@ -1638,6 +1688,8 @@ def __init__(self,
16381688
def from_dict(cls, _dict: Dict) -> 'Credentials':
16391689
"""Initialize a Credentials object from a json dictionary."""
16401690
args = {}
1691+
if 'REDACTED' in _dict:
1692+
args['redacted'] = _dict.get('REDACTED')
16411693
if 'apikey' in _dict:
16421694
args['apikey'] = _dict.get('apikey')
16431695
if 'iam_apikey_description' in _dict:
@@ -1659,6 +1711,8 @@ def _from_dict(cls, _dict):
16591711
def to_dict(self) -> Dict:
16601712
"""Return a json dictionary representing this model."""
16611713
_dict = {}
1714+
if hasattr(self, 'redacted') and self.redacted is not None:
1715+
_dict['REDACTED'] = self.redacted
16621716
if hasattr(self, 'apikey') and self.apikey is not None:
16631717
_dict['apikey'] = self.apikey
16641718
if hasattr(self, 'iam_apikey_description') and self.iam_apikey_description is not None:

test/integration/test_resource_controller_v2.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,6 +1292,20 @@ def test_52_reclaim_resource_instance(self):
12921292
assert result.get('state') == 'RECLAIMING'
12931293

12941294
time.sleep(20)
1295+
1296+
def test_53_cancel_last_operation(self):
1297+
1298+
customHeaders = {}
1299+
customHeaders["Transaction-Id"] = "rc-sdk-python-test53-" + \
1300+
self.transactionId
1301+
try:
1302+
response = self.service.cancel_lastop_resource_instance(
1303+
id=self.testInstanceGuid,
1304+
headers=customHeaders)
1305+
assert response is not None
1306+
except ApiException as e:
1307+
assert e.message == "The instance is not cancelable."
1308+
12951309

12961310
# Commented because redis timeouts cause intermittent failure
12971311

test/unit/test_resource_controller_v2.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,71 @@ def test_unlock_resource_instance_value_error(self):
900900
with pytest.raises(ValueError):
901901
_service.unlock_resource_instance(**req_copy)
902902

903+
class TestCancelLastopResourceInstance():
904+
"""
905+
Test Class for cancel_lastop_resource_instance
906+
"""
907+
908+
def preprocess_url(self, request_url: str):
909+
"""
910+
Preprocess the request URL to ensure the mock response will be found.
911+
"""
912+
if re.fullmatch('.*/+', request_url) is None:
913+
return request_url
914+
else:
915+
return re.compile(request_url.rstrip('/') + '/+')
916+
@responses.activate
917+
def test_cancel_lastop_resource_instance_all_params(self):
918+
"""
919+
cancel_lastop_resource_instance()
920+
"""
921+
# Set up mock
922+
url = self.preprocess_url(_base_url + '/v2/resource_instances/testString/last_operation')
923+
mock_response = '{"id": "id", "guid": "guid", "url": "url", "created_at": "2019-01-01T12:00:00.000Z", "updated_at": "2019-01-01T12:00:00.000Z", "deleted_at": "2019-01-01T12:00:00.000Z", "created_by": "created_by", "updated_by": "updated_by", "deleted_by": "deleted_by", "scheduled_reclaim_at": "2019-01-01T12:00:00.000Z", "restored_at": "2019-01-01T12:00:00.000Z", "restored_by": "restored_by", "scheduled_reclaim_by": "scheduled_reclaim_by", "name": "name", "region_id": "region_id", "account_id": "account_id", "reseller_channel_id": "reseller_channel_id", "resource_plan_id": "resource_plan_id", "resource_group_id": "resource_group_id", "resource_group_crn": "resource_group_crn", "target_crn": "target_crn", "parameters": {"mapKey": "anyValue"}, "allow_cleanup": false, "crn": "crn", "state": "active", "type": "type", "sub_type": "sub_type", "resource_id": "resource_id", "dashboard_url": "dashboard_url", "last_operation": {"type": "type", "state": "in progress", "sub_type": "sub_type", "async": true, "description": "description", "reason_code": "reason_code", "poll_after": 10, "cancelable": true, "poll": true}, "resource_aliases_url": "resource_aliases_url", "resource_bindings_url": "resource_bindings_url", "resource_keys_url": "resource_keys_url", "plan_history": [{"resource_plan_id": "resource_plan_id", "start_date": "2019-01-01T12:00:00.000Z", "requestor_id": "requestor_id"}], "migrated": true, "extensions": {"mapKey": "anyValue"}, "controlled_by": "controlled_by", "locked": true}'
924+
responses.add(responses.DELETE,
925+
url,
926+
body=mock_response,
927+
content_type='application/json',
928+
status=200)
929+
930+
# Set up parameter values
931+
id = 'testString'
932+
933+
# Invoke method
934+
response = _service.cancel_lastop_resource_instance(
935+
id,
936+
headers={}
937+
)
938+
939+
# Check for correct operation
940+
assert len(responses.calls) == 1
941+
assert response.status_code == 200
942+
943+
@responses.activate
944+
def test_cancel_lastop_resource_instance_value_error(self):
945+
"""
946+
test_cancel_lastop_resource_instance_value_error()
947+
"""
948+
# Set up mock
949+
url = self.preprocess_url(_base_url + '/v2/resource_instances/testString/last_operation')
950+
mock_response = '{"id": "id", "guid": "guid", "url": "url", "created_at": "2019-01-01T12:00:00.000Z", "updated_at": "2019-01-01T12:00:00.000Z", "deleted_at": "2019-01-01T12:00:00.000Z", "created_by": "created_by", "updated_by": "updated_by", "deleted_by": "deleted_by", "scheduled_reclaim_at": "2019-01-01T12:00:00.000Z", "restored_at": "2019-01-01T12:00:00.000Z", "restored_by": "restored_by", "scheduled_reclaim_by": "scheduled_reclaim_by", "name": "name", "region_id": "region_id", "account_id": "account_id", "reseller_channel_id": "reseller_channel_id", "resource_plan_id": "resource_plan_id", "resource_group_id": "resource_group_id", "resource_group_crn": "resource_group_crn", "target_crn": "target_crn", "parameters": {"mapKey": "anyValue"}, "allow_cleanup": false, "crn": "crn", "state": "active", "type": "type", "sub_type": "sub_type", "resource_id": "resource_id", "dashboard_url": "dashboard_url", "last_operation": {"type": "type", "state": "in progress", "sub_type": "sub_type", "async": true, "description": "description", "reason_code": "reason_code", "poll_after": 10, "cancelable": true, "poll": true}, "resource_aliases_url": "resource_aliases_url", "resource_bindings_url": "resource_bindings_url", "resource_keys_url": "resource_keys_url", "plan_history": [{"resource_plan_id": "resource_plan_id", "start_date": "2019-01-01T12:00:00.000Z", "requestor_id": "requestor_id"}], "migrated": true, "extensions": {"mapKey": "anyValue"}, "controlled_by": "controlled_by", "locked": true}'
951+
responses.add(responses.DELETE,
952+
url,
953+
body=mock_response,
954+
content_type='application/json',
955+
status=200)
956+
957+
# Set up parameter values
958+
id = 'testString'
959+
960+
# Pass in all but one required param and check for a ValueError
961+
req_param_dict = {
962+
"id": id,
963+
}
964+
for param in req_param_dict.keys():
965+
req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()}
966+
with pytest.raises(ValueError):
967+
_service.cancel_lastop_resource_instance(**req_copy)
903968

904969

905970
# endregion

0 commit comments

Comments
 (0)