Best Python code snippet using localstack_python
test_apigw_method.py
Source:test_apigw_method.py
1#!/usr/bin/python2# TODO: License goes here3import library.apigw_method as apigw_method4from library.apigw_method import ApiGwMethod, InvalidInputError5import mock6from mock import patch7from mock import create_autospec8from mock import ANY9import unittest10import boto11from botocore.exceptions import BotoCoreError, ClientError12import copy13def merge(a, b):14 for key in b:15 if key in a:16 if isinstance(a[key], dict) and isinstance(b[key], dict):17 merge(a[key], b[key])18 else:19 a[key] = b[key]20 else:21 a[key] = b[key]22 return a23def purge(a, keys):24 for key in keys:25 parts = key.split('.')26 k = parts[0]27 newkey = ".".join(parts[1:])28 if newkey:29 purge(a[k], [newkey])30 else:31 a.pop(key, None)32 return a33def mock_args(*args, **kwargs):34 return {'mock': 'args'}35class TestApiGwMethod(unittest.TestCase):36 def setUp(self):37 self.module = mock.MagicMock()38 self.module.check_mode = False39 self.module.exit_json = mock.MagicMock()40 self.module.fail_json = mock.MagicMock()41 self.method = ApiGwMethod(self.module)42 self.method.client = mock.MagicMock()43 self.method.client.get_method = mock.MagicMock()44 self.method.client.put_method = mock.MagicMock()45 self.method.client.put_method_response = mock.MagicMock()46 self.method.client.put_integration = mock.MagicMock()47 self.method.client.put_integration_response = mock.MagicMock()48 self.method.module.params = {49 'rest_api_id': 'restid',50 'resource_id': 'rsrcid',51 'name': 'GET',52 'authorization_type': 'NONE',53 'api_key_required': False,54 'request_params': [],55 'method_integration': {'integration_type': 'value'},56 'method_responses': [],57 'state': 'present'58 }59 reload(apigw_method)60### boto3 tests61 def test_boto_module_not_found(self):62 # Setup Mock Import Function63 import __builtin__ as builtins64 real_import = builtins.__import__65 def mock_import(name, *args):66 if name == 'boto': raise ImportError67 return real_import(name, *args)68 with mock.patch('__builtin__.__import__', side_effect=mock_import):69 reload(apigw_method)70 ApiGwMethod(self.module)71 self.module.fail_json.assert_called_with(msg='boto and boto3 are required for this module')72 def test_boto3_module_not_found(self):73 # Setup Mock Import Function74 import __builtin__ as builtins75 real_import = builtins.__import__76 def mock_import(name, *args):77 if name == 'boto3': raise ImportError78 return real_import(name, *args)79 with mock.patch('__builtin__.__import__', side_effect=mock_import):80 reload(apigw_method)81 ApiGwMethod(self.module)82 self.module.fail_json.assert_called_with(msg='boto and boto3 are required for this module')83 @patch.object(apigw_method, 'boto3')84 def test_boto3_client_properly_instantiated(self, mock_boto):85 ApiGwMethod(self.module)86 mock_boto.client.assert_called_once_with('apigateway')87### end boto3 tests88### Find tests89 @patch.object(ApiGwMethod, '_update_method', return_value=[None, None])90 @patch.object(ApiGwMethod, '_create_method', return_value=[None, None])91 def test_process_request_gets_method_on_invocation(self, mock_create, mock_update):92 self.method.client.get_method=mock.MagicMock(return_value='response')93 self.method.process_request()94 self.method.client.get_method.assert_called_once_with(95 restApiId='restid',96 resourceId='rsrcid',97 httpMethod='GET'98 )99 self.assertEqual('response', self.method.method)100 @patch.object(ApiGwMethod, '_create_method', return_value=[None, None])101 def test_process_request_sets_method_result_to_None_when_get_method_throws_not_found(self, mock_create):102 self.method.client.get_method=mock.MagicMock(103 side_effect=ClientError({'Error': {'Code': 'x NotFoundException x'}}, 'xxx'))104 self.method.process_request()105 self.method.client.get_method.assert_called_once_with(106 restApiId='restid',107 resourceId='rsrcid',108 httpMethod='GET'109 )110 self.assertIsNone(self.method.method)111 @patch.object(ApiGwMethod, '_create_method', return_value=[None, None])112 def test_process_request_calls_fail_json_when_ClientError_is_not_NotFoundException(self, mock_create):113 self.method.client.get_method=mock.MagicMock(114 side_effect=ClientError({'Error': {'Code': 'boom', 'Message': 'error'}}, 'xxx'))115 self.method.process_request()116 self.method.client.get_method.assert_called_once_with(117 restApiId='restid',118 resourceId='rsrcid',119 httpMethod='GET'120 )121 self.method.module.fail_json.assert_called_once_with(msg='Error calling boto3 get_method: An error occurred (boom) when calling the xxx operation: error')122 @patch.object(ApiGwMethod, '_create_method', return_value=[None, None])123 def test_process_request_calls_fail_json_when_get_method_throws_other_boto_core_error(self, mock_create):124 self.method.client.get_method=mock.MagicMock(side_effect=BotoCoreError())125 self.method.process_request()126 self.method.client.get_method.assert_called_once_with(127 restApiId='restid',128 resourceId='rsrcid',129 httpMethod='GET'130 )131 self.method.module.fail_json.assert_called_once_with(msg='Error calling boto3 get_method: An unspecified error occurred')132### End find teste133### Delete tests134 @patch.object(ApiGwMethod, '_find_method', return_value=True)135 def test_process_request_deletes_method_when_method_is_present(self, mock_find):136 self.method.client.delete_method = mock.MagicMock()137 self.method.module.params['state'] = 'absent'138 self.method.process_request()139 self.method.client.delete_method.assert_called_once_with(140 restApiId='restid',141 resourceId='rsrcid',142 httpMethod='GET'143 )144 self.method.module.exit_json.assert_called_once_with(changed=True, method=None)145 @patch.object(ApiGwMethod, '_find_method', return_value=True)146 def test_process_skips_delete_and_returns_true_when_check_mode_enabled_and_method_exists(self, mock_find):147 self.method.client.delete_method = mock.MagicMock()148 self.method.module.params['state'] = 'absent'149 self.method.module.check_mode = True150 self.method.process_request()151 self.assertEqual(0, self.method.client.delete_method.call_count)152 self.method.module.exit_json.assert_called_once_with(changed=True, method=None)153 @patch.object(ApiGwMethod, '_find_method', return_value=True)154 def test_process_request_calls_fail_json_when_delete_method_throws_error(self, mock_find):155 self.method.client.delete_method = mock.MagicMock(side_effect=BotoCoreError())156 self.method.module.params['state'] = 'absent'157 self.method.process_request()158 self.method.client.delete_method.assert_called_once_with(159 restApiId='restid',160 resourceId='rsrcid',161 httpMethod='GET'162 )163 self.method.module.fail_json.assert_called_once_with(164 msg='Error calling boto3 delete_method: An unspecified error occurred')165 @patch.object(ApiGwMethod, '_find_method', return_value=None)166 def test_process_request_skips_delete_when_method_is_absent(self, mock_find):167 self.method.client.delete_method = mock.MagicMock()168 self.method.module.params['state'] = 'absent'169 self.method.process_request()170 self.assertEqual(0, self.method.client.delete_method.call_count)171 self.method.module.exit_json.assert_called_once_with(changed=False, method=None)172### End delete173### Update174 @patch('library.apigw_method.put_integration', mock_args)175 @patch.object(ApiGwMethod, 'validate_params')176 @patch.object(ApiGwMethod, '_find_method')177 def test_process_request_calls_update_method_when_present_and_changed(self, mock_find, mock_vp):178 mock_find.return_value = {179 'apiKeyRequired': False,180 'authorizationType': 'NONE',181 'httpMethod': 'GET',182 'requestParameters': {'method.request.querystring.qs_test': False, 'method.request.header.frank': True}183 }184 self.method.module.params = {185 'rest_api_id': 'restid',186 'resource_id': 'rsrcid',187 'authorization_type': 'custom',188 'authorizer_id': 'xxx',189 'name': 'GET',190 'api_key_required': True,191 'request_models': [192 { 'content_type': 'application/json', 'model': 'json' },193 { 'content_type': 'application/pdf', 'model': 'pdf' },194 ],195 'request_params': [196 {'name': 'bob', 'location': 'path', 'param_required': True},197 {'name': 'frank', 'location': 'header', 'param_required': False},198 ],199 'state': 'present'200 }201 expected_patch_ops = [202 {'op': 'replace', 'path': '/apiKeyRequired', 'value': 'True'},203 {'op': 'replace', 'path': '/authorizationType', 'value': 'custom'},204 {'op': 'replace', 'path': '/authorizerId', 'value': 'xxx'},205 {'op': 'add', 'path': '/requestParameters/method.request.path.bob', 'value': 'True'},206 {'op': 'add', 'path': '/requestModels/application~1json', 'value': 'json' },207 {'op': 'add', 'path': '/requestModels/application~1pdf', 'value': 'pdf'},208 {'op': 'remove', 'path': '/requestParameters/method.request.querystring.qs_test'},209 {'op': 'replace', 'path': '/requestParameters/method.request.header.frank', 'value': 'False'},210 ]211 self.method.process_request()212 self.method.client.update_method.assert_called_once_with(213 restApiId='restid',214 resourceId='rsrcid',215 httpMethod='GET',216 patchOperations=mock.ANY217 )218 self.assertItemsEqual(expected_patch_ops, self.method.client.update_method.call_args[1]['patchOperations'])219 @patch('library.apigw_method.put_integration', mock_args)220 @patch.object(ApiGwMethod, 'validate_params')221 @patch.object(ApiGwMethod, '_find_method')222 def test_process_request_calls_update_method_with_patch_operations_to_delete_all_method_request_models(self, mock_find, mock_vp):223 mock_find.return_value = {224 'httpMethod': 'GET',225 'requestModels': {226 'application/json': '{}',227 'application/pdf': '{}'228 }229 }230 self.method.module.params = {231 'rest_api_id': 'restid',232 'resource_id': 'rsrcid',233 'name': 'GET',234 'state': 'present'235 }236 expected_patch_ops = [237 {'op': 'remove', 'path': '/requestModels/application~1json'},238 {'op': 'remove', 'path': '/requestModels/application~1pdf'}239 ]240 self.method.process_request()241 self.assertItemsEqual(expected_patch_ops, self.method.client.update_method.call_args[1]['patchOperations'])242 @patch('library.apigw_method.put_integration', mock_args)243 @patch.object(ApiGwMethod, 'validate_params')244 @patch.object(ApiGwMethod, '_find_method')245 def test_process_request_calls_update_method_with_patch_operations_to_add_update_or_remove_request_models_for_method(self, mock_find, mock_vp):246 mock_find.return_value = {247 'httpMethod': 'GET',248 'requestModels': {249 'application/json': '{}',250 'application/gson': 'some_value',251 'application/octet-stream': 'octet'252 }253 }254 self.method.module.params = {255 'rest_api_id': 'restid',256 'resource_id': 'rsrcid',257 'name': 'GET',258 'state': 'present',259 'request_models': [260 { 'content_type': 'application/json', 'model': 'new_value' },261 { 'content_type': 'application/pdf', 'model': 'pdf' }262 ]263 }264 expected_patch_ops = [265 {'op': 'remove', 'path': '/requestModels/application~1gson'},266 {'op': 'remove', 'path': '/requestModels/application~1octet-stream'},267 {'op': 'replace', 'path': '/requestModels/application~1json', 'value': 'new_value'},268 {'op': 'add', 'path': '/requestModels/application~1pdf', 'value': 'pdf'}269 ]270 self.method.process_request()271 self.assertItemsEqual(expected_patch_ops, self.method.client.update_method.call_args[1]['patchOperations'])272 @patch('library.apigw_method.put_integration', mock_args)273 @patch.object(ApiGwMethod, 'validate_params')274 @patch.object(ApiGwMethod, '_find_method', return_value={})275 def test_process_request_skips_update_and_returns_true_when_check_mode_set(self, mock_find, mock_vp):276 mock_find.return_value = {277 'apiKeyRequired': False,278 'authorizationType': 'NONE',279 'httpMethod': 'GET',280 'requestParameters': {},281 'methodIntegration': {}282 }283 self.method.module.params = {284 'rest_api_id': 'restid',285 'resource_id': 'rsrcid',286 'name': 'GET',287 'api_key_required': True,288 'authorizer_id': 'authid',289 'method_responses': [{'status_code': 202}],290 'integration_responses': [291 {'status_code': 202, 'is_default': True}292 ],293 'state': 'present'294 }295 self.method.module.check_mode = True296 self.method.process_request()297 self.assertEqual(0, self.method.client.update_method.call_count)298 self.assertEqual(0, self.method.client.put_integration.call_count)299 self.assertEqual(0, self.method.client.update_integration.call_count)300 self.assertEqual(0, self.method.client.put_method_response.call_count)301 self.assertEqual(0, self.method.client.update_method_response.call_count)302 self.assertEqual(0, self.method.client.delete_method_response.call_count)303 self.assertEqual(0, self.method.client.put_integration_response.call_count)304 self.assertEqual(0, self.method.client.update_integration_response.call_count)305 self.assertEqual(0, self.method.client.delete_integration_response.call_count)306 self.method.module.exit_json(changed=True, method=None)307 @patch('library.apigw_method.update_method', mock_args)308 @patch.object(ApiGwMethod, 'validate_params')309 @patch.object(ApiGwMethod, '_find_method', return_value={})310 def test_process_request_calls_fail_json_when_update_method_raises_exception(self, mock_find, mock_vp):311 self.method.client.update_method = mock.MagicMock(side_effect=BotoCoreError())312 self.method.process_request()313 self.method.client.update_method.assert_called_once_with(mock="args")314 self.method.module.fail_json(315 msg='Error while updating method via boto3: An unspecified error occurred')316 @patch.object(ApiGwMethod, 'validate_params')317 @patch.object(ApiGwMethod, '_find_method')318 def test_process_skips_update_when_content_handling_missing_from_aws_and_default_from_user(self, mock_find, mock_vp):319 mock_find.return_value = {320 'methodIntegration': {321 'type': 'AWS',322 'httpMethod': 'POST',323 'uri': 'magical-uri',324 'passthroughBehavior': 'when_no_templates',325 'credentials': 'existing creds',326 'cacheNamespace': '',327 'cacheKeyParameters': [],328 }329 }330 self.method.module.params = {331 'method_integration': {332 'integration_type': 'AWS',333 'http_method': 'POST',334 'uri': 'magical-uri',335 'credentials': 'existing creds',336 'passthrough_behavior': 'when_no_templates',337 },338 'state': 'present'339 }340 self.method.process_request()341 self.assertEqual(0, self.method.client.update_integration.call_count)342 @patch.object(ApiGwMethod, 'validate_params')343 @patch.object(ApiGwMethod, '_find_method')344 def test_process_request_calls_update_integration_when_present_and_changed(self, mock_find, mock_vp):345 mock_find.return_value = {346 'methodIntegration': {347 'type': 'wrong',348 'httpMethod': 'POST',349 'uri': 'less-magical-uri',350 'passthroughBehavior': 'when_no_templates',351 'credentials': 'existing creds',352 'requestParameters': {'integration.request.path.bob': 'not-sure'},353 'contentHandling': 'CONVERT_TO_TEXT',354 'cacheNamespace': '',355 'cacheKeyParameters': [u'ckp1', u'ckp2'],356 'requestTemplates': {357 'deleteme': 'whatevs',358 'change/me': 'totally different value',359 }360 }361 }362 self.method.module.params = {363 'rest_api_id': 'restid',364 'resource_id': 'rsrcid',365 'name': 'GET',366 'method_integration': {367 'integration_type': 'aws',368 'http_method': 'POST',369 'uri': 'magical-uri',370 'credentials': 'new creds',371 'passthrough_behavior': 'when_no_templates',372 'request_templates': [373 {'content_type': 'addme', 'template': 'addval'},374 {'content_type': 'change/me', 'template': 'changeval'},375 ],376 'uses_caching': True,377 'cache_namespace': 'cn',378 'cache_key_parameters': ['ckp2', 'ckp1'],379 'integration_params': [380 {'name': 'bob', 'location': 'path', 'value': 'sure'},381 ],382 },383 'state': 'present'384 }385 expected_patch_ops = [386 {'op': 'replace', 'path': '/type', 'value': 'aws'},387 {'op': 'replace', 'path': '/uri', 'value': 'magical-uri'},388 {'op': 'replace', 'path': '/cacheNamespace', 'value': 'cn'},389 {'op': 'replace', 'path': '/credentials', 'value': 'new creds'},390 {'op': 'replace', 'path': '/contentHandling', 'value': ''},391 {'op': 'replace', 'path': '/requestParameters/integration.request.path.bob', 'value': 'sure'},392 {'op': 'add', 'path': '/requestTemplates/addme', 'value': 'addval'},393 {'op': 'remove', 'path': '/requestTemplates/deleteme'},394 {'op': 'replace', 'path': '/requestTemplates/change~1me', 'value': 'changeval'},395 ]396 self.method.process_request()397 self.method.client.update_integration.assert_called_once_with(398 restApiId='restid',399 resourceId='rsrcid',400 httpMethod='GET',401 patchOperations=mock.ANY402 )403 self.assertItemsEqual(expected_patch_ops, self.method.client.update_integration.call_args[1]['patchOperations'])404 @patch.object(ApiGwMethod, 'validate_params')405 @patch.object(ApiGwMethod, '_find_method')406 def test_process_request_skips_patching_http_method_and_uri_when_not_AWS(self, mock_find, mock_vp):407 mock_find.return_value = {408 'methodIntegration': {409 'type': 'XXX',410 'httpMethod': 'POST',411 'uri': 'magical-uri',412 'passthroughBehavior': 'when_no_templates',413 'requestParameters': {},414 'requestTemplates': {}415 }416 }417 self.method.module.params = {418 'rest_api_id': 'restid',419 'resource_id': 'rsrcid',420 'name': 'GET',421 'method_integration': {422 'integration_type': 'XXX',423 'http_method': 'totally different',424 'uri': 'also totally different',425 'passthrough_behavior': 'when_no_templates',426 'request_templates': [],427 'uses_caching': False,428 'integration_params': [],429 },430 'state': 'present'431 }432 self.method.process_request()433 self.assertEqual(0, self.method.client.update_integration.call_count)434 @patch.object(ApiGwMethod, 'validate_params')435 @patch.object(ApiGwMethod, '_find_method', return_value={})436 def test_process_request_adds_integration_type_and_http_method_for_certain_integration_types(self, mock_find, mock_vp):437 self.method.module.params = {438 'rest_api_id': 'restid',439 'resource_id': 'rsrcid',440 'name': 'GET',441 'method_integration': {442 'http_method': 'methody method',443 'uri': 'a majestic uri',444 'passthrough_behavior': 'when_no_templates',445 'request_templates': [],446 'uses_caching': False,447 'integration_params': [],448 },449 'state': 'present'450 }451 for t in ['AWS', 'HTTP', 'AWS_PROXY']:452 self.method.module.params['method_integration']['integration_type'] = t453 self.method.process_request()454 self.assertEqual('a majestic uri', self.method.client.put_integration.call_args[1]['uri'])455 self.assertEqual('methody method', self.method.client.put_integration.call_args[1]['integrationHttpMethod'])456 self.method.client.put_integration.call_args = []457 @patch.object(ApiGwMethod, 'validate_params')458 @patch.object(ApiGwMethod, '_find_method')459 def test_process_request_creates_patches_for_uri_and_http_method_for_certain_types(self, mock_find, mock_vp):460 mock_return = {461 'methodIntegration': {462 'httpMethod': 'POST',463 'uri': 'magical-uri',464 'passthroughBehavior': 'when_no_templates',465 'requestParameters': {},466 'requestTemplates': {}467 }468 }469 self.method.module.params = {470 'rest_api_id': 'restid',471 'resource_id': 'rsrcid',472 'name': 'GET',473 'method_integration': {474 'http_method': 'totally different',475 'uri': 'also totally different',476 'passthrough_behavior': 'when_no_templates',477 'request_templates': [],478 'uses_caching': False,479 'integration_params': [],480 },481 'state': 'present'482 }483 expected = [484 {'path': '/httpMethod', 'value': 'totally different', 'op': 'replace'},485 {'path': '/uri', 'value': 'also totally different', 'op': 'replace'}486 ]487 for t in ['AWS', 'HTTP', 'AWS_PROXY']:488 mock_return['methodIntegration']['type'] = t489 self.method.module.params['method_integration']['integration_type'] = t490 mock_find.return_value = mock_return491 self.method.process_request()492 self.assertItemsEqual(expected, self.method.client.update_integration.call_args[1]['patchOperations'])493 self.method.client.update_integration.call_args = []494 @patch.object(ApiGwMethod, 'validate_params')495 @patch.object(ApiGwMethod, '_find_method')496 def test_process_request_skips_patching_inherited_cache_values_when_not_using_cache(self, mock_find, mock_vp):497 mock_find.return_value = {498 'methodIntegration': {499 'type': 'XXX',500 'httpMethod': 'POST',501 'uri': 'magical-uri',502 'passthroughBehavior': 'when_no_templates',503 'requestParameters': {},504 'cacheNamespace': 'stupid inherited cache namespace',505 'cacheKeyParameters': [],506 'requestTemplates': {}507 }508 }509 self.method.module.params = {510 'rest_api_id': 'restid',511 'resource_id': 'rsrcid',512 'name': 'GET',513 'method_integration': {514 'integration_type': 'XXX',515 'http_method': 'POST',516 'uri': 'magical-uri',517 'passthrough_behavior': 'when_no_templates',518 'request_templates': [],519 'uses_caching': False,520 'integration_params': [],521 },522 'state': 'present'523 }524 self.method.process_request()525 self.assertEqual(0, self.method.client.update_integration.call_count)526 @patch.object(ApiGwMethod, 'validate_params')527 @patch.object(ApiGwMethod, '_find_method', return_value={'something': 'here'})528 def test_process_request_calls_put_integration_when_method_exists_but_integration_does_not(self, mock_find, mock_vp):529 self.method.module.params = {530 'rest_api_id': 'restid',531 'resource_id': 'rsrcid',532 'name': 'GET',533 'method_integration': {534 'integration_type': 'AWS',535 'http_method': 'POST',536 'uri': 'magical-uri',537 'passthrough_behavior': 'when_no_templates',538 'request_templates': [539 {'content_type': 'addme', 'template': 'addval'},540 {'content_type': 'change/me', 'template': 'changeval'},541 ],542 'cache_namespace': 'cn',543 'cache_key_parameters': [],544 'content_handling': 'convert_to_text',545 'integration_params': [546 {'name': 'bob', 'location': 'path', 'value': 'sure'},547 ],548 },549 'state': 'present'550 }551 expected = dict(552 restApiId='restid',553 resourceId='rsrcid',554 httpMethod='GET',555 type='AWS',556 integrationHttpMethod='POST',557 uri='magical-uri',558 requestParameters={559 'integration.request.path.bob': 'sure',560 },561 requestTemplates={'addme': 'addval', 'change/me': 'changeval'},562 passthroughBehavior='when_no_templates',563 cacheNamespace='cn',564 cacheKeyParameters=[],565 contentHandling='CONVERT_TO_TEXT'566 )567 self.method.process_request()568 self.method.client.put_integration.assert_called_once_with(**expected)569 @patch('library.apigw_method.update_method', mock_args)570 @patch.object(ApiGwMethod, 'validate_params')571 @patch.object(ApiGwMethod, '_find_method', return_value={})572 def test_process_request_calls_fail_json_when_update_integration_raises_exception(self, mock_find, mock_vp):573 self.method.client.update_integration = mock.MagicMock(side_effect=BotoCoreError())574 self.method.process_request()575 self.method.client.update_method.assert_called_once_with(mock="args")576 self.method.module.fail_json(577 msg='Error while updating method via boto3: An unspecified error occurred')578 @patch('library.apigw_method.put_integration', mock_args)579 @patch.object(ApiGwMethod, 'validate_params')580 @patch.object(ApiGwMethod, '_find_method')581 def test_process_request_updates_method_responses_when_present_and_changed(self, mock_find, mock_vp):582 mock_find.return_value = {583 'methodResponses': {584 '202': {'statusCode': '202',585 'responseModels': {'application/json': 'Empty', 'delete': 'me'},586 'responseParameters': {587 'method.response.header.shouldbetrue': False,588 'method.response.header.deleteme': True,589 },590 },591 '400': {'statusCode': '400'},592 '404': {'statusCode': '404'}593 }594 }595 self.method.module.params = {596 'rest_api_id': 'restid',597 'resource_id': 'rsrcid',598 'name': 'GET',599 'method_responses': [600 {'status_code': 202,601 'response_params': [602 {'name': 'addparam', 'is_required': False},603 {'name': 'shouldbetrue', 'is_required': True}],604 'response_models': [605 {'content_type': 'application/json', 'model': 'Error'},606 {'content_type': 'add', 'model': 'me'}607 ]},608 {'status_code': 400},609 {'status_code': 500},610 ],611 'state': 'present'612 }613 expected_patch_ops = [614 {'op': 'replace', 'path': '/responseModels/application~1json', 'value': 'Error'},615 {'op': 'add', 'path': '/responseModels/add', 'value': 'me'},616 {'op': 'remove', 'path': '/responseModels/delete'},617 {'op': 'replace', 'path': '/responseParameters/method.response.header.shouldbetrue', 'value': 'True'},618 {'op': 'add', 'path': '/responseParameters/method.response.header.addparam', 'value': 'False'},619 {'op': 'remove', 'path': '/responseParameters/method.response.header.deleteme'},620 ]621 self.method.process_request()622 self.method.client.update_method_response.assert_called_once_with(623 restApiId='restid',624 resourceId='rsrcid',625 httpMethod='GET',626 statusCode='202',627 patchOperations=mock.ANY628 )629 self.assertItemsEqual(expected_patch_ops, self.method.client.update_method_response.call_args[1]['patchOperations'])630 self.method.client.put_method_response.assert_called_once_with(631 restApiId='restid',632 resourceId='rsrcid',633 httpMethod='GET',634 statusCode='500',635 responseParameters={},636 responseModels={}637 )638 self.method.client.delete_method_response.assert_called_once_with(639 restApiId='restid',640 resourceId='rsrcid',641 httpMethod='GET',642 statusCode='404'643 )644 @patch('library.apigw_method.put_integration', mock_args)645 @patch.object(ApiGwMethod, 'validate_params')646 @patch.object(ApiGwMethod, '_find_method')647 def test_process_request_adds_models_and_params_when_missing_from_aws(self, mock_find, mock_vp):648 mock_find.return_value = {'methodResponses': {'202': {'statusCode': '202'}}}649 self.method.module.params = {650 'rest_api_id': 'restid',651 'resource_id': 'rsrcid',652 'name': 'GET',653 'method_responses': [654 {'status_code': 202,655 'response_params': [{'name': 'add_absent', 'is_required': True}],656 'response_models': [{'content_type': 'add_absent', 'model': 'test_model'}]657 }658 ],659 'state': 'present'660 }661 expected_patch_ops = [662 {'op': 'add', 'path': '/responseModels/add_absent', 'value': 'test_model'},663 {'op': 'add', 'path': '/responseParameters/method.response.header.add_absent', 'value': 'True'},664 ]665 self.method.process_request()666 self.method.client.update_method_response.assert_called_once_with(667 restApiId='restid',668 resourceId='rsrcid',669 httpMethod='GET',670 statusCode='202',671 patchOperations=mock.ANY672 )673 self.assertItemsEqual(expected_patch_ops, self.method.client.update_method_response.call_args[1]['patchOperations'])674 @patch('library.apigw_method.put_integration', mock_args)675 @patch.object(ApiGwMethod, 'validate_params')676 @patch.object(ApiGwMethod, '_find_method', return_value={'some': 'thing'})677 def test_process_request_calls_put_method_response_when_method_exists_without_methodResponses(self, mock_find, mock_vp):678 self.method.module.params = {679 'rest_api_id': 'restid',680 'resource_id': 'rsrcid',681 'name': 'GET',682 'method_responses': [683 {684 'status_code': 202,685 'response_params': [ {'name': 'param1', 'is_required': False} ],686 'response_models': [ {'content_type': 'application/json', 'model': 'Error'}, ]687 },688 ],689 'state': 'present'690 }691 self.method.process_request()692 self.assertEqual(0, self.method.client.update_method_response.call_count)693 self.assertEqual(0, self.method.client.delete_method_response.call_count)694 self.method.client.put_method_response.assert_called_once_with(695 restApiId='restid',696 resourceId='rsrcid',697 httpMethod='GET',698 statusCode='202',699 responseParameters={'method.response.header.param1': False},700 responseModels={'application/json': 'Error'}701 )702 @patch.object(ApiGwMethod, 'validate_params')703 @patch.object(ApiGwMethod, '_find_method')704 def test_process_request_updates_method_integrations_when_present_and_changed(self, mock_find, mock_vp):705 mock_find.return_value = {706 'methodIntegration': {707 'integrationResponses': {708 '202': {709 'statusCode': '202',710 'responseTemplates': {'application/json': 'orig-value', 'delete': 'me'},711 'responseParameters': {'also-delete': 'me', 'method.response.header.change': 'me'}712 },713 '400': {'statusCode': '400', 'selectionPattern': '.*Bad.*'},714 '404': {'statusCode': '404', 'selectionPattern': '.*Not Found.*'}715 }716 }717 }718 self.method.module.params = {719 'rest_api_id': 'restid',720 'resource_id': 'rsrcid',721 'name': 'GET',722 'integration_responses': [723 {'status_code': 202,724 'pattern': 'totally-invalid-but-whatever',725 'response_templates': [726 {'content_type': 'application/json', 'template': 'new-value'},727 {'content_type': 'add', 'template': 'me'}728 ],729 'response_params': [730 {'name': 'addme', 'location': 'body', 'value': 'bodyval'},731 {'name': 'change', 'location': 'header', 'value': 'newval'},732 ],733 },734 {'status_code': 400, 'pattern': '.*Bad.*'},735 {'status_code': 500, 'pattern': '.*Unknown.*'},736 ],737 'state': 'present'738 }739 expected_patch_ops = [740 {'op': 'replace', 'path': '/selectionPattern', 'value': 'totally-invalid-but-whatever'},741 {'op': 'replace', 'path': '/responseTemplates/application~1json', 'value': 'new-value'},742 {'op': 'add', 'path': '/responseTemplates/add', 'value': 'me'},743 {'op': 'remove', 'path': '/responseTemplates/delete'},744 {'op': 'replace', 'path': '/responseParameters/method.response.header.change', 'value': 'newval'},745 {'op': 'add', 'path': '/responseParameters/method.response.body.addme', 'value': 'bodyval'},746 {'op': 'remove', 'path': '/responseParameters/also-delete'}747 ]748 self.method.process_request()749 self.method.client.update_integration_response.assert_called_once_with(750 restApiId='restid',751 resourceId='rsrcid',752 httpMethod='GET',753 statusCode='202',754 patchOperations=mock.ANY755 )756 self.assertItemsEqual(757 expected_patch_ops,758 self.method.client.update_integration_response.call_args[1]['patchOperations']759 )760 self.method.client.put_integration_response.assert_called_once_with(761 restApiId='restid',762 resourceId='rsrcid',763 httpMethod='GET',764 statusCode='500',765 selectionPattern='.*Unknown.*',766 responseParameters={},767 responseTemplates={}768 )769 self.method.client.delete_integration_response.assert_called_once_with(770 restApiId='restid',771 resourceId='rsrcid',772 httpMethod='GET',773 statusCode='404'774 )775 @patch.object(ApiGwMethod, 'validate_params')776 @patch.object(ApiGwMethod, '_find_method')777 def test_process_request_calls_put_integration_response_when_method_is_present_but_missing_integrationResponses(self, mock_find, mock_vp):778 mock_find.return_value = {779 'methodIntegration': {}780 }781 self.method.module.params = {782 'rest_api_id': 'restid',783 'resource_id': 'rsrcid',784 'name': 'GET',785 'integration_responses': [786 {'status_code': 1234, 'pattern': '.*Bad.*'},787 ],788 'state': 'present'789 }790 self.method.process_request()791 self.assertEqual(0, self.method.client.update_integration_response.call_count)792 self.assertEqual(0, self.method.client.delete_integration_response.call_count)793 self.method.client.put_integration_response.assert_called_once_with(794 restApiId='restid',795 resourceId='rsrcid',796 httpMethod='GET',797 statusCode='1234',798 selectionPattern='.*Bad.*',799 responseParameters={},800 responseTemplates={}801 )802 @patch.object(ApiGwMethod, '_find_method', side_effect=[None, 'Called post-update'])803 def test_process_request_calls_get_method_and_returns_result_after_update_when_method_is_up_to_date(self, mock_find):804 initial_find = {805 'apiKeyRequired': False,806 'authorizationType': 'NONE',807 'httpMethod': 'GET',808 'requestParameters': {},809 'methodResponses': {'24601': {'statusCode': '24601'}},810 'methodIntegration': {811 'type': 'XXX',812 'httpMethod': 'POST',813 'uri': 'this-is-uri',814 'passthroughBehavior': 'WHEN_NO_TEMPLATES',815 'requestParameters': {},816 'requestTemplates': {},817 'integrationResponses': {'24601': {'statusCode': '24601', 'selectionPattern': 'pattern'}},818 }819 }820 self.method.module.params = {821 'rest_api_id': 'restid',822 'resource_id': 'rsrcid',823 'name': 'GET',824 'authorization_type': 'NONE',825 'api_key_required': False,826 'request_params': [],827 'method_integration': {828 'integration_type': 'XXX',829 'http_method': 'POST',830 'uri': 'this-is-uri',831 'passthrough_behavior': 'when_no_templates',832 },833 'method_responses': [{'status_code': 24601}],834 'integration_responses': [{'status_code': 24601, 'pattern': 'pattern'}],835 'state': 'present'836 }837 mock_find.side_effect = [initial_find, 'Called post-update']838 self.method.process_request()839 self.assertEqual(0, self.method.client.update_method.call_count)840 self.assertEqual(0, self.method.client.put_integration.call_count)841 self.assertEqual(0, self.method.client.update_integration.call_count)842 self.assertEqual(0, self.method.client.put_method_response.call_count)843 self.assertEqual(0, self.method.client.update_method_response.call_count)844 self.assertEqual(0, self.method.client.delete_method_response.call_count)845 self.assertEqual(0, self.method.client.put_integration_response.call_count)846 self.assertEqual(0, self.method.client.update_integration_response.call_count)847 self.assertEqual(0, self.method.client.delete_integration_response.call_count)848 self.method.module.exit_json.assert_called_once_with(changed=False, method='Called post-update')849### End update850### Create tests851 @patch.object(ApiGwMethod, '_find_method', side_effect=[None, 'Called post-create'])852 def test_process_request_calls_get_method_and_returns_result_after_create_when_method_is_absent(self, mock_find):853 self.method.process_request()854 self.method.module.exit_json.assert_called_once_with(changed=True, method='Called post-create')855 @patch.object(ApiGwMethod, '_find_method', return_value=None)856 def test_process_request_calls_put_method_when_method_is_absent(self, mock_find):857 self.method.module.params['request_models'] = [{ 'content_type': 'application/json', 'model': 'ModelName' }]858 self.method.module.params['api_key_required'] = True859 self.method.module.params['request_params'] = [{860 'name': 'qs_param',861 'param_required': False,862 'location': 'querystring'863 },{864 'name': 'path_param',865 'param_required': True,866 'location': 'path'867 },{868 'name': 'header_param',869 'param_required': True,870 'location': 'header'871 }]872 request_params = {873 'method.request.querystring.qs_param': False,874 'method.request.path.path_param': True,875 'method.request.header.header_param': True876 }877 self.method.process_request()878 self.method.client.put_method.assert_called_once_with(879 restApiId='restid',880 resourceId='rsrcid',881 httpMethod='GET',882 authorizationType='NONE',883 apiKeyRequired=True,884 requestParameters=request_params,885 requestModels={ 'application/json': 'ModelName' }886 )887 @patch.object(ApiGwMethod, '_find_method', return_value=None)888 def test_process_request_calls_put_integration_when_method_is_absent(self, mock_find):889 p = {890 'integration_type': 'AWS',891 'http_method': 'POST',892 'uri': 'valid_uri',893 'credentials': 'creds',894 'passthrough_behavior': 'ptb',895 'request_templates': [{'content_type': 'application/json', 'template': '{}'}],896 'cache_namespace': 'cn',897 'cache_key_parameters': ['param1', 'param2'],898 'integration_params': [899 {'name': 'qs_param', 'value': 'qsval', 'location': 'querystring'},900 {'name': 'path_param', 'value': 'pathval', 'location': 'path'},901 {'name': 'header_param', 'value': 'headerval', 'location': 'header'}902 ]903 }904 self.method.module.params['method_integration'] = p905 expected = dict(906 restApiId='restid',907 resourceId='rsrcid',908 httpMethod='GET',909 type='AWS',910 integrationHttpMethod='POST',911 uri='valid_uri',912 credentials='creds',913 requestParameters={914 'integration.request.querystring.qs_param': 'qsval',915 'integration.request.path.path_param': 'pathval',916 'integration.request.header.header_param': 'headerval'917 },918 requestTemplates={'application/json': '{}'},919 passthroughBehavior='ptb',920 cacheNamespace='cn',921 cacheKeyParameters=['param1', 'param2']922 )923 self.method.process_request()924 self.method.client.put_integration.assert_called_once_with(**expected)925 @patch.object(ApiGwMethod, '_find_method', return_value=None)926 def test_process_request_calls_put_method_response_when_method_is_absent(self, mock_find):927 p = [928 {'status_code': 200, 'response_models': [{'content_type': 'ct1', 'model': 'model'},{'content_type': 'ct2'}]},929 {'status_code': 400, 'response_params': [{'name': 'err_param', 'is_required': True}]},930 {'status_code': 500}931 ]932 self.method.module.params['method_responses'] = p933 expected = [934 dict(935 restApiId='restid', resourceId='rsrcid', httpMethod='GET',936 statusCode='200', responseParameters={}, responseModels={'ct1': 'model', 'ct2': 'Empty'}937 ),938 dict(939 restApiId='restid', resourceId='rsrcid', httpMethod='GET',940 statusCode='400', responseParameters={'method.response.header.err_param': True}, responseModels={}),941 dict(942 restApiId='restid', resourceId='rsrcid', httpMethod='GET',943 statusCode='500', responseParameters={}, responseModels={}944 )945 ]946 self.method.process_request()947 self.assertEqual(3, self.method.client.put_method_response.call_count)948 for kwargs in expected:949 self.method.client.put_method_response.assert_any_call(**kwargs)950 @patch.object(ApiGwMethod, '_find_method', return_value=None)951 def test_process_request_calls_put_integration_response_when_method_is_absent(self, mock_find):952 p = [953 {954 'status_code': 200,955 'is_default': True,956 },957 {958 'status_code': 400,959 'is_default': False,960 'pattern': '.*Bad Request.*',961 'response_params': [962 {'name': 'body_param', 'value': 'json-expression', 'location': 'body'},963 {'name': 'header_param', 'value': 'headerval', 'location': 'header'}964 ],965 'response_templates': [{'content_type': 'application/json', 'template': '{}'}]966 },967 ]968 self.method.module.params['integration_responses'] = p969 expected = [970 dict(971 restApiId='restid',972 resourceId='rsrcid',973 httpMethod='GET',974 statusCode='200',975 selectionPattern='',976 responseParameters={},977 responseTemplates={}978 ),979 dict(980 restApiId='restid',981 resourceId='rsrcid',982 httpMethod='GET',983 statusCode='400',984 selectionPattern='.*Bad Request.*',985 responseParameters={986 'method.response.body.body_param': 'json-expression',987 'method.response.header.header_param': 'headerval'988 },989 responseTemplates={'application/json': '{}'}990 ),991 ]992 self.method.process_request()993 self.assertEqual(2, self.method.client.put_integration_response.call_count)994 for kwargs in expected:995 self.method.client.put_integration_response.assert_any_call(**kwargs)996 @patch.object(ApiGwMethod, '_find_method', return_value=None)997 def test_process_request_calls_fail_json_when_put_method_throws_error(self, mock_find):998 self.method.client.put_method = mock.MagicMock(side_effect=BotoCoreError())999 self.method.process_request()1000 self.method.client.put_method.assert_called_once_with(1001 restApiId='restid',1002 resourceId='rsrcid',1003 httpMethod='GET',1004 authorizationType='NONE',1005 apiKeyRequired=False,1006 requestParameters={},1007 requestModels={}1008 )1009 self.method.module.fail_json.assert_called_once_with(1010 msg='Error while creating method via boto3: An unspecified error occurred')1011 @patch.object(ApiGwMethod, '_find_method', return_value=None)1012 def test_process_request_calls_fail_json_when_put_integration_throws_error(self, mock_find):1013 self.method.client.put_integration = mock.MagicMock(side_effect=BotoCoreError())1014 self.method.process_request()1015 self.method.client.put_integration.assert_called_once_with(1016 restApiId='restid',1017 resourceId='rsrcid',1018 httpMethod='GET',1019 type='value',1020 requestParameters={},1021 requestTemplates={}1022 )1023 self.method.module.fail_json.assert_called_once_with(1024 msg='Error while creating method via boto3: An unspecified error occurred')1025 @patch.object(ApiGwMethod, '_find_method', return_value=None)1026 def test_process_request_calls_fail_json_when_put_method_response_throws_error(self, mock_find):1027 self.method.client.put_method_response = mock.MagicMock(side_effect=BotoCoreError())1028 self.method.module.params['method_responses'] = [{'status_code': 200}]1029 self.method.process_request()1030 self.method.client.put_method_response.assert_called_once_with(1031 restApiId='restid',1032 resourceId='rsrcid',1033 httpMethod='GET',1034 statusCode='200',1035 responseParameters={},1036 responseModels={}1037 )1038 self.method.module.fail_json.assert_called_once_with(1039 msg='Error while creating method via boto3: An unspecified error occurred')1040 @patch.object(ApiGwMethod, '_find_method', return_value=None)1041 def test_process_request_calls_fail_json_when_put_integration_response_throws_error(self, mock_find):1042 self.method.client.put_integration_response = mock.MagicMock(side_effect=BotoCoreError())1043 self.method.module.params['integration_responses'] = [{'status_code': 200, 'is_default': True}]1044 self.method.process_request()1045 self.method.client.put_integration_response.assert_called_once_with(1046 restApiId='restid',1047 resourceId='rsrcid',1048 httpMethod='GET',1049 statusCode='200',1050 selectionPattern='',1051 responseParameters={},1052 responseTemplates={}1053 )1054 self.method.module.fail_json.assert_called_once_with(1055 msg='Error while creating method via boto3: An unspecified error occurred')1056 @patch.object(ApiGwMethod, '_find_method', return_value=None)1057 def test_process_request_skips_create_and_returns_true_when_method_is_absent_and_check_mode_set(self, mock_find):1058 self.method.module.check_mode = True1059 self.method.process_request()1060 self.assertEqual(0, self.method.client.put_method.call_count)1061 self.assertEqual(0, self.method.client.put_integration.call_count)1062 self.method.module.exit_json.assert_called_once_with(changed=True, method=None)1063### End create1064### Gauntlet of validation1065 # This 'test' is a facility to allow testing of all validation scenarios. This1066 # probably isn't the best of practices, but it gives us a one-stop shop for capturing1067 # tests of all of the conditional requirements of the various boto method invocations.1068 #1069 # This is not meant to test that ansible enforces the various conditions in the1070 # argument spec and instead tests errors arising from illegal combinations of1071 # parameters (as per the boto3 apigateway docs)1072 def test_validation_of_arguments(self):1073 # A complete and valid param list1074 params = {1075 'rest_api_id': 'restid',1076 'resource_id': 'rsrcid',1077 'name': 'GET',1078 'authorization_type': 'NONE',1079 'api_key_required': False,1080 'request_params': [{'name': 'reqparm', 'location': 'header', 'param_required': True}],1081 'method_integration': {1082 'integration_type': 'AWS',1083 'http_method': 'POST',1084 'uri': 'this.is.a.uri',1085 'passthrough_behavior': 'when_no_templates',1086 'request_templates': [{1087 'content_type': 'application/json',1088 'template': '{"key": "value"}'1089 }],1090 'integration_params': [{'name': 'intparam', 'location': 'querystring', 'value': 'yes'}]1091 },1092 'method_responses': [{1093 'status_code': '200',1094 'response_models': [{'content_type': 'application/json', 'model': 'Empty'}],1095 }],1096 'integration_responses': [{1097 'status_code': '200',1098 'is_default': False,1099 'pattern': '.*whatever.*',1100 'response_params': [{'name': 'irparam', 'location': 'path', 'value': 'sure'}],1101 'response_templates': [{'content_type': 'application/xml', 'template': 'xml stuff'}]1102 }],1103 'state': 'present',1104 }1105 # Changes needed to invoke an exception1106 tests = [1107 {1108 'changes': {'authorization_type': 'CUSTOM'},1109 'delete_keys': [],1110 'error': {1111 'param': 'authorizer_id',1112 'msg': "authorizer_id must be provided when authorization_type is 'CUSTOM'"1113 }1114 },1115 {1116 'changes': {},1117 'delete_keys': ['method_integration.http_method'],1118 'error': {1119 'param': 'method_integration',1120 'msg': "http_method must be provided when integration_type is 'AWS', 'AWS_PROXY', or 'HTTP'"1121 }1122 },1123 {1124 'changes': {'method_integration': {'integration_type': 'HTTP'}},1125 'delete_keys': ['method_integration.http_method'],1126 'error': {1127 'param': 'method_integration',1128 'msg': "http_method must be provided when integration_type is 'AWS', 'AWS_PROXY', or 'HTTP'"1129 }1130 },1131 {1132 'changes': {'method_integration': {'integration_type': 'AWS_PROXY'}},1133 'delete_keys': ['method_integration.http_method'],1134 'error': {1135 'param': 'method_integration',1136 'msg': "http_method must be provided when integration_type is 'AWS', 'AWS_PROXY', or 'HTTP'"1137 }1138 },1139 {1140 'changes': {},1141 'delete_keys': ['method_integration.uri'],1142 'error': {1143 'param': 'method_integration',1144 'msg': "uri must be provided when integration_type is 'AWS', 'AWS_PROXY', or 'HTTP'"1145 }1146 },1147 {1148 'changes': {'method_integration': {'integration_type': 'HTTP'}},1149 'delete_keys': ['method_integration.uri'],1150 'error': {1151 'param': 'method_integration',1152 'msg': "uri must be provided when integration_type is 'AWS', 'AWS_PROXY', or 'HTTP'"1153 }1154 },1155 {1156 'changes': {'method_integration': {'integration_type': 'AWS_PROXY'}},1157 'delete_keys': ['method_integration.uri'],1158 'error': {1159 'param': 'method_integration',1160 'msg': "uri must be provided when integration_type is 'AWS', 'AWS_PROXY', or 'HTTP'"1161 }1162 },1163 {1164 'changes': {'integration_responses': [{'status_code': 'x', 'is_default': True, 'pattern': 'xxx'}]},1165 'delete_keys': [],1166 'error': {1167 'param': 'integration_responses',1168 'msg': "'pattern' must not be provided when 'is_default' is True"1169 }1170 },1171 {1172 'changes': {'integration_responses': [{'status_code': 'x', 'is_default': False}]},1173 'delete_keys': [],1174 'error': {1175 'param': 'integration_responses',1176 'msg': "'pattern' must be provided when 'is_default' is False"1177 }1178 },1179 {1180 'changes': {},1181 'delete_keys': ['method_integration'],1182 'error': {1183 'param': 'method_integration',1184 'msg': "'method_integration' must be provided when 'state' is present"1185 }1186 },1187 {1188 'changes': {},1189 'delete_keys': ['method_responses'],1190 'error': {1191 'param': 'method_responses',1192 'msg': "'method_responses' must be provided when 'state' is present"1193 }1194 },1195 {1196 'changes': {},1197 'delete_keys': ['integration_responses'],1198 'error': {1199 'param': 'integration_responses',1200 'msg': "'integration_responses' must be provided when 'state' is present"1201 }1202 },1203 ]1204 for test in tests:1205 p = copy.deepcopy(params)1206 p = merge(p, test['changes'])1207 p = purge(p, test['delete_keys'])1208 self.method.module.params = p1209 try:1210 self.method.validate_params()1211 self.assertEqual("", test['error']['msg'])1212 except apigw_method.InvalidInputError as e:1213 self.assertEqual("Error validating {0}: {1}".format(test['error']['param'], test['error']['msg']), str(e))1214### End validation1215 def test_define_argument_spec(self):1216 result = ApiGwMethod._define_module_argument_spec()1217 self.assertIsInstance(result, dict)1218 self.assertEqual(result, dict(1219 name=dict(1220 required=True,1221 choices=['GET', 'PUT', 'POST', 'DELETE', 'PATCH', 'HEAD', 'ANY', 'OPTIONS'],1222 aliases=['method']1223 ),1224 rest_api_id=dict(required=True),1225 resource_id=dict(required=True),1226 authorization_type=dict(required=False, default='NONE'),1227 authorizer_id=dict(required=False),1228 api_key_required=dict(required=False, type='bool', default=False),1229 request_models=dict(1230 type='list',1231 required=False,1232 default=[],1233 content_type=dict(required=True),1234 model=dict(required=True)1235 ),1236 request_params=dict(1237 type='list',1238 required=False,1239 default=[],1240 name=dict(required=True),1241 location=dict(required=True, choices=['querystring', 'path', 'header']),1242 param_required=dict(type='bool')1243 ),1244 method_integration=dict(1245 type='dict',1246 default={},1247 integration_type=dict(required=False, default='AWS', choices=['AWS', 'MOCK', 'HTTP', 'HTTP_PROXY', 'AWS_PROXY']),1248 http_method=dict(required=False, default='POST', choices=['POST', 'GET', 'PUT']),1249 uri=dict(required=False),1250 credentials=dict(required=False),1251 passthrough_behavior=dict(required=False, default='when_no_templates', choices=['when_no_templates', 'when_no_match', 'never']),1252 request_templates=dict(1253 required=False,1254 type='list',1255 default=[],1256 content_type=dict(required=True),1257 template=dict(required=True)1258 ),1259 uses_caching=dict(required=False, default=False, type='bool'),1260 cache_namespace=dict(required=False, default=''),1261 cache_key_parameters=dict(required=False, type='list', default=[]),1262 content_handling=dict(required=False, default='', choices=['convert_to_binary', 'convert_to_text', '']),1263 integration_params=dict(1264 type='list',1265 required=False,1266 default=[],1267 name=dict(required=True),1268 location=dict(required=True, choices=['querystring', 'path', 'header']),1269 value=dict(required=True)1270 )1271 ),1272 method_responses=dict(1273 type='list',1274 default=[],1275 status_code=dict(required=True),1276 response_params=dict(1277 type='list',1278 required=False,1279 default=[],1280 name=dict(required=True),1281 is_required=dict(required=True, type='bool')1282 ),1283 response_models=dict(1284 type='list',1285 required=False,1286 default=[],1287 content_type=dict(required=True),1288 model=dict(required=False, default='Empty', choices=['Empty', 'Error'])1289 )1290 ),1291 integration_responses=dict(1292 type='list',1293 default=[],1294 status_code=dict(required=True),1295 is_default=dict(required=False, default=False, type='bool'),1296 pattern=dict(required=False),1297 response_params=dict(1298 type='list',1299 required=False,1300 default=[],1301 name=dict(required=True),1302 location=dict(required=True, choices=['body', 'header']),1303 value=dict(required=True)1304 ),1305 response_templates=dict(1306 required=False,1307 type='list',1308 default=[],1309 content_type=dict(required=True),1310 template=dict(required=True)1311 ),1312 ),1313 state=dict(default='present', choices=['present', 'absent'])1314 ))1315 @patch.object(apigw_method, 'AnsibleModule')1316 @patch.object(apigw_method, 'ApiGwMethod')1317 def test_main(self, mock_ApiGwMethod, mock_AnsibleModule):1318 mock_ApiGwMethod_instance = mock.MagicMock()1319 mock_AnsibleModule_instance = mock.MagicMock()1320 mock_ApiGwMethod.return_value = mock_ApiGwMethod_instance1321 mock_AnsibleModule.return_value = mock_AnsibleModule_instance1322 apigw_method.main()1323 mock_ApiGwMethod.assert_called_once_with(mock_AnsibleModule_instance)1324 self.assertEqual(1, mock_ApiGwMethod_instance.process_request.call_count)1325if __name__ == '__main__':...
apigateway.py
Source:apigateway.py
...311 resp = self.apigateway_client.delete_method_response(restApiId=restid, resourceId=resourceid, httpMethod=method,312 statusCode=statuscode)313 super(Apigateway, self).query_information(query=resp)314 return resp315 def update_method_response(self, restid, resourceid, method, statuscode, operation):316 """317 This function updates a method a response318 :param method: the method that is requested319 :type method: basestring320 :param restid: the id of the rest api object321 :type restid: basestring322 :param resourceid: id of a single resource object323 :type resourceid: basestring324 :param statuscode:the statuscode to update325 :type statuscode: basestring326 :param operation: an array of patchOperations327 :type operation: list[dict]328 :return: the obdated method response object329 """330 if self.dryrun:331 logger.info("Dryrun requested no changes will be done")332 return None333 resp = self.apigateway_client.update_method_response(restApiId=restid, resourceId=resourceid, httpMethod=method,334 statusCode=statuscode, patchOperations=operation)335 super(Apigateway, self).query_information(query=resp)336 logger.debug("The response of the update method response: %s" % resp)337 return resp338 def create_integration(self, restid, resourceid, method, integration_type, further_opts=None):339 """340 This function creates an integration object341 :param method: the method that is requested342 :type method: basestring343 :param restid: the id of the rest api object344 :type restid: basestring345 :param resourceid: id of a single resource object346 :type resourceid: basestring347 :param integration_type: an enum of the integration type348 :type integration_type: basestring349 :param further_opts: This opt passes in json_data fur not mandatory options350 :type further_opts: dict351 :return: object of the created integration352 """353 if self.dryrun:354 logger.info("Dryrun requested no changes will be done")355 return None356 opts = {'restApiId': restid, 'resourceId': resourceid, 'httpMethod': method, 'type': integration_type,357 'integrationHttpMethod': method}358 # There is aws cli bug and integrationHttpMethod also needs to be added. may change later359 # opts = {'restApiId': restid, 'resourceId': resourceid, 'httpMethod': method, 'type': integration_type}360 for element in ['integrationHttpMethod', 'uri', 'credentials', 'requestParameters', 'requestTemplates',361 'cacheNamespace', 'cacheNamespace']:362 if element in further_opts:363 opts[element] = further_opts[element]364 logger.debug("The opts for integration object creation: %s" % opts)365 resp = self.apigateway_client.put_integration(**opts)366 super(Apigateway, self).query_information(query=resp)367 return resp368 def get_rest_api_by_name(self, name):369 """370 This function returns a rest api by name371 :param name: the name of the reste api to return372 :type name: basestring373 :return: a rest api top level object374 :rtype: object375 """376 gws = self.get_rest_api()377 ret = None378 logger.debug("Searcing for rest api by name")379 for gw in gws:380 if gw['name'] == name:381 logger.info("Found the gw by name")382 ret = gw383 return ret384 def update_rest_api(self, restid, operation):385 """386 This function updates a rest api top level object387 :param restid: the id of the rest api object388 :type restid: basestring389 :param operation: a list of patchOperations390 :type operation: list391 :return: the updated rest api object392 :rtype: object393 """394 if self.dryrun:395 logger.info("Dryrun requested no changes will be done")396 return None397 resp = self.apigateway_client.update_rest_api(restApiId=restid, patchOperations=operation)398 super(Apigateway, self).query_information(query=resp)399 return resp400 def update_method(self, restid, resourceid, method, operation):401 """402 This function updates a method object403 :param method: the method that is requested404 :type method: basestring405 :param restid: the id of the rest api object406 :type restid: basestring407 :param resourceid: id of a single resource object408 :type resourceid: basestring409 :param operation: an list of patchOperations410 :type operation: list411 :return: the updated method object412 :rtype: object413 """414 if self.dryrun:415 logger.info("Dryrun requested no changes will be done")416 return None417 resp = self.apigateway_client.update_method(restApiId=restid, resourceId=resourceid, httpMethod=method,418 operation=operation)419 super(Apigateway, self).query_information(query=resp)420 return resp421 def update_integration_response(self, restid, resourceid, method, statuscode, operation):422 """423 This function updates an integration response424 :param method: the method that is requested425 :type method: basestring426 :param restid: the id of the rest api object427 :type restid: basestring428 :param resourceid: id of a single resource object429 :type resourceid: basestring430 :param statuscode: the statuscode where the integration response is431 :type statuscode: basestring432 :param operation: a list of patchOperations433 :type operation: list434 :return: the updated integration response object435 """436 if self.dryrun:437 logger.info("Dryrun requested no changes will be done")438 return None439 resp = self.apigateway_client.update_integration_response(restApiId=restid, resourceId=resourceid,440 httpMethod=method, statusCode=statuscode,441 patchOperations=operation)442 super(Apigateway, self).query_information(query=resp)443 return resp444 def generate_resourcehash(self, restid):445 """446 This function collects and returns a hash with resource object and their ids.447 This is used to find any resources that should be deleted or added448 :param restid: the id of the rest api object449 :type restid: basestring450 :return: a dict with resource name with resource id-s451 :rtype: dict452 """453 resources = self.get_resource(restid=restid)454 ret = {}455 for resource in resources:456 ret[resource['path']] = resource['id']457 return ret458 def create_resource(self, restid, parentid, pathpart):459 """460 This function creates a resource object461 :param restid: the id of the rest api object462 :type restid: basestring463 :param parentid: the parent id of the created resource, should be rest api464 :type parentid: basestring465 :param pathpart: The pathpart where the resource be466 :type pathpart: basestring467 :return: the resource object created468 """469 if self.dryrun:470 logger.info("Dryrun requested no changes will be done")471 return None472 resp = self.apigateway_client.create_resource(restApiId=restid, parentId=parentid, pathPart=pathpart)473 super(Apigateway, self).query_information(query=resp)474 return resp475 def delete_resource(self, restid, resourceid):476 """477 This function deletes a resource object478 :param restid: the id of the rest api object479 :type restid: basestring480 :param resourceid: id of a single resource object481 :type resourceid: basestring482 :return: None483 """484 if self.dryrun:485 logger.info("Dryrun requested no changes will be done")486 return None487 resp = self.apigateway_client.delete_resource(restApiId=restid, resourceId=resourceid)488 super(Apigateway, self).query_information(query=resp)489 return resp490 def delete_integration_response(self, restid, resourceid, method, statuscode):491 """492 This function deletes an integration response493 :param method: the method that is requested494 :type method: basestring495 :param restid: the id of the rest api object496 :type restid: basestring497 :param resourceid: id of a single resource object498 :type resourceid: basestring499 :param statuscode: the statuscode to delete500 :type statuscode: basestring501 :return: None502 """503 if self.dryrun:504 logger.info("Dryrun requested no changes will be done")505 return None506 resp = self.apigateway_client.delete_integration_response(restApiId=restid, resourceId=resourceid,507 httpMethod=method, statusCode=statuscode)508 super(Apigateway, self).query_information(query=resp)509 return resp510 def method_exists(self, restid, resourceid, method):511 try:512 self.get_method(restid=restid, resourceid=resourceid, method=method)513 return True514 except:515 return False516 def method_response_exists(self, restid, resourceid, method, statuscode):517 try:518 self.get_method_response(restid=restid, resourceid=resourceid, method=method, statuscode=statuscode)519 return True520 except:521 return False522 def integration_response_exists(self, restid, resourceid, method, status):523 try:524 self.get_integration_response(restid=restid, resourceid=resourceid, method=method, status=status)525 return True526 except:527 return False528 def integration_exists(self, restid, resourceid, method):529 try:530 self.get_integration(restid=restid, resourceid=resourceid, method=method)531 return True532 except:533 return False534 def compare_method(self, restid, resourceid, method, json_data):535 """536 This function compares a json data to the current method to detect an updates that need to be done537 :param method: the method that is requested538 :type method: basestring539 :param restid: the id of the rest api object540 :type restid: basestring541 :param resourceid: id of a single resource object542 :type resourceid: basestring543 :param json_data: the json data from the model that is the representation of the current state544 :type json_data: dict545 :return: None546 """547 logger.info("Looking at restid: %s, resourceid: %s, and method: %s" % (restid, resourceid, method))548 # First we test if the top level method is created or we need to create it549 if not self.method_exists(restid=restid, resourceid=resourceid, method=method):550 logger.info("Need to create method: %s" % method)551 cur_method = self.create_method(restid=restid, resourceid=resourceid, method=method,552 authorizationtype=json_data['authorizationType'], further_opts=json_data)553 else:554 cur_method = self.get_method(restid=restid, resourceid=resourceid, method=method)555 logger.info("Method existed, need to compare for changes")556 for element in ['authorizationType', 'apiKeyRequired', 'requestParameters', 'requestModels']:557 if (element in json_data and element in cur_method) and json_data[element] != cur_method[element]:558 logger.warning("Need to update %s" % element)559 self.update_method(restid=restid, resourceid=resourceid, method=method, operation=[560 {'op': 'replace', 'path': "/%s" % element, 'value': json_data[element]}])561 if element not in json_data:562 logger.debug("Upload template missing key %s, skipping" % element)563 if element not in cur_method and element in json_data:564 logger.warning("Not defined in current method need to update current method with %s" % element)565 # Check if method needs to be deleted566 if 'methodResponses' in cur_method:567 for statuscode in cur_method['methodResponses']:568 if statuscode not in json_data['methodResponses']:569 logger.warning("This method response needs to be deleted %s" % statuscode)570 self.delete_method_response(restid=restid, resourceid=resourceid, method=method,571 statuscode=statuscode)572 # iterate over status codes and check we need to create or update573 for statuscode in json_data['methodResponses']:574 if not self.method_response_exists(restid=restid, resourceid=resourceid, method=method,575 statuscode=statuscode):576 logger.debug("Creating method response %s" % statuscode)577 self.create_method_response(restid=restid, resourceid=resourceid, method=method, statuscode=statuscode,578 further_ops=json_data['methodResponses'][statuscode])579 else:580 cur_response = self.get_method_response(restid=restid, resourceid=resourceid, method=method,581 statuscode=statuscode)582 logger.debug("Need to compare the responses")583 dictdiffer = DictDiffer(cur_response, json_data['methodResponses'][statuscode])584 for remove_statuscode in dictdiffer.added():585 logger.info("Need to remove statuscode: %s" % remove_statuscode)586 self.delete_method_response(restid=restid, resourceid=resourceid, method=method,587 statuscode=remove_statuscode)588 for add_statuscode in dictdiffer.removed():589 logger.info("Need to add statuscode: %s" % add_statuscode)590 self.create_method_response(restid=restid, resourceid=resourceid, method=method,591 statuscode=add_statuscode,592 further_ops=json_data['methodResponses'][add_statuscode])593 for changed_statuscode in dictdiffer.changed():594 logger.info("Need to update statuscode: %s" % changed_statuscode)595 cur_method_statuscode = cur_method['methodResponses'][changed_statuscode]596 json_data_statuscode = json_data['methodmethod']['methodResponses'][changed_statuscode]597 for element in ['responseParameters', 'responseTemplates']:598 if element not in json_data_statuscode:599 continue600 change_dictdiffer = DictDiffer(601 cur_method_statuscode[element],602 json_data_statuscode[element])603 for add_int_statuscode in change_dictdiffer.removed():604 logger.info("method response is missing, adding: %s" % add_int_statuscode)605 self.update_method_response(restid=restid, resourceid=resourceid, method=method,606 statuscode=changed_statuscode, operation=[607 {'op': 'add', 'path': "/%s/%s" % (element, add_int_statuscode), 'value':608 json_data_statuscode[element][add_int_statuscode]}])609 for remove_int_statuscode in change_dictdiffer.added():610 logger.info("Method response is present, deleting: %s" % remove_int_statuscode)611 self.update_method_response(restid=restid, resourceid=resourceid, method=method,612 statuscode=changed_statuscode, operation=[613 {'op': 'remove', 'path': "/%s/%s" % (element, remove_int_statuscode)}])614 for change_int_statuscode in change_dictdiffer.changed():615 logger.info("There is a change in value, need to update: %s" % change_int_statuscode)616 self.update_method_response(restid=restid, resourceid=resourceid, method=method,617 statuscode=changed_statuscode, operation=[618 {'op': 'replace', 'path': "/%s/%s" % (element, change_int_statuscode), 'value':619 json_data_statuscode[element][change_int_statuscode]}])620 # method integration621 if self.integration_exists(restid=restid, resourceid=resourceid, method=method):622 cur_method_integration = self.get_integration(restid=restid, resourceid=resourceid, method=method)623 dictdiffer_integration_response = DictDiffer(cur_method_integration['integrationResponses'],624 json_data['methodIntegration']['integrationResponses'])625 for remove_response in dictdiffer_integration_response.added():626 logger.info("Need to remove integration response: %s" % remove_response)627 self.delete_integration_response(restid=restid, resourceid=resourceid, method=method,628 statuscode=remove_response)629 for add_response in dictdiffer_integration_response.removed():630 logger.info("Need to add integration response: %s" % add_response)...
test_generator.py
Source:test_generator.py
1from pyboto3 import interface_generator2import json3def test_get_services_dir():4 assert interface_generator.get_services_dir()5 service_json_path_dict = dict(interface_generator.iter_services_json_paths())6 for service_name, json_path in service_json_path_dict.iteritems():7 print service_name, json_path8 apigateway_methods = {'can_paginate', 'create_api_key', 'create_authorizer', 'create_base_path_mapping',9 'create_deployment',10 'create_domain_name', 'create_model', 'create_resource', 'create_rest_api', 'create_stage',11 'delete_api_key', 'delete_authorizer', 'delete_base_path_mapping',12 'delete_client_certificate',13 'delete_deployment', 'delete_domain_name', 'delete_integration',14 'delete_integration_response',15 'delete_method', 'delete_method_response', 'delete_model', 'delete_resource',16 'delete_rest_api',17 'delete_stage', 'flush_stage_authorizers_cache', 'flush_stage_cache',18 'generate_client_certificate',19 'generate_presigned_url', 'get_account', 'get_api_key', 'get_api_keys', 'get_authorizer',20 'get_authorizers', 'get_base_path_mapping', 'get_base_path_mappings',21 'get_client_certificate',22 'get_client_certificates', 'get_deployment', 'get_deployments', 'get_domain_name',23 'get_domain_names',24 'get_export', 'get_integration', 'get_integration_response', 'get_method',25 'get_method_response',26 'get_model', 'get_model_template', 'get_models', 'get_paginator', 'get_resource',27 'get_resources',28 'get_rest_api', 'get_rest_apis', 'get_sdk', 'get_stage', 'get_stages', 'get_waiter',29 'import_rest_api',30 'put_integration', 'put_integration_response', 'put_method', 'put_method_response',31 'put_rest_api',32 'test_invoke_authorizer', 'test_invoke_method', 'update_account', 'update_api_key',33 'update_authorizer',34 'update_base_path_mapping', 'update_client_certificate', 'update_deployment',35 'update_domain_name',36 'update_integration', 'update_integration_response', 'update_method',37 'update_method_response',38 'update_model', 'update_resource', 'update_rest_api', 'update_stage'}39 apigateway_dict = json.load(open(service_json_path_dict.get('apigateway')))40 assert set(interface_generator.iter_method_names(apigateway_dict)) == apigateway_methods41 s3_methods = {'abort_multipart_upload', 'can_paginate', 'complete_multipart_upload', 'copy_object',42 'create_bucket',43 'create_multipart_upload', 'delete_bucket', 'delete_bucket_cors', 'delete_bucket_lifecycle',44 'delete_bucket_policy', 'delete_bucket_replication', 'delete_bucket_tagging',45 'delete_bucket_website',46 'delete_object', 'delete_objects', 'download_file', 'generate_presigned_post',47 'generate_presigned_url', 'get_bucket_accelerate_configuration', 'get_bucket_acl',48 'get_bucket_cors',49 'get_bucket_lifecycle', 'get_bucket_lifecycle_configuration', 'get_bucket_location',50 'get_bucket_logging', 'get_bucket_notification', 'get_bucket_notification_configuration',51 'get_bucket_policy', 'get_bucket_replication', 'get_bucket_request_payment', 'get_bucket_tagging',52 'get_bucket_versioning', 'get_bucket_website', 'get_object', 'get_object_acl',53 'get_object_torrent',54 'get_paginator', 'get_waiter', 'head_bucket', 'head_object', 'list_buckets',55 'list_multipart_uploads',56 'list_object_versions', 'list_objects', 'list_objects_v2', 'list_parts',57 'put_bucket_accelerate_configuration', 'put_bucket_acl', 'put_bucket_cors',58 'put_bucket_lifecycle',59 'put_bucket_lifecycle_configuration', 'put_bucket_logging', 'put_bucket_notification',60 'put_bucket_notification_configuration', 'put_bucket_policy', 'put_bucket_replication',61 'put_bucket_request_payment', 'put_bucket_tagging', 'put_bucket_versioning', 'put_bucket_website',62 'put_object', 'put_object_acl', 'restore_object', 'upload_file', 'upload_part',63 'upload_part_copy'}64 s3_dict = json.load(open(service_json_path_dict.get('s3')))...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!