Skip to content

Commit f79e75d

Browse files
dylanratcliffeactions-user
authored andcommitted
Merge pull request #546 from overmindtech/feat/apigateway-authorizer-deployment
(3) Feat/apigateway autorizer and deployment adapters GitOrigin-RevId: ea93a996ee53c9a68b6f9df89747bc03f0339b26
1 parent cb7739a commit f79e75d

11 files changed

+664
-0
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package adapters
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/aws/aws-sdk-go-v2/service/apigateway"
9+
"github.com/aws/aws-sdk-go-v2/service/apigateway/types"
10+
11+
"github.com/overmindtech/cli/aws-source/adapterhelpers"
12+
"github.com/overmindtech/cli/sdp-go"
13+
)
14+
15+
// convertGetAuthorizerOutputToAuthorizer converts a GetAuthorizerOutput to an Authorizer
16+
func convertGetAuthorizerOutputToAuthorizer(output *apigateway.GetAuthorizerOutput) *types.Authorizer {
17+
return &types.Authorizer{
18+
Id: output.Id,
19+
Name: output.Name,
20+
Type: output.Type,
21+
ProviderARNs: output.ProviderARNs,
22+
AuthType: output.AuthType,
23+
AuthorizerUri: output.AuthorizerUri,
24+
AuthorizerCredentials: output.AuthorizerCredentials,
25+
IdentitySource: output.IdentitySource,
26+
IdentityValidationExpression: output.IdentityValidationExpression,
27+
AuthorizerResultTtlInSeconds: output.AuthorizerResultTtlInSeconds,
28+
}
29+
}
30+
31+
func authorizerOutputMapper(query, scope string, awsItem *types.Authorizer) (*sdp.Item, error) {
32+
attributes, err := adapterhelpers.ToAttributesWithExclude(awsItem, "tags")
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
item := sdp.Item{
38+
Type: "apigateway-authorizer",
39+
UniqueAttribute: "Id",
40+
Attributes: attributes,
41+
Scope: scope,
42+
}
43+
44+
item.LinkedItemQueries = append(item.LinkedItemQueries, &sdp.LinkedItemQuery{
45+
Query: &sdp.Query{
46+
Type: "apigateway-rest-api",
47+
Method: sdp.QueryMethod_GET,
48+
Query: strings.Split(query, "/")[0],
49+
Scope: scope,
50+
},
51+
BlastPropagation: &sdp.BlastPropagation{
52+
// They are tightly coupled, so we need to propagate the blast to the linked item
53+
In: true,
54+
Out: true,
55+
},
56+
})
57+
58+
return &item, nil
59+
}
60+
61+
func NewAPIGatewayAuthorizerAdapter(client *apigateway.Client, accountID string, region string) *adapterhelpers.GetListAdapter[*types.Authorizer, *apigateway.Client, *apigateway.Options] {
62+
return &adapterhelpers.GetListAdapter[*types.Authorizer, *apigateway.Client, *apigateway.Options]{
63+
ItemType: "apigateway-authorizer",
64+
Client: client,
65+
AccountID: accountID,
66+
Region: region,
67+
AdapterMetadata: authorizerAdapterMetadata,
68+
GetFunc: func(ctx context.Context, client *apigateway.Client, scope, query string) (*types.Authorizer, error) {
69+
f := strings.Split(query, "/")
70+
if len(f) != 2 {
71+
return nil, &sdp.QueryError{
72+
ErrorType: sdp.QueryError_NOTFOUND,
73+
ErrorString: fmt.Sprintf("query must be in the format of: the rest-api-id/authorizer-id, but found: %s", query),
74+
}
75+
}
76+
out, err := client.GetAuthorizer(ctx, &apigateway.GetAuthorizerInput{
77+
RestApiId: &f[0],
78+
AuthorizerId: &f[1],
79+
})
80+
if err != nil {
81+
return nil, err
82+
}
83+
return convertGetAuthorizerOutputToAuthorizer(out), nil
84+
},
85+
DisableList: true,
86+
SearchFunc: func(ctx context.Context, client *apigateway.Client, scope string, query string) ([]*types.Authorizer, error) {
87+
out, err := client.GetAuthorizers(ctx, &apigateway.GetAuthorizersInput{
88+
RestApiId: &query,
89+
})
90+
if err != nil {
91+
return nil, err
92+
}
93+
94+
authorizers := make([]*types.Authorizer, 0, len(out.Items))
95+
for _, authorizer := range out.Items {
96+
authorizers = append(authorizers, &authorizer)
97+
}
98+
99+
return authorizers, nil
100+
},
101+
ItemMapper: func(query, scope string, awsItem *types.Authorizer) (*sdp.Item, error) {
102+
return authorizerOutputMapper(query, scope, awsItem)
103+
},
104+
}
105+
}
106+
107+
var authorizerAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{
108+
Type: "apigateway-authorizer",
109+
DescriptiveName: "API Gateway Authorizer",
110+
Category: sdp.AdapterCategory_ADAPTER_CATEGORY_SECURITY,
111+
SupportedQueryMethods: &sdp.AdapterSupportedQueryMethods{
112+
Get: true,
113+
Search: true,
114+
GetDescription: "Get an API Gateway Authorizer by its rest API ID and ID: rest-api-id/authorizer-id",
115+
SearchDescription: "Search for API Gateway Authorizers by their rest API ID",
116+
},
117+
TerraformMappings: []*sdp.TerraformMapping{
118+
{TerraformQueryMap: "aws_api_gateway_authorizer.id"},
119+
},
120+
})
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package adapters
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/aws/aws-sdk-go-v2/aws"
8+
"github.com/aws/aws-sdk-go-v2/service/apigateway"
9+
"github.com/aws/aws-sdk-go-v2/service/apigateway/types"
10+
11+
"github.com/overmindtech/cli/aws-source/adapterhelpers"
12+
"github.com/overmindtech/cli/sdp-go"
13+
)
14+
15+
func TestAuthorizerOutputMapper(t *testing.T) {
16+
awsItem := &types.Authorizer{
17+
Id: aws.String("authorizer-id"),
18+
Name: aws.String("authorizer-name"),
19+
Type: types.AuthorizerTypeRequest,
20+
ProviderARNs: []string{"arn:aws:iam::123456789012:role/service-role"},
21+
AuthType: aws.String("custom"),
22+
AuthorizerUri: aws.String("arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:my-function/invocations"),
23+
AuthorizerCredentials: aws.String("arn:aws:iam::123456789012:role/service-role"),
24+
IdentitySource: aws.String("method.request.header.Authorization"),
25+
IdentityValidationExpression: aws.String(".*"),
26+
AuthorizerResultTtlInSeconds: aws.Int32(300),
27+
}
28+
29+
item, err := authorizerOutputMapper("rest-api-id", "scope", awsItem)
30+
if err != nil {
31+
t.Fatalf("unexpected error: %v", err)
32+
}
33+
34+
if err := item.Validate(); err != nil {
35+
t.Error(err)
36+
}
37+
38+
tests := adapterhelpers.QueryTests{
39+
{
40+
ExpectedType: "apigateway-rest-api",
41+
ExpectedMethod: sdp.QueryMethod_GET,
42+
ExpectedQuery: "rest-api-id",
43+
ExpectedScope: "scope",
44+
},
45+
}
46+
47+
tests.Execute(t, item)
48+
}
49+
50+
func TestNewAPIGatewayAuthorizerAdapter(t *testing.T) {
51+
config, account, region := adapterhelpers.GetAutoConfig(t)
52+
53+
client := apigateway.NewFromConfig(config)
54+
55+
adapter := NewAPIGatewayAuthorizerAdapter(client, account, region)
56+
57+
test := adapterhelpers.E2ETest{
58+
Adapter: adapter,
59+
Timeout: 10 * time.Second,
60+
SkipList: true,
61+
}
62+
63+
test.Run(t)
64+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package adapters
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/aws/aws-sdk-go-v2/service/apigateway"
9+
"github.com/aws/aws-sdk-go-v2/service/apigateway/types"
10+
11+
"github.com/overmindtech/cli/aws-source/adapterhelpers"
12+
"github.com/overmindtech/cli/sdp-go"
13+
)
14+
15+
// convertGetDeploymentOutputToDeployment converts a GetDeploymentOutput to a Deployment
16+
func convertGetDeploymentOutputToDeployment(output *apigateway.GetDeploymentOutput) *types.Deployment {
17+
return &types.Deployment{
18+
Id: output.Id,
19+
CreatedDate: output.CreatedDate,
20+
Description: output.Description,
21+
ApiSummary: output.ApiSummary,
22+
}
23+
}
24+
25+
func deploymentOutputMapper(query, scope string, awsItem *types.Deployment) (*sdp.Item, error) {
26+
attributes, err := adapterhelpers.ToAttributesWithExclude(awsItem, "tags")
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
item := sdp.Item{
32+
Type: "apigateway-deployment",
33+
UniqueAttribute: "Id",
34+
Attributes: attributes,
35+
Scope: scope,
36+
}
37+
38+
item.LinkedItemQueries = append(item.LinkedItemQueries, &sdp.LinkedItemQuery{
39+
Query: &sdp.Query{
40+
Type: "apigateway-rest-api",
41+
Method: sdp.QueryMethod_GET,
42+
Query: strings.Split(query, "/")[0],
43+
Scope: scope,
44+
},
45+
BlastPropagation: &sdp.BlastPropagation{
46+
// They are tightly coupled, so we need to propagate the blast to the linked item
47+
In: true,
48+
Out: true,
49+
},
50+
})
51+
52+
return &item, nil
53+
}
54+
55+
func NewAPIGatewayDeploymentAdapter(client *apigateway.Client, accountID string, region string) *adapterhelpers.GetListAdapter[*types.Deployment, *apigateway.Client, *apigateway.Options] {
56+
return &adapterhelpers.GetListAdapter[*types.Deployment, *apigateway.Client, *apigateway.Options]{
57+
ItemType: "apigateway-deployment",
58+
Client: client,
59+
AccountID: accountID,
60+
Region: region,
61+
AdapterMetadata: deploymentAdapterMetadata,
62+
GetFunc: func(ctx context.Context, client *apigateway.Client, scope, query string) (*types.Deployment, error) {
63+
f := strings.Split(query, "/")
64+
if len(f) != 2 {
65+
return nil, &sdp.QueryError{
66+
ErrorType: sdp.QueryError_NOTFOUND,
67+
ErrorString: fmt.Sprintf("query must be in the format of: the rest-api-id/deployment-id, but found: %s", query),
68+
}
69+
}
70+
out, err := client.GetDeployment(ctx, &apigateway.GetDeploymentInput{
71+
RestApiId: &f[0],
72+
DeploymentId: &f[1],
73+
})
74+
if err != nil {
75+
return nil, err
76+
}
77+
return convertGetDeploymentOutputToDeployment(out), nil
78+
},
79+
DisableList: true,
80+
SearchFunc: func(ctx context.Context, client *apigateway.Client, scope string, query string) ([]*types.Deployment, error) {
81+
out, err := client.GetDeployments(ctx, &apigateway.GetDeploymentsInput{
82+
RestApiId: &query,
83+
})
84+
if err != nil {
85+
return nil, err
86+
}
87+
88+
response := make([]*types.Deployment, 0, len(out.Items))
89+
for _, item := range out.Items {
90+
response = append(response, &item)
91+
}
92+
93+
return response, nil
94+
},
95+
ItemMapper: func(query, scope string, awsItem *types.Deployment) (*sdp.Item, error) {
96+
return deploymentOutputMapper(query, scope, awsItem)
97+
},
98+
}
99+
}
100+
101+
var deploymentAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{
102+
Type: "apigateway-deployment",
103+
DescriptiveName: "API Gateway Deployment",
104+
Category: sdp.AdapterCategory_ADAPTER_CATEGORY_CONFIGURATION,
105+
SupportedQueryMethods: &sdp.AdapterSupportedQueryMethods{
106+
Get: true,
107+
Search: true,
108+
GetDescription: "Get an API Gateway Deployment by its rest API ID and ID: rest-api-id/deployment-id",
109+
SearchDescription: "Search for API Gateway Deployments by their rest API ID",
110+
},
111+
TerraformMappings: []*sdp.TerraformMapping{
112+
{TerraformQueryMap: "aws_api_gateway_deployment.id"},
113+
},
114+
})
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package adapters
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/aws/aws-sdk-go-v2/aws"
8+
"github.com/aws/aws-sdk-go-v2/service/apigateway"
9+
"github.com/aws/aws-sdk-go-v2/service/apigateway/types"
10+
11+
"github.com/overmindtech/cli/aws-source/adapterhelpers"
12+
"github.com/overmindtech/cli/sdp-go"
13+
)
14+
15+
func TestDeploymentOutputMapper(t *testing.T) {
16+
awsItem := &types.Deployment{
17+
Id: aws.String("deployment-id"),
18+
CreatedDate: aws.Time(time.Now()),
19+
Description: aws.String("deployment-description"),
20+
ApiSummary: map[string]map[string]types.MethodSnapshot{},
21+
}
22+
23+
item, err := deploymentOutputMapper("rest-api-id", "scope", awsItem)
24+
if err != nil {
25+
t.Fatalf("unexpected error: %v", err)
26+
}
27+
28+
if err := item.Validate(); err != nil {
29+
t.Error(err)
30+
}
31+
32+
tests := adapterhelpers.QueryTests{
33+
{
34+
ExpectedType: "apigateway-rest-api",
35+
ExpectedMethod: sdp.QueryMethod_GET,
36+
ExpectedQuery: "rest-api-id",
37+
ExpectedScope: "scope",
38+
},
39+
}
40+
41+
tests.Execute(t, item)
42+
}
43+
44+
func TestNewAPIGatewayDeploymentAdapter(t *testing.T) {
45+
config, account, region := adapterhelpers.GetAutoConfig(t)
46+
47+
client := apigateway.NewFromConfig(config)
48+
49+
adapter := NewAPIGatewayDeploymentAdapter(client, account, region)
50+
51+
test := adapterhelpers.E2ETest{
52+
Adapter: adapter,
53+
Timeout: 10 * time.Second,
54+
SkipList: true,
55+
}
56+
57+
test.Run(t)
58+
}

