Repository: klothoplatform/klotho
Pull request #897 (2024-01-22 21:54:07)

only consider edges reused if not a superset path

making sure edges arent part of a superset path when determining if they should affect edge deletion in path removal


testing repro example
```
constraints:
- operator: must_not_exist
  scope: edge
  target:
    source: aws:api_integration:rest_api_0:rest_api_0_integration_0
    target: aws:lambda_function:lambda_function_2
resources:
    aws:api_stage:rest_api_0:api_stage-0:
        Deployment: aws:api_deployment:rest_api_0:api_deployment-0
        RestApi: aws:rest_api:rest_api_0
        StageName: stage
    aws:api_deployment:rest_api_0:api_deployment-0:
        RestApi: aws:rest_api:rest_api_0
        Triggers:
            rest_api_0_integration_0: rest_api_0_integration_0
            rest_api_0_integration_0_method: rest_api_0_integration_0_method
    aws:rest_api:rest_api_0:
        BinaryMediaTypes:
            - application/octet-stream
            - image/*
    aws:api_resource:rest_api_0:api_resource-0:
        FullPath: /{proxy+}
        PathPart: '{proxy+}'
        RestApi: aws:rest_api:rest_api_0
    aws:api_method:rest_api_0:rest_api_0_integration_0_method:
        Authorization: NONE
        HttpMethod: ANY
        RequestParameters:
            method.request.path.proxy: true
        Resource: aws:api_resource:rest_api_0:api_resource-0
        RestApi: aws:rest_api:rest_api_0
    aws:api_integration:rest_api_0:rest_api_0_integration_0:
        IntegrationHttpMethod: POST
        Method: aws:api_method:rest_api_0:rest_api_0_integration_0_method
        RequestParameters:
            integration.request.path.proxy: method.request.path.proxy
        Resource: aws:api_resource:rest_api_0:api_resource-0
        RestApi: aws:rest_api:rest_api_0
        Route: /{proxy+}
        Target: aws:lambda_function:lambda_function_2
        Type: AWS_PROXY
        Uri: aws:lambda_function:lambda_function_2#LambdaIntegrationUri
    aws:lambda_permission:rest_api_0_integration_0-lambda_function_2:
        Action: lambda:InvokeFunction
        Function: aws:lambda_function:lambda_function_2
        Principal: apigateway.amazonaws.com
        Source: aws:rest_api:rest_api_0#ChildResources
    aws:lambda_function:lambda_function_2:
        ExecutionRole: aws:iam_role:lambda_function_2-ExecutionRole
        Image: aws:ecr_image:lambda_function_2-image
        LogGroup: aws:log_group:lambda_function_2-log-group
        MemorySize: 512
        Timeout: 180
    aws:SERVICE_API:lambda_function_2-lambda_function_2-log-group:
    aws:ecr_image:lambda_function_2-image:
        Context: .
        Dockerfile: lambda_function_2-image.Dockerfile
        Repo: aws:ecr_repo:ecr_repo-0
    aws:iam_role:lambda_function_2-ExecutionRole:
        AssumeRolePolicyDoc:
            Statement:
                - Action:
                    - sts:AssumeRole
                  Effect: Allow
                  Principal:
                    Service:
                        - lambda.amazonaws.com
            Version: "2012-10-17"
        ManagedPolicies:
            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
    aws:ecr_repo:ecr_repo-0:
        ForceDelete: true
    aws:log_group:lambda_function_2-log-group:
        LogGroupName: /aws/lambda/lambda_function_2
        RetentionInDays: 5
edges:
    aws:api_stage:rest_api_0:api_stage-0 -> aws:api_deployment:rest_api_0:api_deployment-0:
    aws:api_stage:rest_api_0:api_stage-0 -> aws:rest_api:rest_api_0:
    aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
    aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_method:rest_api_0:rest_api_0_integration_0_method:
    aws:api_deployment:rest_api_0:api_deployment-0 -> aws:rest_api:rest_api_0:
    aws:rest_api:rest_api_0 -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
    aws:rest_api:rest_api_0 -> aws:api_method:rest_api_0:rest_api_0_integration_0_method:
    aws:rest_api:rest_api_0 -> aws:api_resource:rest_api_0:api_resource-0:
    aws:api_resource:rest_api_0:api_resource-0 -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
    aws:api_resource:rest_api_0:api_resource-0 -> aws:api_method:rest_api_0:rest_api_0_integration_0_method:
    aws:api_method:rest_api_0:rest_api_0_integration_0_method -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
    aws:api_integration:rest_api_0:rest_api_0_integration_0 -> aws:lambda_permission:rest_api_0_integration_0-lambda_function_2:
    aws:lambda_permission:rest_api_0_integration_0-lambda_function_2 -> aws:lambda_function:lambda_function_2:
    aws:lambda_function:lambda_function_2 -> aws:SERVICE_API:lambda_function_2-lambda_function_2-log-group:
    aws:lambda_function:lambda_function_2 -> aws:ecr_image:lambda_function_2-image:
    aws:lambda_function:lambda_function_2 -> aws:iam_role:lambda_function_2-ExecutionRole:
    aws:SERVICE_API:lambda_function_2-lambda_function_2-log-group -> aws:log_group:lambda_function_2-log-group:
    aws:ecr_image:lambda_function_2-image -> aws:ecr_repo:ecr_repo-0:
    aws:iam_role:lambda_function_2-ExecutionRole -> aws:log_group:lambda_function_2-log-group:

```

