localstack-core 4.7.1.dev49__py3-none-any.whl → 4.10.1.dev12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- localstack/aws/api/cloudformation/__init__.py +18 -4
- localstack/aws/api/cloudwatch/__init__.py +41 -1
- localstack/aws/api/config/__init__.py +4 -0
- localstack/aws/api/core.py +6 -2
- localstack/aws/api/dynamodb/__init__.py +30 -0
- localstack/aws/api/ec2/__init__.py +1522 -65
- localstack/aws/api/iam/__init__.py +7 -0
- localstack/aws/api/kinesis/__init__.py +19 -0
- localstack/aws/api/kms/__init__.py +6 -0
- localstack/aws/api/lambda_/__init__.py +13 -0
- localstack/aws/api/logs/__init__.py +15 -0
- localstack/aws/api/redshift/__init__.py +9 -3
- localstack/aws/api/route53/__init__.py +5 -0
- localstack/aws/api/s3/__init__.py +12 -0
- localstack/aws/api/s3control/__init__.py +54 -0
- localstack/aws/api/ssm/__init__.py +2 -0
- localstack/aws/api/transcribe/__init__.py +17 -0
- localstack/aws/client.py +7 -2
- localstack/aws/forwarder.py +52 -5
- localstack/aws/handlers/analytics.py +1 -1
- localstack/aws/handlers/internal_requests.py +6 -1
- localstack/aws/handlers/logging.py +12 -2
- localstack/aws/handlers/metric_handler.py +41 -1
- localstack/aws/handlers/service.py +40 -20
- localstack/aws/mocking.py +2 -2
- localstack/aws/patches.py +2 -2
- localstack/aws/protocol/parser.py +459 -32
- localstack/aws/protocol/serializer.py +689 -69
- localstack/aws/protocol/service_router.py +120 -20
- localstack/aws/protocol/validate.py +1 -1
- localstack/aws/scaffold.py +1 -1
- localstack/aws/skeleton.py +4 -2
- localstack/aws/spec-patches.json +58 -0
- localstack/aws/spec.py +37 -16
- localstack/cli/exceptions.py +1 -1
- localstack/cli/localstack.py +6 -6
- localstack/cli/lpm.py +3 -4
- localstack/cli/plugins.py +1 -1
- localstack/cli/profiles.py +1 -2
- localstack/config.py +25 -18
- localstack/constants.py +4 -29
- localstack/dev/kubernetes/__main__.py +130 -7
- localstack/dev/run/configurators.py +1 -4
- localstack/dev/run/paths.py +1 -1
- localstack/dns/plugins.py +5 -1
- localstack/dns/server.py +13 -4
- localstack/logging/format.py +3 -3
- localstack/packages/api.py +9 -8
- localstack/packages/core.py +2 -2
- localstack/packages/plugins.py +0 -8
- localstack/runtime/analytics.py +3 -0
- localstack/runtime/hooks.py +1 -1
- localstack/runtime/init.py +2 -2
- localstack/runtime/main.py +5 -5
- localstack/runtime/patches.py +2 -2
- localstack/services/apigateway/helpers.py +1 -4
- localstack/services/apigateway/legacy/helpers.py +7 -8
- localstack/services/apigateway/legacy/integration.py +4 -3
- localstack/services/apigateway/legacy/invocations.py +6 -5
- localstack/services/apigateway/legacy/provider.py +148 -68
- localstack/services/apigateway/legacy/templates.py +1 -1
- localstack/services/apigateway/next_gen/execute_api/handlers/method_request.py +7 -2
- localstack/services/apigateway/next_gen/execute_api/handlers/resource_router.py +1 -2
- localstack/services/apigateway/next_gen/execute_api/integrations/aws.py +3 -0
- localstack/services/apigateway/next_gen/execute_api/integrations/http.py +3 -3
- localstack/services/apigateway/next_gen/execute_api/template_mapping.py +2 -2
- localstack/services/apigateway/next_gen/execute_api/test_invoke.py +114 -9
- localstack/services/apigateway/next_gen/provider.py +5 -0
- localstack/services/apigateway/resource_providers/aws_apigateway_resource.py +1 -1
- localstack/services/cloudformation/api_utils.py +4 -8
- localstack/services/cloudformation/cfn_utils.py +1 -1
- localstack/services/cloudformation/engine/entities.py +14 -4
- localstack/services/cloudformation/engine/template_deployer.py +6 -4
- localstack/services/cloudformation/engine/transformers.py +6 -4
- localstack/services/cloudformation/engine/v2/change_set_model.py +201 -13
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +52 -3
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +117 -76
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +205 -52
- localstack/services/cloudformation/engine/v2/change_set_model_transform.py +350 -116
- localstack/services/cloudformation/engine/v2/change_set_model_validator.py +56 -14
- localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +1 -0
- localstack/services/cloudformation/engine/v2/resolving.py +7 -5
- localstack/services/cloudformation/engine/yaml_parser.py +9 -2
- localstack/services/cloudformation/provider.py +7 -5
- localstack/services/cloudformation/resource_provider.py +7 -1
- localstack/services/cloudformation/resources.py +24149 -0
- localstack/services/cloudformation/service_models.py +2 -2
- localstack/services/cloudformation/v2/entities.py +19 -9
- localstack/services/cloudformation/v2/provider.py +336 -106
- localstack/services/cloudformation/v2/types.py +13 -7
- localstack/services/cloudformation/v2/utils.py +4 -1
- localstack/services/cloudwatch/alarm_scheduler.py +4 -1
- localstack/services/cloudwatch/provider.py +18 -13
- localstack/services/cloudwatch/provider_v2.py +25 -28
- localstack/services/dynamodb/packages.py +2 -1
- localstack/services/dynamodb/provider.py +42 -0
- localstack/services/dynamodb/server.py +2 -2
- localstack/services/dynamodb/v2/provider.py +42 -0
- localstack/services/ecr/resource_providers/aws_ecr_repository.py +5 -2
- localstack/services/edge.py +1 -1
- localstack/services/es/provider.py +2 -2
- localstack/services/events/event_rule_engine.py +31 -13
- localstack/services/events/models.py +4 -5
- localstack/services/events/provider.py +17 -14
- localstack/services/events/target.py +17 -9
- localstack/services/events/v1/provider.py +5 -5
- localstack/services/firehose/provider.py +14 -4
- localstack/services/iam/provider.py +11 -116
- localstack/services/iam/resources/policy_simulator.py +133 -0
- localstack/services/kinesis/models.py +15 -2
- localstack/services/kinesis/provider.py +86 -3
- localstack/services/kms/provider.py +14 -5
- localstack/services/lambda_/api_utils.py +6 -3
- localstack/services/lambda_/invocation/docker_runtime_executor.py +1 -1
- localstack/services/lambda_/invocation/event_manager.py +1 -1
- localstack/services/lambda_/invocation/internal_sqs_queue.py +5 -9
- localstack/services/lambda_/invocation/lambda_models.py +10 -7
- localstack/services/lambda_/invocation/lambda_service.py +5 -1
- localstack/services/lambda_/packages.py +1 -1
- localstack/services/lambda_/provider.py +4 -3
- localstack/services/lambda_/provider_utils.py +1 -1
- localstack/services/logs/provider.py +36 -19
- localstack/services/moto.py +2 -1
- localstack/services/opensearch/cluster.py +15 -7
- localstack/services/opensearch/packages.py +26 -7
- localstack/services/opensearch/provider.py +8 -2
- localstack/services/opensearch/versions.py +56 -7
- localstack/services/plugins.py +11 -7
- localstack/services/providers.py +10 -2
- localstack/services/redshift/provider.py +0 -21
- localstack/services/s3/constants.py +5 -2
- localstack/services/s3/cors.py +4 -4
- localstack/services/s3/models.py +1 -1
- localstack/services/s3/notifications.py +55 -39
- localstack/services/s3/presigned_url.py +35 -54
- localstack/services/s3/provider.py +73 -15
- localstack/services/s3/utils.py +42 -22
- localstack/services/s3/validation.py +46 -32
- localstack/services/s3/website_hosting.py +4 -2
- localstack/services/ses/provider.py +18 -8
- localstack/services/sns/constants.py +7 -1
- localstack/services/sns/executor.py +9 -2
- localstack/services/sns/provider.py +8 -5
- localstack/services/sns/publisher.py +31 -16
- localstack/services/sns/v2/models.py +167 -0
- localstack/services/sns/v2/provider.py +867 -0
- localstack/services/sns/v2/utils.py +130 -0
- localstack/services/sqs/constants.py +1 -1
- localstack/services/sqs/developer_api.py +205 -0
- localstack/services/sqs/models.py +48 -5
- localstack/services/sqs/provider.py +38 -311
- localstack/services/sqs/query_api.py +6 -2
- localstack/services/sqs/utils.py +121 -2
- localstack/services/ssm/provider.py +1 -1
- localstack/services/stepfunctions/asl/component/intrinsic/member.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_choice/comparison/comparison.py +5 -11
- localstack/services/stepfunctions/asl/component/state/state_choice/state_choice.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/state_parallel.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_pass/state_pass.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_succeed/state_succeed.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_wait/state_wait.py +1 -1
- localstack/services/stepfunctions/asl/eval/environment.py +1 -1
- localstack/services/stepfunctions/asl/jsonata/jsonata.py +1 -1
- localstack/services/stepfunctions/backend/execution.py +2 -1
- localstack/services/stores.py +1 -1
- localstack/services/transcribe/provider.py +6 -1
- localstack/state/codecs.py +61 -0
- localstack/state/core.py +11 -5
- localstack/state/pickle.py +10 -49
- localstack/testing/aws/cloudformation_utils.py +1 -1
- localstack/testing/pytest/cloudformation/fixtures.py +3 -3
- localstack/testing/pytest/cloudformation/transformers.py +0 -0
- localstack/testing/pytest/container.py +4 -5
- localstack/testing/pytest/fixtures.py +33 -31
- localstack/testing/pytest/in_memory_localstack.py +0 -4
- localstack/testing/pytest/marking.py +38 -11
- localstack/testing/pytest/stepfunctions/utils.py +4 -3
- localstack/testing/pytest/util.py +1 -1
- localstack/testing/pytest/validation_tracking.py +1 -2
- localstack/testing/snapshots/transformer_utility.py +6 -1
- localstack/utils/analytics/events.py +2 -2
- localstack/utils/analytics/metadata.py +6 -4
- localstack/utils/analytics/metrics/counter.py +8 -15
- localstack/utils/analytics/publisher.py +1 -2
- localstack/utils/analytics/service_providers.py +19 -0
- localstack/utils/analytics/service_request_aggregator.py +2 -2
- localstack/utils/archives.py +11 -11
- localstack/utils/asyncio.py +2 -2
- localstack/utils/aws/arns.py +24 -29
- localstack/utils/aws/aws_responses.py +8 -8
- localstack/utils/aws/aws_stack.py +2 -3
- localstack/utils/aws/dead_letter_queue.py +1 -5
- localstack/utils/aws/message_forwarding.py +1 -2
- localstack/utils/aws/request_context.py +4 -5
- localstack/utils/aws/resources.py +1 -1
- localstack/utils/aws/templating.py +1 -1
- localstack/utils/batch_policy.py +3 -3
- localstack/utils/bootstrap.py +21 -13
- localstack/utils/catalog/catalog.py +139 -0
- localstack/utils/catalog/catalog_loader.py +119 -0
- localstack/utils/catalog/common.py +58 -0
- localstack/utils/catalog/plugins.py +28 -0
- localstack/utils/cloudwatch/cloudwatch_util.py +5 -5
- localstack/utils/collections.py +7 -8
- localstack/utils/config_listener.py +1 -1
- localstack/utils/container_networking.py +2 -3
- localstack/utils/container_utils/container_client.py +135 -136
- localstack/utils/container_utils/docker_cmd_client.py +85 -69
- localstack/utils/container_utils/docker_sdk_client.py +69 -66
- localstack/utils/crypto.py +10 -10
- localstack/utils/diagnose.py +3 -4
- localstack/utils/docker_utils.py +9 -5
- localstack/utils/files.py +33 -13
- localstack/utils/functions.py +4 -3
- localstack/utils/http.py +11 -11
- localstack/utils/json.py +20 -6
- localstack/utils/kinesis/kinesis_connector.py +2 -1
- localstack/utils/net.py +15 -9
- localstack/utils/no_exit_argument_parser.py +2 -2
- localstack/utils/numbers.py +9 -2
- localstack/utils/objects.py +7 -6
- localstack/utils/patch.py +10 -3
- localstack/utils/run.py +12 -11
- localstack/utils/scheduler.py +11 -11
- localstack/utils/server/tcp_proxy.py +2 -2
- localstack/utils/serving.py +3 -4
- localstack/utils/strings.py +15 -16
- localstack/utils/sync.py +126 -1
- localstack/utils/tagging.py +8 -6
- localstack/utils/testutil.py +8 -8
- localstack/utils/threads.py +2 -2
- localstack/utils/time.py +12 -4
- localstack/utils/urls.py +1 -3
- localstack/utils/xray/traceid.py +1 -1
- localstack/version.py +16 -3
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/METADATA +18 -14
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/RECORD +248 -239
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/entry_points.txt +8 -4
- localstack_core-4.10.1.dev12.dist-info/plux.json +1 -0
- localstack/packages/terraform.py +0 -46
- localstack/services/cloudformation/deploy.html +0 -144
- localstack/services/cloudformation/deploy_ui.py +0 -47
- localstack/services/cloudformation/plugins.py +0 -12
- localstack_core-4.7.1.dev49.dist-info/plux.json +0 -1
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack +0 -0
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/WHEEL +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/top_level.txt +0 -0
localstack/runtime/patches.py
CHANGED
|
@@ -38,14 +38,14 @@ def patch_urllib3_connection_pool(**constructor_kwargs):
|
|
|
38
38
|
class MyHTTPSConnectionPool(connectionpool.HTTPSConnectionPool):
|
|
39
39
|
def __init__(self, *args, **kwargs):
|
|
40
40
|
kwargs.update(constructor_kwargs)
|
|
41
|
-
super(
|
|
41
|
+
super().__init__(*args, **kwargs)
|
|
42
42
|
|
|
43
43
|
poolmanager.pool_classes_by_scheme["https"] = MyHTTPSConnectionPool
|
|
44
44
|
|
|
45
45
|
class MyHTTPConnectionPool(connectionpool.HTTPConnectionPool):
|
|
46
46
|
def __init__(self, *args, **kwargs):
|
|
47
47
|
kwargs.update(constructor_kwargs)
|
|
48
|
-
super(
|
|
48
|
+
super().__init__(*args, **kwargs)
|
|
49
49
|
|
|
50
50
|
poolmanager.pool_classes_by_scheme["http"] = MyHTTPConnectionPool
|
|
51
51
|
except Exception:
|
|
@@ -367,10 +367,7 @@ def resolve_references(data: dict, rest_api_id, allow_recursive=True) -> dict:
|
|
|
367
367
|
|
|
368
368
|
def path_based_url(api_id: str, stage_name: str, path: str) -> str:
|
|
369
369
|
"""Return URL for inbound API gateway for given API ID, stage name, and path"""
|
|
370
|
-
pattern = "
|
|
371
|
-
config.external_service_url(),
|
|
372
|
-
PATH_USER_REQUEST,
|
|
373
|
-
)
|
|
370
|
+
pattern = f"{config.external_service_url()}/restapis/{{api_id}}/{{stage_name}}/{PATH_USER_REQUEST}{{path}}"
|
|
374
371
|
return pattern.format(api_id=api_id, stage_name=stage_name, path=path)
|
|
375
372
|
|
|
376
373
|
|
|
@@ -37,7 +37,7 @@ PATH_REGEX_TEST_INVOKE_API = r"^\/restapis\/([A-Za-z0-9_\-]+)\/resources\/([A-Za
|
|
|
37
37
|
|
|
38
38
|
# regex path pattern for user requests, handles stages like $default
|
|
39
39
|
PATH_REGEX_USER_REQUEST = (
|
|
40
|
-
|
|
40
|
+
rf"^/restapis/([A-Za-z0-9_\\-]+)(?:/([A-Za-z0-9\_($|%24)\\-]+))?/{PATH_USER_REQUEST}/(.*)$"
|
|
41
41
|
)
|
|
42
42
|
# URL pattern for invocations
|
|
43
43
|
HOST_REGEX_EXECUTE_API = r"(?:.*://)?([a-zA-Z0-9]+)(?:(-vpce-[^.]+))?\.execute-api\.(.*)"
|
|
@@ -375,12 +375,12 @@ def get_apigateway_path_for_resource(
|
|
|
375
375
|
path_part = target_resource.get("pathPart", "")
|
|
376
376
|
if path_suffix:
|
|
377
377
|
if path_part:
|
|
378
|
-
path_suffix = "
|
|
378
|
+
path_suffix = f"{path_part}/{path_suffix}"
|
|
379
379
|
else:
|
|
380
380
|
path_suffix = path_part
|
|
381
381
|
parent_id = target_resource.get("parentId")
|
|
382
382
|
if not parent_id:
|
|
383
|
-
return "
|
|
383
|
+
return f"/{path_suffix}"
|
|
384
384
|
return get_apigateway_path_for_resource(
|
|
385
385
|
api_id,
|
|
386
386
|
parent_id,
|
|
@@ -419,7 +419,7 @@ def get_resource_for_path(
|
|
|
419
419
|
for api_path, details in path_map.items():
|
|
420
420
|
api_path_regex = re.sub(r"{[^+]+\+}", r"[^\?#]+", api_path)
|
|
421
421
|
api_path_regex = re.sub(r"{[^}]+}", r"[^/]+", api_path_regex)
|
|
422
|
-
if re.match(
|
|
422
|
+
if re.match(rf"^{api_path_regex}$", path):
|
|
423
423
|
matches.append((api_path, details))
|
|
424
424
|
|
|
425
425
|
# if there are no matches, it's not worth to proceed, bail here!
|
|
@@ -508,8 +508,7 @@ def connect_api_gateway_to_sqs(gateway_name, stage_name, queue_arn, path, accoun
|
|
|
508
508
|
"integrations": [
|
|
509
509
|
{
|
|
510
510
|
"type": "AWS",
|
|
511
|
-
"uri": "arn
|
|
512
|
-
% (partition, sqs_region, sqs_account, queue_name),
|
|
511
|
+
"uri": f"arn:{partition}:apigateway:{sqs_region}:sqs:path/{sqs_account}/{queue_name}",
|
|
513
512
|
"requestTemplates": {"application/json": template},
|
|
514
513
|
"requestParameters": {
|
|
515
514
|
"integration.request.header.Content-Type": "'application/x-www-form-urlencoded'"
|
|
@@ -660,11 +659,11 @@ def set_api_id_stage_invocation_path(
|
|
|
660
659
|
if path_match:
|
|
661
660
|
api_id = path_match.group(1)
|
|
662
661
|
stage = path_match.group(2)
|
|
663
|
-
relative_path_w_query_params = "
|
|
662
|
+
relative_path_w_query_params = f"/{path_match.group(3)}"
|
|
664
663
|
elif host_match:
|
|
665
664
|
api_id = extract_api_id_from_hostname_in_url(host_header)
|
|
666
665
|
stage = path.strip("/").split("/")[0]
|
|
667
|
-
relative_path_w_query_params = "
|
|
666
|
+
relative_path_w_query_params = "/{}".format(path.lstrip("/").partition("/")[2])
|
|
668
667
|
elif test_invoke_match:
|
|
669
668
|
stage = invocation_context.stage
|
|
670
669
|
api_id = invocation_context.api_id
|
|
@@ -783,7 +783,7 @@ class HTTPIntegration(BackendIntegration):
|
|
|
783
783
|
instances = client.list_instances(ServiceId=service_id)["Instances"]
|
|
784
784
|
instance = (instances or [None])[0]
|
|
785
785
|
if instance and instance.get("Id"):
|
|
786
|
-
uri = "http
|
|
786
|
+
uri = "http://{}/{}".format(instance["Id"], invocation_path.lstrip("/"))
|
|
787
787
|
|
|
788
788
|
# apply custom request template
|
|
789
789
|
invocation_context.context = get_event_request_context(invocation_context)
|
|
@@ -977,8 +977,9 @@ class StepFunctionIntegration(BackendIntegration):
|
|
|
977
977
|
headers={"Content-Type": APPLICATION_JSON},
|
|
978
978
|
data=json.dumps(
|
|
979
979
|
{
|
|
980
|
-
"message": "StepFunctions execution
|
|
981
|
-
|
|
980
|
+
"message": "StepFunctions execution {} failed with status '{}'".format(
|
|
981
|
+
result["executionArn"], result_status
|
|
982
|
+
)
|
|
982
983
|
}
|
|
983
984
|
),
|
|
984
985
|
)
|
|
@@ -142,8 +142,9 @@ class RequestValidator:
|
|
|
142
142
|
# try to get the resolved model first
|
|
143
143
|
resolved_schema = model_resolver.get_resolved_model()
|
|
144
144
|
if not resolved_schema:
|
|
145
|
-
LOG.
|
|
146
|
-
"An exception occurred while trying to validate the request: could not find the model"
|
|
145
|
+
LOG.error(
|
|
146
|
+
"An exception occurred while trying to validate the request: could not find the model",
|
|
147
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
147
148
|
)
|
|
148
149
|
return False
|
|
149
150
|
|
|
@@ -279,7 +280,7 @@ def invoke_rest_api(invocation_context: ApiInvocationContext):
|
|
|
279
280
|
|
|
280
281
|
extracted_path, resource = get_target_resource_details(invocation_context)
|
|
281
282
|
if not resource:
|
|
282
|
-
return make_error_response("Unable to find path
|
|
283
|
+
return make_error_response(f"Unable to find path {invocation_context.path}", 404)
|
|
283
284
|
|
|
284
285
|
# validate request
|
|
285
286
|
validator = RequestValidator(invocation_context)
|
|
@@ -306,7 +307,7 @@ def invoke_rest_api(invocation_context: ApiInvocationContext):
|
|
|
306
307
|
# default to returning CORS headers if this is an OPTIONS request
|
|
307
308
|
return get_cors_response(headers)
|
|
308
309
|
return make_error_response(
|
|
309
|
-
"Unable to find integration for:
|
|
310
|
+
f"Unable to find integration for: {method} {invocation_path} ({raw_path})",
|
|
310
311
|
404,
|
|
311
312
|
)
|
|
312
313
|
|
|
@@ -334,7 +335,7 @@ def invoke_rest_api_integration(invocation_context: ApiInvocationContext):
|
|
|
334
335
|
return e.to_response()
|
|
335
336
|
except Exception as e:
|
|
336
337
|
msg = f"Error invoking integration for API Gateway ID '{invocation_context.api_id}': {e}"
|
|
337
|
-
LOG.
|
|
338
|
+
LOG.error(msg, exc_info=LOG.isEnabledFor(logging.DEBUG))
|
|
338
339
|
return make_error_response(msg, 400)
|
|
339
340
|
|
|
340
341
|
|
|
@@ -323,8 +323,18 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
323
323
|
tags: MapOfStringToString = None,
|
|
324
324
|
**kwargs,
|
|
325
325
|
) -> ApiKey:
|
|
326
|
+
if name and len(name) > 1024:
|
|
327
|
+
raise BadRequestException("Invalid API Key name, can be at most 1024 characters.")
|
|
328
|
+
if value:
|
|
329
|
+
if len(value) > 128:
|
|
330
|
+
raise BadRequestException("API Key value exceeds maximum size of 128 characters")
|
|
331
|
+
elif len(value) < 20:
|
|
332
|
+
raise BadRequestException("API Key value should be at least 20 characters")
|
|
333
|
+
if description and len(description) > 125000:
|
|
334
|
+
raise BadRequestException("Invalid API Key description specified.")
|
|
326
335
|
api_key = call_moto(context)
|
|
327
|
-
|
|
336
|
+
if name == "":
|
|
337
|
+
api_key.pop("name", None)
|
|
328
338
|
# transform array of stage keys [{'restApiId': '0iscapk09u', 'stageName': 'dev'}] into
|
|
329
339
|
# array of strings ['0iscapk09u/dev']
|
|
330
340
|
stage_keys = api_key.get("stageKeys", [])
|
|
@@ -665,66 +675,6 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
665
675
|
parent_id = moto_resource.parent_id
|
|
666
676
|
api_resources[parent_id].remove(resource_id)
|
|
667
677
|
|
|
668
|
-
def update_integration_response(
|
|
669
|
-
self,
|
|
670
|
-
context: RequestContext,
|
|
671
|
-
rest_api_id: String,
|
|
672
|
-
resource_id: String,
|
|
673
|
-
http_method: String,
|
|
674
|
-
status_code: StatusCode,
|
|
675
|
-
patch_operations: ListOfPatchOperation = None,
|
|
676
|
-
**kwargs,
|
|
677
|
-
) -> IntegrationResponse:
|
|
678
|
-
# XXX: THIS IS NOT A COMPLETE IMPLEMENTATION, just the minimum required to get tests going
|
|
679
|
-
# TODO: validate patch operations
|
|
680
|
-
|
|
681
|
-
moto_rest_api = get_moto_rest_api(context, rest_api_id)
|
|
682
|
-
moto_resource = moto_rest_api.resources.get(resource_id)
|
|
683
|
-
if not moto_resource:
|
|
684
|
-
raise NotFoundException("Invalid Resource identifier specified")
|
|
685
|
-
|
|
686
|
-
moto_method = moto_resource.resource_methods.get(http_method)
|
|
687
|
-
if not moto_method:
|
|
688
|
-
raise NotFoundException("Invalid Method identifier specified")
|
|
689
|
-
|
|
690
|
-
integration_response = moto_method.method_integration.integration_responses.get(status_code)
|
|
691
|
-
if not integration_response:
|
|
692
|
-
raise NotFoundException("Invalid Integration Response identifier specified")
|
|
693
|
-
|
|
694
|
-
for patch_operation in patch_operations:
|
|
695
|
-
op = patch_operation.get("op")
|
|
696
|
-
path = patch_operation.get("path")
|
|
697
|
-
|
|
698
|
-
# for path "/responseTemplates/application~1json"
|
|
699
|
-
if "/responseTemplates" in path:
|
|
700
|
-
integration_response.response_templates = (
|
|
701
|
-
integration_response.response_templates or {}
|
|
702
|
-
)
|
|
703
|
-
value = patch_operation.get("value")
|
|
704
|
-
if not isinstance(value, str):
|
|
705
|
-
raise BadRequestException(
|
|
706
|
-
f"Invalid patch value '{value}' specified for op '{op}'. Must be a string"
|
|
707
|
-
)
|
|
708
|
-
param = path.removeprefix("/responseTemplates/")
|
|
709
|
-
param = param.replace("~1", "/")
|
|
710
|
-
if op == "remove":
|
|
711
|
-
integration_response.response_templates.pop(param)
|
|
712
|
-
elif op in ("add", "replace"):
|
|
713
|
-
integration_response.response_templates[param] = value
|
|
714
|
-
|
|
715
|
-
elif "/contentHandling" in path and op == "replace":
|
|
716
|
-
integration_response.content_handling = patch_operation.get("value")
|
|
717
|
-
|
|
718
|
-
elif "/selectionPattern" in path and op == "replace":
|
|
719
|
-
integration_response.selection_pattern = patch_operation.get("value")
|
|
720
|
-
|
|
721
|
-
response: IntegrationResponse = integration_response.to_json()
|
|
722
|
-
# in case it's empty, we still want to pass it on as ""
|
|
723
|
-
# TODO: add a test case for this
|
|
724
|
-
response["selectionPattern"] = integration_response.selection_pattern
|
|
725
|
-
|
|
726
|
-
return response
|
|
727
|
-
|
|
728
678
|
def update_resource(
|
|
729
679
|
self,
|
|
730
680
|
context: RequestContext,
|
|
@@ -757,6 +707,9 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
757
707
|
f"Invalid patch path '{path}' specified for op '{op}'. Please choose supported operations"
|
|
758
708
|
)
|
|
759
709
|
|
|
710
|
+
if moto_resource.parent_id is None:
|
|
711
|
+
raise BadRequestException(f"Root resource cannot update its {path.strip('/')}.")
|
|
712
|
+
|
|
760
713
|
if path == "/parentId":
|
|
761
714
|
value = patch_operation.get("value")
|
|
762
715
|
future_parent_resource = moto_rest_api.resources.get(value)
|
|
@@ -793,7 +746,7 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
793
746
|
api_resources.pop(current_parent_id)
|
|
794
747
|
|
|
795
748
|
# add it to the new parent children
|
|
796
|
-
future_sibling_resources = api_resources
|
|
749
|
+
future_sibling_resources = api_resources.setdefault(moto_resource.parent_id, [])
|
|
797
750
|
future_sibling_resources.append(resource_id)
|
|
798
751
|
|
|
799
752
|
response = moto_resource.to_dict()
|
|
@@ -2111,6 +2064,20 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
2111
2064
|
for integration_response in integration_responses.values():
|
|
2112
2065
|
remove_empty_attributes_from_integration_response(integration_response)
|
|
2113
2066
|
|
|
2067
|
+
if response.get("connectionType") == "VPC_LINK":
|
|
2068
|
+
# FIXME: this is hacky to workaround moto not saving the VPC Link `connectionId`
|
|
2069
|
+
# only do this internal check of Moto if the integration is of VPC_LINK type
|
|
2070
|
+
moto_rest_api = get_moto_rest_api(context=context, rest_api_id=rest_api_id)
|
|
2071
|
+
try:
|
|
2072
|
+
method = moto_rest_api.resources[resource_id].resource_methods[http_method]
|
|
2073
|
+
integration = method.method_integration
|
|
2074
|
+
if connection_id := getattr(integration, "connection_id", None):
|
|
2075
|
+
response["connectionId"] = connection_id
|
|
2076
|
+
|
|
2077
|
+
except (AttributeError, KeyError):
|
|
2078
|
+
# this error should have been caught by `call_moto`
|
|
2079
|
+
pass
|
|
2080
|
+
|
|
2114
2081
|
return response
|
|
2115
2082
|
|
|
2116
2083
|
def put_integration(
|
|
@@ -2165,6 +2132,7 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
2165
2132
|
moto_request.setdefault("timeoutInMillis", 29000)
|
|
2166
2133
|
if integration_type in (IntegrationType.HTTP, IntegrationType.HTTP_PROXY):
|
|
2167
2134
|
moto_request.setdefault("connectionType", ConnectionType.INTERNET)
|
|
2135
|
+
|
|
2168
2136
|
response = call_moto_with_request(context, moto_request)
|
|
2169
2137
|
remove_empty_attributes_from_integration(integration=response)
|
|
2170
2138
|
|
|
@@ -2172,6 +2140,13 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
2172
2140
|
if integration_type == "MOCK":
|
|
2173
2141
|
response.pop("uri", None)
|
|
2174
2142
|
|
|
2143
|
+
# TODO: moto does not save the connection_id
|
|
2144
|
+
elif moto_request.get("connectionType") == "VPC_LINK":
|
|
2145
|
+
connection_id = moto_request.get("connectionId", "")
|
|
2146
|
+
# attach the connection id to the moto object
|
|
2147
|
+
method.method_integration.connection_id = connection_id
|
|
2148
|
+
response["connectionId"] = connection_id
|
|
2149
|
+
|
|
2175
2150
|
return response
|
|
2176
2151
|
|
|
2177
2152
|
def update_integration(
|
|
@@ -2193,6 +2168,7 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
2193
2168
|
raise NotFoundException("Invalid Integration identifier specified")
|
|
2194
2169
|
|
|
2195
2170
|
integration = method.method_integration
|
|
2171
|
+
# TODO: validate the patch operations
|
|
2196
2172
|
patch_api_gateway_entity(integration, patch_operations)
|
|
2197
2173
|
|
|
2198
2174
|
# fix data types
|
|
@@ -2201,8 +2177,12 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
2201
2177
|
if skip_verification := (integration.tls_config or {}).get("insecureSkipVerification"):
|
|
2202
2178
|
integration.tls_config["insecureSkipVerification"] = str_to_bool(skip_verification)
|
|
2203
2179
|
|
|
2204
|
-
|
|
2205
|
-
|
|
2180
|
+
response: Integration = integration.to_json()
|
|
2181
|
+
|
|
2182
|
+
if connection_id := getattr(integration, "connection_id", None):
|
|
2183
|
+
response["connectionId"] = connection_id
|
|
2184
|
+
|
|
2185
|
+
return response
|
|
2206
2186
|
|
|
2207
2187
|
def delete_integration(
|
|
2208
2188
|
self,
|
|
@@ -2307,6 +2287,93 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
2307
2287
|
|
|
2308
2288
|
return response
|
|
2309
2289
|
|
|
2290
|
+
def update_integration_response(
|
|
2291
|
+
self,
|
|
2292
|
+
context: RequestContext,
|
|
2293
|
+
rest_api_id: String,
|
|
2294
|
+
resource_id: String,
|
|
2295
|
+
http_method: String,
|
|
2296
|
+
status_code: StatusCode,
|
|
2297
|
+
patch_operations: ListOfPatchOperation = None,
|
|
2298
|
+
**kwargs,
|
|
2299
|
+
) -> IntegrationResponse:
|
|
2300
|
+
# XXX: THIS IS NOT A COMPLETE IMPLEMENTATION, just the minimum required to get tests going
|
|
2301
|
+
# TODO: validate patch operations
|
|
2302
|
+
|
|
2303
|
+
moto_rest_api = get_moto_rest_api(context, rest_api_id)
|
|
2304
|
+
moto_resource = moto_rest_api.resources.get(resource_id)
|
|
2305
|
+
if not moto_resource:
|
|
2306
|
+
raise NotFoundException("Invalid Resource identifier specified")
|
|
2307
|
+
|
|
2308
|
+
moto_method = moto_resource.resource_methods.get(http_method)
|
|
2309
|
+
if not moto_method:
|
|
2310
|
+
raise NotFoundException("Invalid Method identifier specified")
|
|
2311
|
+
|
|
2312
|
+
integration_response = moto_method.method_integration.integration_responses.get(status_code)
|
|
2313
|
+
if not integration_response:
|
|
2314
|
+
raise NotFoundException("Invalid Integration Response identifier specified")
|
|
2315
|
+
|
|
2316
|
+
for patch_operation in patch_operations:
|
|
2317
|
+
op = patch_operation.get("op")
|
|
2318
|
+
path = patch_operation.get("path")
|
|
2319
|
+
|
|
2320
|
+
# for path "/responseTemplates/application~1json"
|
|
2321
|
+
if "/responseTemplates" in path:
|
|
2322
|
+
integration_response.response_templates = (
|
|
2323
|
+
integration_response.response_templates or {}
|
|
2324
|
+
)
|
|
2325
|
+
value = patch_operation.get("value")
|
|
2326
|
+
if not isinstance(value, str):
|
|
2327
|
+
raise BadRequestException(
|
|
2328
|
+
f"Invalid patch value '{value}' specified for op '{op}'. Must be a string"
|
|
2329
|
+
)
|
|
2330
|
+
param = path.removeprefix("/responseTemplates/")
|
|
2331
|
+
param = param.replace("~1", "/")
|
|
2332
|
+
if op == "remove":
|
|
2333
|
+
integration_response.response_templates.pop(param)
|
|
2334
|
+
elif op in ("add", "replace"):
|
|
2335
|
+
integration_response.response_templates[param] = value
|
|
2336
|
+
|
|
2337
|
+
elif "/contentHandling" in path and op == "replace":
|
|
2338
|
+
integration_response.content_handling = patch_operation.get("value")
|
|
2339
|
+
|
|
2340
|
+
elif "/selectionPattern" in path and op == "replace":
|
|
2341
|
+
integration_response.selection_pattern = patch_operation.get("value")
|
|
2342
|
+
|
|
2343
|
+
response: IntegrationResponse = integration_response.to_json()
|
|
2344
|
+
# in case it's empty, we still want to pass it on as ""
|
|
2345
|
+
# TODO: add a test case for this
|
|
2346
|
+
response["selectionPattern"] = integration_response.selection_pattern
|
|
2347
|
+
|
|
2348
|
+
return response
|
|
2349
|
+
|
|
2350
|
+
def delete_integration_response(
|
|
2351
|
+
self,
|
|
2352
|
+
context: RequestContext,
|
|
2353
|
+
rest_api_id: String,
|
|
2354
|
+
resource_id: String,
|
|
2355
|
+
http_method: String,
|
|
2356
|
+
status_code: StatusCode,
|
|
2357
|
+
**kwargs,
|
|
2358
|
+
) -> None:
|
|
2359
|
+
moto_backend = apigw_models.apigateway_backends[context.account_id][context.region]
|
|
2360
|
+
moto_rest_api = moto_backend.apis.get(rest_api_id)
|
|
2361
|
+
if not moto_rest_api:
|
|
2362
|
+
raise NotFoundException("Invalid Resource identifier specified")
|
|
2363
|
+
|
|
2364
|
+
if not (moto_resource := moto_rest_api.resources.get(resource_id)):
|
|
2365
|
+
raise NotFoundException("Invalid Resource identifier specified")
|
|
2366
|
+
|
|
2367
|
+
if not (moto_method := moto_resource.resource_methods.get(http_method)):
|
|
2368
|
+
raise NotFoundException("Invalid Integration identifier specified")
|
|
2369
|
+
|
|
2370
|
+
if not moto_method.method_integration:
|
|
2371
|
+
raise NotFoundException("Invalid Integration identifier specified")
|
|
2372
|
+
if not (
|
|
2373
|
+
integration_responses := moto_method.method_integration.integration_responses
|
|
2374
|
+
) or not integration_responses.pop(status_code, None):
|
|
2375
|
+
raise NotFoundException("Invalid Response status code specified")
|
|
2376
|
+
|
|
2310
2377
|
def get_export(
|
|
2311
2378
|
self,
|
|
2312
2379
|
context: RequestContext,
|
|
@@ -2363,6 +2430,10 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
2363
2430
|
for api_key in api_keys:
|
|
2364
2431
|
api_key.pop("value")
|
|
2365
2432
|
|
|
2433
|
+
if limit is not None:
|
|
2434
|
+
if limit < 1 or limit > 500:
|
|
2435
|
+
limit = None
|
|
2436
|
+
|
|
2366
2437
|
item_list = PaginatedList(api_keys)
|
|
2367
2438
|
|
|
2368
2439
|
def token_generator(item):
|
|
@@ -2387,6 +2458,14 @@ class ApigatewayProvider(ApigatewayApi, ServiceLifecycleHook):
|
|
|
2387
2458
|
patch_operations: ListOfPatchOperation = None,
|
|
2388
2459
|
**kwargs,
|
|
2389
2460
|
) -> ApiKey:
|
|
2461
|
+
for patch_op in patch_operations:
|
|
2462
|
+
if patch_op["path"] not in ("/description", "/enabled", "/name", "/customerId"):
|
|
2463
|
+
raise BadRequestException(
|
|
2464
|
+
f"Invalid patch path '{patch_op['path']}' specified for op '{patch_op['op']}'. Must be one of: [/description, /enabled, /name, /customerId]"
|
|
2465
|
+
)
|
|
2466
|
+
|
|
2467
|
+
if patch_op["path"] == "/description" and len(patch_op["value"]) > 125000:
|
|
2468
|
+
raise BadRequestException("Invalid API Key description specified.")
|
|
2390
2469
|
response: ApiKey = call_moto(context)
|
|
2391
2470
|
if "value" in response:
|
|
2392
2471
|
response.pop("value", None)
|
|
@@ -2947,6 +3026,7 @@ def create_custom_context(
|
|
|
2947
3026
|
ctx = create_aws_request_context(
|
|
2948
3027
|
service_name=context.service.service_name,
|
|
2949
3028
|
action=action,
|
|
3029
|
+
protocol=context.service.protocol,
|
|
2950
3030
|
parameters=parameters,
|
|
2951
3031
|
region=context.region,
|
|
2952
3032
|
)
|
|
@@ -2998,7 +3078,7 @@ def to_documentation_part_response_json(api_id, data):
|
|
|
2998
3078
|
|
|
2999
3079
|
|
|
3000
3080
|
def to_base_mapping_response_json(domain_name, base_path, data):
|
|
3001
|
-
self_link = "/domainnames
|
|
3081
|
+
self_link = f"/domainnames/{domain_name}/basepathmappings/{base_path}"
|
|
3002
3082
|
result = to_response_json("basepathmapping", data, self_link=self_link)
|
|
3003
3083
|
result = select_from_typed_dict(BasePathMapping, result)
|
|
3004
3084
|
return result
|
|
@@ -3034,9 +3114,9 @@ def to_response_json(model_type, data, api_id=None, self_link=None, id_attr=None
|
|
|
3034
3114
|
id_attr = id_attr or "id"
|
|
3035
3115
|
result = deepcopy(data)
|
|
3036
3116
|
if not self_link:
|
|
3037
|
-
self_link = "
|
|
3117
|
+
self_link = f"/{model_type}s/{data[id_attr]}"
|
|
3038
3118
|
if api_id:
|
|
3039
|
-
self_link = "/restapis
|
|
3119
|
+
self_link = f"/restapis/{api_id}/{self_link}"
|
|
3040
3120
|
# TODO: check if this is still required - "_links" are listed in the sample responses in the docs, but
|
|
3041
3121
|
# recent parity tests indicate that this field is not returned by real AWS...
|
|
3042
3122
|
# https://docs.aws.amazon.com/apigateway/latest/api/API_GetAuthorizers.html#API_GetAuthorizers_Example_1_Response
|
|
@@ -3048,7 +3128,7 @@ def to_response_json(model_type, data, api_id=None, self_link=None, id_attr=None
|
|
|
3048
3128
|
"name": model_type,
|
|
3049
3129
|
"templated": True,
|
|
3050
3130
|
}
|
|
3051
|
-
result["_links"]["
|
|
3131
|
+
result["_links"][f"{model_type}:delete"] = {"href": self_link}
|
|
3052
3132
|
return result
|
|
3053
3133
|
|
|
3054
3134
|
|
|
@@ -68,7 +68,7 @@ class AttributeDict(dict):
|
|
|
68
68
|
"""
|
|
69
69
|
|
|
70
70
|
def __init__(self, *args, **kwargs):
|
|
71
|
-
super(
|
|
71
|
+
super().__init__(*args, **kwargs)
|
|
72
72
|
for key, value in self.items():
|
|
73
73
|
if isinstance(value, dict):
|
|
74
74
|
self[key] = AttributeDict(value)
|
|
@@ -50,7 +50,11 @@ class MethodRequestHandler(RestApiGatewayHandler):
|
|
|
50
50
|
# check if there is a validator for this request
|
|
51
51
|
if not (validator := rest_api.validators.get(request_validator_id)):
|
|
52
52
|
# TODO Should we raise an exception instead?
|
|
53
|
-
LOG.
|
|
53
|
+
LOG.error(
|
|
54
|
+
"No validator were found with matching id: '%s'",
|
|
55
|
+
request_validator_id,
|
|
56
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
57
|
+
)
|
|
54
58
|
return
|
|
55
59
|
|
|
56
60
|
if self.should_validate_request(validator) and (
|
|
@@ -87,9 +91,10 @@ class MethodRequestHandler(RestApiGatewayHandler):
|
|
|
87
91
|
# try to get the resolved model first
|
|
88
92
|
resolved_schema = model_resolver.get_resolved_model()
|
|
89
93
|
if not resolved_schema:
|
|
90
|
-
LOG.
|
|
94
|
+
LOG.error(
|
|
91
95
|
"An exception occurred while trying to validate the request: could not resolve the model '%s'",
|
|
92
96
|
model_name,
|
|
97
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
93
98
|
)
|
|
94
99
|
return False
|
|
95
100
|
|
|
@@ -85,12 +85,11 @@ class RestAPIResourceRouter:
|
|
|
85
85
|
rule, args = matcher.match(path, method=request.method, return_rule=True)
|
|
86
86
|
except (MethodNotAllowed, NotFound) as e:
|
|
87
87
|
# MethodNotAllowed (405) exception is raised if a path is matching, but the method does not.
|
|
88
|
-
#
|
|
88
|
+
# AWS handles this and the regular 404 as a '403 MissingAuthTokenError'
|
|
89
89
|
LOG.warning(
|
|
90
90
|
"API Gateway: No resource or method was found for: %s %s",
|
|
91
91
|
request.method,
|
|
92
92
|
path,
|
|
93
|
-
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
94
93
|
)
|
|
95
94
|
raise MissingAuthTokenError("Missing Authentication Token") from e
|
|
96
95
|
|
|
@@ -211,6 +211,9 @@ class RestApiAwsIntegration(RestApiIntegration):
|
|
|
211
211
|
action = parsed_uri["path"]
|
|
212
212
|
|
|
213
213
|
if target := self.get_action_service_target(service_name, action):
|
|
214
|
+
# TODO: properly implement the auto-`Content-Type` headers depending on the service protocol
|
|
215
|
+
# e.g. `x-amz-json-1.0` for DynamoDB
|
|
216
|
+
# this is needed to properly support multi-protocol
|
|
214
217
|
headers["X-Amz-Target"] = target
|
|
215
218
|
|
|
216
219
|
query_params["Action"] = action
|
|
@@ -8,7 +8,7 @@ from werkzeug.datastructures import Headers
|
|
|
8
8
|
from localstack.aws.api.apigateway import Integration
|
|
9
9
|
|
|
10
10
|
from ..context import EndpointResponse, IntegrationRequest, RestApiInvocationContext
|
|
11
|
-
from ..gateway_response import ApiConfigurationError, IntegrationFailureError
|
|
11
|
+
from ..gateway_response import ApiConfigurationError, IntegrationFailureError, InternalServerError
|
|
12
12
|
from ..header_utils import build_multi_value_headers
|
|
13
13
|
from .core import RestApiIntegration
|
|
14
14
|
|
|
@@ -72,7 +72,7 @@ class RestApiHttpIntegration(BaseRestApiHttpIntegration):
|
|
|
72
72
|
except (requests.exceptions.InvalidURL, requests.exceptions.InvalidSchema) as e:
|
|
73
73
|
LOG.warning("Execution failed due to configuration error: Invalid endpoint address")
|
|
74
74
|
LOG.debug("The URI specified for the HTTP/HTTP_PROXY integration is invalid: %s", uri)
|
|
75
|
-
raise
|
|
75
|
+
raise InternalServerError("Internal server error") from e
|
|
76
76
|
|
|
77
77
|
except (requests.exceptions.Timeout, requests.exceptions.SSLError) as e:
|
|
78
78
|
# TODO make the exception catching more fine grained
|
|
@@ -127,7 +127,7 @@ class RestApiHttpProxyIntegration(BaseRestApiHttpIntegration):
|
|
|
127
127
|
except (requests.exceptions.InvalidURL, requests.exceptions.InvalidSchema) as e:
|
|
128
128
|
LOG.warning("Execution failed due to configuration error: Invalid endpoint address")
|
|
129
129
|
LOG.debug("The URI specified for the HTTP/HTTP_PROXY integration is invalid: %s", uri)
|
|
130
|
-
raise
|
|
130
|
+
raise InternalServerError("Internal server error") from e
|
|
131
131
|
|
|
132
132
|
except (requests.exceptions.Timeout, requests.exceptions.SSLError):
|
|
133
133
|
# TODO make the exception catching more fine grained
|
|
@@ -109,7 +109,7 @@ class VTLJsonList(list):
|
|
|
109
109
|
"""
|
|
110
110
|
|
|
111
111
|
def __init__(self, *args):
|
|
112
|
-
super(
|
|
112
|
+
super().__init__(*args)
|
|
113
113
|
for idx, item in enumerate(self):
|
|
114
114
|
self[idx] = cast_to_vtl_json_object(item)
|
|
115
115
|
|
|
@@ -138,7 +138,7 @@ class AttributeDict(dict):
|
|
|
138
138
|
"""
|
|
139
139
|
|
|
140
140
|
def __init__(self, *args, **kwargs):
|
|
141
|
-
super(
|
|
141
|
+
super().__init__(*args, **kwargs)
|
|
142
142
|
for key, value in self.items():
|
|
143
143
|
if isinstance(value, dict):
|
|
144
144
|
self[key] = AttributeDict(value)
|