aws-source/adapters/apigateway-rest-api.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,36 @@ func restApiOutputMapper(scope string, awsItem *types.RestApi) (*sdp.Item, error
139139
},
140140
})
141141

142+
item.LinkedItemQueries = append(item.LinkedItemQueries, &sdp.LinkedItemQuery{
143+
Query: &sdp.Query{
144+
Type: "apigateway-deployment",
145+
Method: sdp.QueryMethod_SEARCH,
146+
Query: *awsItem.Id,
147+
Scope: scope,
148+
},
149+
BlastPropagation: &sdp.BlastPropagation{
150+
// Updating a deployment won't affect the REST API
151+
In: false,
152+
// Updating the REST API will affect the deployments
153+
Out: true,
154+
},
155+
})
156+
157+
item.LinkedItemQueries = append(item.LinkedItemQueries, &sdp.LinkedItemQuery{
158+
Query: &sdp.Query{
159+
Type: "apigateway-authorizer",
160+
Method: sdp.QueryMethod_SEARCH,
161+
Query: *awsItem.Id,
162+
Scope: scope,
163+
},
164+
BlastPropagation: &sdp.BlastPropagation{
165+
// Updating an authorizer won't affect the REST API
166+
In: false,
167+
// Updating the REST API will affect the authorizers
168+
Out: true,
169+
},
170+
})
171+
142172
return &item, nil
143173
}
144174

0 commit comments

Comments
 (0)