### Standard checks

- **Unit tests**: Any special considerations?
- **Docs**: Do we need to update any docs, internal or public?
- **Backwards compatibility**: Will this break existing apps? If so, what would be the extra work required to keep them working?


jhsinger-klotho: > Can you put the repro input as a functional test case?

sure, but i think at some point the functional tests will not become maintainable if we add each specific use case to them, rather than just relying on unit tests, which i added to catch this case
gordon-klotho: > at some point the functional tests will not become maintainable if we add each specific use case to them, rather than just relying on unit tests, which i added to catch this case

I agree, but I don't think we have an edge removal case do we? Just a resource removal but maybe I'm mistaken

Patch:
diff --git a/pkg/engine2/reconciler/remove_edge.go b/pkg/engine2/reconciler/remove_edge.go
index 0a601905..64a2348e 100644
--- a/pkg/engine2/reconciler/remove_edge.go
+++ b/pkg/engine2/reconciler/remove_edge.go
@@ -47,7 +47,12 @@ func RemovePath(
 	// We will pass the explicit field as false so that explicitly added resources do not get deleted
 	for _, path := range paths {
 		for _, resource := range path {
-			errs = errors.Join(errs, RemoveResource(ctx, resource, false))
+			// by this point its possible the resource no longer exists due to being deleted by the removeSinglePath call
+			// since this is ensuring we dont orphan resources we can ignore the error if we do not find the resource
+			err := RemoveResource(ctx, resource, false)
+			if err != nil && !errors.Is(err, graph.ErrVertexNotFound) {
+				errs = errors.Join(errs, err)
+			}
 		}
 	}
 	return errs
@@ -161,6 +166,10 @@ func findEdgesUsedInOtherPathSelection(
 			continue
 		}
 		for _, path := range paths {
+			// if the source is in the path then the path is just a superset of the path we are trying to delete
+			if path.Contains(source) {
+				continue
+			}
 			for i, res := range path {
 				if i == 0 {
 					continue
@@ -197,6 +206,10 @@ func findEdgesUsedInOtherPathSelection(
 		}
 
 		for _, path := range paths {
+			// if the target is in the path then the path is just a superset of the path we are trying to delete
+			if path.Contains(target) {
+				continue
+			}
 			for i, res := range path {
 				if i == 0 {
 					continue


Test patch:
diff --git a/pkg/engine2/reconciler/remove_edge_test.go b/pkg/engine2/reconciler/remove_edge_test.go
index 25de518a..596834ec 100644
--- a/pkg/engine2/reconciler/remove_edge_test.go
+++ b/pkg/engine2/reconciler/remove_edge_test.go
@@ -139,6 +139,46 @@ func Test_findEdgesUsedInOtherPathSelection(t *testing.T) {
 				{Source: construct.ResourceId{Provider: "p", Type: "t", Name: "t"}, Target: construct.ResourceId{Provider: "p", Type: "t", Name: "b"}},
 			},
 		},
+		{
+			name:         "path is superset of deletion path",
+			source:       construct.ResourceId{Provider: "p", Type: "t", Name: "s"},
+			target:       construct.ResourceId{Provider: "p", Type: "t", Name: "b"},
+			initialState: []any{"p:t:s", "p:t:t", "p:t:t -> p:t:s", "p:t:a", "p:t:b", "p:t:a -> p:t:t", "p:t:s -> p:t:b", "p:t:l", "p:t:l -> p:t:b"},
+			want: []construct.SimpleEdge{
+				{Source: construct.ResourceId{Provider: "p", Type: "t", Name: "l"}, Target: construct.ResourceId{Provider: "p", Type: "t", Name: "b"}},
+			},
+			mockKB: []mock.Call{
+				{
+					Method:    "GetResourceTemplate",
+					Arguments: []any{mock.Anything},
+					ReturnArguments: []any{&knowledgebase.ResourceTemplate{
+						Classification: knowledgebase.Classification{Is: []string{string(knowledgebase.Compute)}},
+						PathSatisfaction: knowledgebase.PathSatisfaction{
+							AsSource: []knowledgebase.PathSatisfactionRoute{
+								{Classification: ""},
+							},
+							AsTarget: []knowledgebase.PathSatisfactionRoute{
+								{Classification: ""},
+							},
+						},
+					}, nil},
+				},
+				{
+					Method:          "GetEdgeTemplate",
+					Arguments:       []any{mock.Anything, mock.Anything},
+					ReturnArguments: []any{&knowledgebase.EdgeTemplate{}},
+				},
+				{
+					Method:    "GetPathSatisfactionsFromEdge",
+					Arguments: []any{mock.Anything, mock.Anything},
+					ReturnArguments: []any{
+						[]knowledgebase.EdgePathSatisfaction{
+							{Classification: ""},
+						}, nil,
+					},
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/pkg/engine2/testdata/delete_api_to_lambda_edge.dataflow-viz.yaml b/pkg/engine2/testdata/delete_api_to_lambda_edge.dataflow-viz.yaml
new file mode 100644
index 00000000..85412c89
--- /dev/null
+++ b/pkg/engine2/testdata/delete_api_to_lambda_edge.dataflow-viz.yaml
@@ -0,0 +1,22 @@
+provider: aws
+resources:
+  rest_api/rest_api_0:
+    children:
+        - aws:api_deployment:rest_api_0:api_deployment-0
+        - aws:api_integration:rest_api_0:rest_api_0_integration_0
+        - aws:api_method:rest_api_0:rest_api_0_integration_0_method
+        - aws:api_resource:rest_api_0:api_resource-0
+        - aws:api_stage:rest_api_0:api_stage-0
+    tag: parent
+
+  lambda_function/lambda_function_2:
+    children:
+        - aws:ecr_image:lambda_function_2-image
+        - aws:ecr_repo:ecr_repo-0
+        - aws:iam_role:lambda_function_2-ExecutionRole
+    tag: big
+
+  aws:api_integration:rest_api_0/rest_api_0_integration_0:
+    parent: rest_api/rest_api_0
+    tag: big
+
diff --git a/pkg/engine2/testdata/delete_api_to_lambda_edge.expect.yaml b/pkg/engine2/testdata/delete_api_to_lambda_edge.expect.yaml
new file mode 100644
index 00000000..37fb8ead
--- /dev/null
+++ b/pkg/engine2/testdata/delete_api_to_lambda_edge.expect.yaml
@@ -0,0 +1,82 @@
+resources:
+    aws:api_stage:rest_api_0:api_stage-0:
+        Deployment: aws:api_deployment:rest_api_0:api_deployment-0
+        RestApi: aws:rest_api:rest_api_0
+        StageName: stage
+    aws:lambda_function:lambda_function_2:
+        ExecutionRole: aws:iam_role:lambda_function_2-ExecutionRole
+        Image: aws:ecr_image:lambda_function_2-image
+        LogGroup: aws:log_group:lambda_function_2-log-group
+        MemorySize: 512
+        Timeout: 180
+    aws:api_deployment:rest_api_0:api_deployment-0:
+        RestApi: aws:rest_api:rest_api_0
+        Triggers:
+            rest_api_0_integration_0: rest_api_0_integration_0
+            rest_api_0_integration_0_method: rest_api_0_integration_0_method
+    aws:SERVICE_API:lambda_function_2-lambda_function_2-log-group:
+    aws:ecr_image:lambda_function_2-image:
+        Context: .
+        Dockerfile: lambda_function_2-image.Dockerfile
+        Repo: aws:ecr_repo:ecr_repo-0
+    aws:iam_role:lambda_function_2-ExecutionRole:
+        AssumeRolePolicyDoc:
+            Statement:
+                - Action:
+                    - sts:AssumeRole
+                  Effect: Allow
+                  Principal:
+                    Service:
+                        - lambda.amazonaws.com
+            Version: "2012-10-17"
+        ManagedPolicies:
+            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
+    aws:rest_api:rest_api_0:
+        BinaryMediaTypes:
+            - application/octet-stream
+            - image/*
+    aws:ecr_repo:ecr_repo-0:
+        ForceDelete: true
+    aws:log_group:lambda_function_2-log-group:
+        LogGroupName: /aws/lambda/lambda_function_2
+        RetentionInDays: 5
+    aws:api_resource:rest_api_0:api_resource-0:
+        FullPath: /{proxy+}
+        PathPart: '{proxy+}'
+        RestApi: aws:rest_api:rest_api_0
+    aws:api_method:rest_api_0:rest_api_0_integration_0_method:
+        Authorization: NONE
+        HttpMethod: ANY
+        RequestParameters:
+            method.request.path.proxy: true
+        Resource: aws:api_resource:rest_api_0:api_resource-0
+        RestApi: aws:rest_api:rest_api_0
+    aws:api_integration:rest_api_0:rest_api_0_integration_0:
+        IntegrationHttpMethod: POST
+        Method: aws:api_method:rest_api_0:rest_api_0_integration_0_method
+        RequestParameters:
+            integration.request.path.proxy: method.request.path.proxy
+        Resource: aws:api_resource:rest_api_0:api_resource-0
+        RestApi: aws:rest_api:rest_api_0
+        Route: /{proxy+}
+        Target: aws:lambda_function:lambda_function_2
+        Type: AWS_PROXY
+        Uri: aws:lambda_function:lambda_function_2#LambdaIntegrationUri
+edges:
+    aws:api_stage:rest_api_0:api_stage-0 -> aws:api_deployment:rest_api_0:api_deployment-0:
+    aws:api_stage:rest_api_0:api_stage-0 -> aws:rest_api:rest_api_0:
+    aws:lambda_function:lambda_function_2 -> aws:SERVICE_API:lambda_function_2-lambda_function_2-log-group:
+    aws:lambda_function:lambda_function_2 -> aws:ecr_image:lambda_function_2-image:
+    aws:lambda_function:lambda_function_2 -> aws:iam_role:lambda_function_2-ExecutionRole:
+    aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
+    aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_method:rest_api_0:rest_api_0_integration_0_method:
+    aws:api_deployment:rest_api_0:api_deployment-0 -> aws:rest_api:rest_api_0:
+    aws:SERVICE_API:lambda_function_2-lambda_function_2-log-group -> aws:log_group:lambda_function_2-log-group:
+    aws:ecr_image:lambda_function_2-image -> aws:ecr_repo:ecr_repo-0:
+    aws:iam_role:lambda_function_2-ExecutionRole -> aws:log_group:lambda_function_2-log-group:
+    aws:rest_api:rest_api_0 -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
+    aws:rest_api:rest_api_0 -> aws:api_method:rest_api_0:rest_api_0_integration_0_method:
+    aws:rest_api:rest_api_0 -> aws:api_resource:rest_api_0:api_resource-0:
+    aws:api_resource:rest_api_0:api_resource-0 -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
+    aws:api_resource:rest_api_0:api_resource-0 -> aws:api_method:rest_api_0:rest_api_0_integration_0_method:
+    aws:api_method:rest_api_0:rest_api_0_integration_0_method -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
diff --git a/pkg/engine2/testdata/delete_api_to_lambda_edge.iac-viz.yaml b/pkg/engine2/testdata/delete_api_to_lambda_edge.iac-viz.yaml
new file mode 100644
index 00000000..3700557e
--- /dev/null
+++ b/pkg/engine2/testdata/delete_api_to_lambda_edge.iac-viz.yaml
@@ -0,0 +1,41 @@
+provider: aws
+resources:
+  rest_api/rest_api_0:
+
+  ecr_repo/ecr_repo-0:
+
+  log_group/lambda_function_2-log-group:
+
+  aws:api_resource:rest_api_0/api_resource-0:
+
+  aws:api_resource:rest_api_0/api_resource-0 -> rest_api/rest_api_0:
+  ecr_image/lambda_function_2-image:
+
+  ecr_image/lambda_function_2-image -> ecr_repo/ecr_repo-0:
+  iam_role/lambda_function_2-executionrole:
+
+  iam_role/lambda_function_2-executionrole -> log_group/lambda_function_2-log-group:
+  aws:api_method:rest_api_0/rest_api_0_integration_0_method:
+
+  aws:api_method:rest_api_0/rest_api_0_integration_0_method -> aws:api_resource:rest_api_0/api_resource-0:
+  aws:api_method:rest_api_0/rest_api_0_integration_0_method -> rest_api/rest_api_0:
+  lambda_function/lambda_function_2:
+
+  lambda_function/lambda_function_2 -> ecr_image/lambda_function_2-image:
+  lambda_function/lambda_function_2 -> iam_role/lambda_function_2-executionrole:
+  lambda_function/lambda_function_2 -> log_group/lambda_function_2-log-group:
+  aws:api_integration:rest_api_0/rest_api_0_integration_0:
+
+  aws:api_integration:rest_api_0/rest_api_0_integration_0 -> aws:api_method:rest_api_0/rest_api_0_integration_0_method:
+  aws:api_integration:rest_api_0/rest_api_0_integration_0 -> aws:api_resource:rest_api_0/api_resource-0:
+  aws:api_integration:rest_api_0/rest_api_0_integration_0 -> lambda_function/lambda_function_2:
+  aws:api_integration:rest_api_0/rest_api_0_integration_0 -> rest_api/rest_api_0:
+  aws:api_deployment:rest_api_0/api_deployment-0:
+
+  aws:api_deployment:rest_api_0/api_deployment-0 -> aws:api_integration:rest_api_0/rest_api_0_integration_0:
+  aws:api_deployment:rest_api_0/api_deployment-0 -> aws:api_method:rest_api_0/rest_api_0_integration_0_method:
+  aws:api_deployment:rest_api_0/api_deployment-0 -> rest_api/rest_api_0:
+  aws:api_stage:rest_api_0/api_stage-0:
+
+  aws:api_stage:rest_api_0/api_stage-0 -> aws:api_deployment:rest_api_0/api_deployment-0:
+  aws:api_stage:rest_api_0/api_stage-0 -> rest_api/rest_api_0:
diff --git a/pkg/engine2/testdata/delete_api_to_lambda_edge.input.yaml b/pkg/engine2/testdata/delete_api_to_lambda_edge.input.yaml
new file mode 100644
index 00000000..bc1c3251
--- /dev/null
+++ b/pkg/engine2/testdata/delete_api_to_lambda_edge.input.yaml
@@ -0,0 +1,95 @@
+constraints:
+- operator: must_not_exist
+  scope: edge
+  target:
+    source: aws:api_integration:rest_api_0:rest_api_0_integration_0
+    target: aws:lambda_function:lambda_function_2
+resources:
+    aws:api_stage:rest_api_0:api_stage-0:
+        Deployment: aws:api_deployment:rest_api_0:api_deployment-0
+        RestApi: aws:rest_api:rest_api_0
+        StageName: stage
+    aws:api_deployment:rest_api_0:api_deployment-0:
+        RestApi: aws:rest_api:rest_api_0
+        Triggers:
+            rest_api_0_integration_0: rest_api_0_integration_0
+            rest_api_0_integration_0_method: rest_api_0_integration_0_method
+    aws:rest_api:rest_api_0:
+        BinaryMediaTypes:
+            - application/octet-stream
+            - image/*
+    aws:api_resource:rest_api_0:api_resource-0:
+        FullPath: /{proxy+}
+        PathPart: '{proxy+}'
+        RestApi: aws:rest_api:rest_api_0
+    aws:api_method:rest_api_0:rest_api_0_integration_0_method:
+        Authorization: NONE
+        HttpMethod: ANY
+        RequestParameters:
+            method.request.path.proxy: true
+        Resource: aws:api_resource:rest_api_0:api_resource-0
+        RestApi: aws:rest_api:rest_api_0
+    aws:api_integration:rest_api_0:rest_api_0_integration_0:
+        IntegrationHttpMethod: POST
+        Method: aws:api_method:rest_api_0:rest_api_0_integration_0_method
+        RequestParameters:
+            integration.request.path.proxy: method.request.path.proxy
+        Resource: aws:api_resource:rest_api_0:api_resource-0
+        RestApi: aws:rest_api:rest_api_0
+        Route: /{proxy+}
+        Target: aws:lambda_function:lambda_function_2
+        Type: AWS_PROXY
+        Uri: aws:lambda_function:lambda_function_2#LambdaIntegrationUri
+    aws:lambda_permission:rest_api_0_integration_0-lambda_function_2:
+        Action: lambda:InvokeFunction
+        Function: aws:lambda_function:lambda_function_2
+        Principal: apigateway.amazonaws.com
+        Source: aws:rest_api:rest_api_0#ChildResources
+    aws:lambda_function:lambda_function_2:
+        ExecutionRole: aws:iam_role:lambda_function_2-ExecutionRole
+        Image: aws:ecr_image:lambda_function_2-image
+        LogGroup: aws:log_group:lambda_function_2-log-group
+        MemorySize: 512
+        Timeout: 180
+    aws:SERVICE_API:lambda_function_2-lambda_function_2-log-group:
+    aws:ecr_image:lambda_function_2-image:
+        Context: .
+        Dockerfile: lambda_function_2-image.Dockerfile
+        Repo: aws:ecr_repo:ecr_repo-0
+    aws:iam_role:lambda_function_2-ExecutionRole:
+        AssumeRolePolicyDoc:
+            Statement:
+                - Action:
+                    - sts:AssumeRole
+                  Effect: Allow
+                  Principal:
+                    Service:
+                        - lambda.amazonaws.com
+            Version: "2012-10-17"
+        ManagedPolicies:
+            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
+    aws:ecr_repo:ecr_repo-0:
+        ForceDelete: true
+    aws:log_group:lambda_function_2-log-group:
+        LogGroupName: /aws/lambda/lambda_function_2
+        RetentionInDays: 5
+edges:
+    aws:api_stage:rest_api_0:api_stage-0 -> aws:api_deployment:rest_api_0:api_deployment-0:
+    aws:api_stage:rest_api_0:api_stage-0 -> aws:rest_api:rest_api_0:
+    aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
+    aws:api_deployment:rest_api_0:api_deployment-0 -> aws:api_method:rest_api_0:rest_api_0_integration_0_method:
+    aws:api_deployment:rest_api_0:api_deployment-0 -> aws:rest_api:rest_api_0:
+    aws:rest_api:rest_api_0 -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
+    aws:rest_api:rest_api_0 -> aws:api_method:rest_api_0:rest_api_0_integration_0_method:
+    aws:rest_api:rest_api_0 -> aws:api_resource:rest_api_0:api_resource-0:
+    aws:api_resource:rest_api_0:api_resource-0 -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
+    aws:api_resource:rest_api_0:api_resource-0 -> aws:api_method:rest_api_0:rest_api_0_integration_0_method:
+    aws:api_method:rest_api_0:rest_api_0_integration_0_method -> aws:api_integration:rest_api_0:rest_api_0_integration_0:
+    aws:api_integration:rest_api_0:rest_api_0_integration_0 -> aws:lambda_permission:rest_api_0_integration_0-lambda_function_2:
+    aws:lambda_permission:rest_api_0_integration_0-lambda_function_2 -> aws:lambda_function:lambda_function_2:
+    aws:lambda_function:lambda_function_2 -> aws:SERVICE_API:lambda_function_2-lambda_function_2-log-group:
+    aws:lambda_function:lambda_function_2 -> aws:ecr_image:lambda_function_2-image:
+    aws:lambda_function:lambda_function_2 -> aws:iam_role:lambda_function_2-ExecutionRole:
+    aws:SERVICE_API:lambda_function_2-lambda_function_2-log-group -> aws:log_group:lambda_function_2-log-group:
+    aws:ecr_image:lambda_function_2-image -> aws:ecr_repo:ecr_repo-0:
+    aws:iam_role:lambda_function_2-ExecutionRole -> aws:log_group:lambda_function_2-log-group: