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!!
