From 7e8239b2b85d0becb3c7fdcf5a2f4afbaf84f8b4 Mon Sep 17 00:00:00 2001 From: kazuho cryer-shinozuka Date: Thu, 29 Feb 2024 01:02:01 +0900 Subject: [PATCH] feat(appsync): add the `grant` method to the imported `GraphqlApi` (#29086) ### Issue Closes #23031. ### Reason for this change The `grantXx` methods are implemented in the `GraphqlApi` class, but it could not be used with an imported graphql api. ### Description of changes Moved the implementation of the `grantXx` method from the `GraphqlApi` class to the `GraphqlApiBase` class." ### Description of how you validated changes I've added both unit and integ tests. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...efaultTestDeployAssertE072C285.assets.json | 19 + ...aultTestDeployAssertE072C285.template.json | 36 + .../aws-appsync-integ.assets.json | 19 + .../aws-appsync-integ.template.json | 116 +++ .../cdk.out | 1 + .../imported-stack.assets.json | 19 + .../imported-stack.template.json | 282 +++++++ .../integ.json | 12 + .../manifest.json | 228 ++++++ .../tree.json | 688 ++++++++++++++++++ .../test/integ.graphql-grant-imported-api.ts | 91 +++ packages/aws-cdk-lib/aws-appsync/README.md | 4 +- .../aws-appsync/lib/graphqlapi-base.ts | 151 +++- .../aws-cdk-lib/aws-appsync/lib/graphqlapi.ts | 113 +-- .../aws-appsync/test/appsync-grant.test.ts | 586 +++++++++++++++ 15 files changed, 2251 insertions(+), 114 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/aws-appsync-integ.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/aws-appsync-integ.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/imported-stack.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/imported-stack.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.assets.json new file mode 100644 index 0000000000000..132fc9ca01136 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/aws-appsync-integ.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/aws-appsync-integ.assets.json new file mode 100644 index 0000000000000..c4ce0ba3b127b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/aws-appsync-integ.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "eaa071d73fbf78d2b0887200d22f7bab11fc3b13290fd6d6d13fde6b1036c165": { + "source": { + "path": "aws-appsync-integ.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "eaa071d73fbf78d2b0887200d22f7bab11fc3b13290fd6d6d13fde6b1036c165.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/aws-appsync-integ.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/aws-appsync-integ.template.json new file mode 100644 index 0000000000000..ba09319e244a3 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/aws-appsync-integ.template.json @@ -0,0 +1,116 @@ +{ + "Resources": { + "PoolD3F588B8": { + "Type": "AWS::Cognito::UserPool", + "Properties": { + "AccountRecoverySetting": { + "RecoveryMechanisms": [ + { + "Name": "verified_phone_number", + "Priority": 1 + }, + { + "Name": "verified_email", + "Priority": 2 + } + ] + }, + "AdminCreateUserConfig": { + "AllowAdminCreateUserOnly": true + }, + "EmailVerificationMessage": "The verification code to your new account is {####}", + "EmailVerificationSubject": "Verify your new account", + "SmsVerificationMessage": "The verification code to your new account is {####}", + "UserPoolName": "myPool", + "VerificationMessageTemplate": { + "DefaultEmailOption": "CONFIRM_WITH_CODE", + "EmailMessage": "The verification code to your new account is {####}", + "EmailSubject": "Verify your new account", + "SmsMessage": "The verification code to your new account is {####}" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ApiF70053CD": { + "Type": "AWS::AppSync::GraphQLApi", + "Properties": { + "AdditionalAuthenticationProviders": [ + { + "AuthenticationType": "AWS_IAM" + } + ], + "AuthenticationType": "AMAZON_COGNITO_USER_POOLS", + "Name": "Integ_Test_IAM", + "UserPoolConfig": { + "AwsRegion": { + "Ref": "AWS::Region" + }, + "DefaultAction": "ALLOW", + "UserPoolId": { + "Ref": "PoolD3F588B8" + } + } + } + }, + "ApiSchema510EECD7": { + "Type": "AWS::AppSync::GraphQLSchema", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "ApiF70053CD", + "ApiId" + ] + }, + "Definition": "type test @aws_iam {\n id: String!\n version: String!\n}\n\ntype Query {\n getTest(id: String!): test\n getTests: [ test! ]\n @aws_iam \n}\n\ninput TestInput {\n version: String!\n}\n\ntype Mutation {\n addTest(input: TestInput!): test\n @aws_iam\n}\n" + } + } + }, + "Outputs": { + "ExportsOutputFnGetAttApiF70053CDApiIdF185726B": { + "Value": { + "Fn::GetAtt": [ + "ApiF70053CD", + "ApiId" + ] + }, + "Export": { + "Name": "aws-appsync-integ:ExportsOutputFnGetAttApiF70053CDApiIdF185726B" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/imported-stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/imported-stack.assets.json new file mode 100644 index 0000000000000..5dc8598e31d89 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/imported-stack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "33fc02df19070d9366b8e00d2471256ee84cacf09985e1a83461f75872733e2e": { + "source": { + "path": "imported-stack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "33fc02df19070d9366b8e00d2471256ee84cacf09985e1a83461f75872733e2e.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/imported-stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/imported-stack.template.json new file mode 100644 index 0000000000000..103c197099d13 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/imported-stack.template.json @@ -0,0 +1,282 @@ +{ + "Resources": { + "lambdaRoleC844FDB1": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "lambdaRoleDefaultPolicyA63A8A92": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "appsync:GraphQL", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appsync:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":apis/", + { + "Fn::ImportValue": "aws-appsync-integ:ExportsOutputFnGetAttApiF70053CDApiIdF185726B" + }, + "/types/Query/fields/getPost" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "lambdaRoleDefaultPolicyA63A8A92", + "Roles": [ + { + "Ref": "lambdaRoleC844FDB1" + } + ] + } + }, + "mutatioLambdaRole7F96C419": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "mutatioLambdaRoleDefaultPolicyCB07404D": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "appsync:GraphQL", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appsync:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":apis/", + { + "Fn::ImportValue": "aws-appsync-integ:ExportsOutputFnGetAttApiF70053CDApiIdF185726B" + }, + "/types/Mutation/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "mutatioLambdaRoleDefaultPolicyCB07404D", + "Roles": [ + { + "Ref": "mutatioLambdaRole7F96C419" + } + ] + } + }, + "queryLambdaRole5A00034A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "queryLambdaRoleDefaultPolicyC3CBAE04": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "appsync:GraphQL", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appsync:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":apis/", + { + "Fn::ImportValue": "aws-appsync-integ:ExportsOutputFnGetAttApiF70053CDApiIdF185726B" + }, + "/types/Query/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "queryLambdaRoleDefaultPolicyC3CBAE04", + "Roles": [ + { + "Ref": "queryLambdaRole5A00034A" + } + ] + } + }, + "subscriptionLambdaRoleB3A245CA": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "subscriptionLambdaRoleDefaultPolicy11B257BC": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "appsync:GraphQL", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appsync:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":apis/", + { + "Fn::ImportValue": "aws-appsync-integ:ExportsOutputFnGetAttApiF70053CDApiIdF185726B" + }, + "/types/Subscription/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "subscriptionLambdaRoleDefaultPolicy11B257BC", + "Roles": [ + { + "Ref": "subscriptionLambdaRoleB3A245CA" + } + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/integ.json new file mode 100644 index 0000000000000..c958b67489e04 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "GraphqlGrantImportedApiInteg/DefaultTest": { + "stacks": [ + "imported-stack" + ], + "assertionStack": "GraphqlGrantImportedApiInteg/DefaultTest/DeployAssert", + "assertionStackName": "GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/manifest.json new file mode 100644 index 0000000000000..05fcf1f21e63a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/manifest.json @@ -0,0 +1,228 @@ +{ + "version": "36.0.0", + "artifacts": { + "aws-appsync-integ.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-appsync-integ.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-appsync-integ": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-appsync-integ.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/eaa071d73fbf78d2b0887200d22f7bab11fc3b13290fd6d6d13fde6b1036c165.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-appsync-integ.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-appsync-integ.assets" + ], + "metadata": { + "/aws-appsync-integ/Pool/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PoolD3F588B8" + } + ], + "/aws-appsync-integ/Api/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ApiF70053CD" + } + ], + "/aws-appsync-integ/Api/Schema": [ + { + "type": "aws:cdk:logicalId", + "data": "ApiSchema510EECD7" + } + ], + "/aws-appsync-integ/Exports/Output{\"Fn::GetAtt\":[\"ApiF70053CD\",\"ApiId\"]}": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsOutputFnGetAttApiF70053CDApiIdF185726B" + } + ], + "/aws-appsync-integ/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-appsync-integ/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-appsync-integ" + }, + "imported-stack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "imported-stack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "imported-stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "imported-stack.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/33fc02df19070d9366b8e00d2471256ee84cacf09985e1a83461f75872733e2e.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "imported-stack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-appsync-integ", + "imported-stack.assets" + ], + "metadata": { + "/imported-stack/lambdaRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "lambdaRoleC844FDB1" + } + ], + "/imported-stack/lambdaRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "lambdaRoleDefaultPolicyA63A8A92" + } + ], + "/imported-stack/mutatioLambdaRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "mutatioLambdaRole7F96C419" + } + ], + "/imported-stack/mutatioLambdaRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "mutatioLambdaRoleDefaultPolicyCB07404D" + } + ], + "/imported-stack/queryLambdaRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "queryLambdaRole5A00034A" + } + ], + "/imported-stack/queryLambdaRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "queryLambdaRoleDefaultPolicyC3CBAE04" + } + ], + "/imported-stack/subscriptionLambdaRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "subscriptionLambdaRoleB3A245CA" + } + ], + "/imported-stack/subscriptionLambdaRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "subscriptionLambdaRoleDefaultPolicy11B257BC" + } + ], + "/imported-stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/imported-stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "imported-stack" + }, + "GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "GraphqlGrantImportedApiIntegDefaultTestDeployAssertE072C285.assets" + ], + "metadata": { + "/GraphqlGrantImportedApiInteg/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/GraphqlGrantImportedApiInteg/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "GraphqlGrantImportedApiInteg/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/tree.json new file mode 100644 index 0000000000000..fc4f63f478dc1 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.js.snapshot/tree.json @@ -0,0 +1,688 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-appsync-integ": { + "id": "aws-appsync-integ", + "path": "aws-appsync-integ", + "children": { + "Pool": { + "id": "Pool", + "path": "aws-appsync-integ/Pool", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appsync-integ/Pool/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Cognito::UserPool", + "aws:cdk:cloudformation:props": { + "accountRecoverySetting": { + "recoveryMechanisms": [ + { + "name": "verified_phone_number", + "priority": 1 + }, + { + "name": "verified_email", + "priority": 2 + } + ] + }, + "adminCreateUserConfig": { + "allowAdminCreateUserOnly": true + }, + "emailVerificationMessage": "The verification code to your new account is {####}", + "emailVerificationSubject": "Verify your new account", + "smsVerificationMessage": "The verification code to your new account is {####}", + "userPoolName": "myPool", + "verificationMessageTemplate": { + "defaultEmailOption": "CONFIRM_WITH_CODE", + "emailMessage": "The verification code to your new account is {####}", + "emailSubject": "Verify your new account", + "smsMessage": "The verification code to your new account is {####}" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cognito.CfnUserPool", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cognito.UserPool", + "version": "0.0.0" + } + }, + "Api": { + "id": "Api", + "path": "aws-appsync-integ/Api", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appsync-integ/Api/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppSync::GraphQLApi", + "aws:cdk:cloudformation:props": { + "additionalAuthenticationProviders": [ + { + "authenticationType": "AWS_IAM" + } + ], + "authenticationType": "AMAZON_COGNITO_USER_POOLS", + "name": "Integ_Test_IAM", + "userPoolConfig": { + "userPoolId": { + "Ref": "PoolD3F588B8" + }, + "awsRegion": { + "Ref": "AWS::Region" + }, + "defaultAction": "ALLOW" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appsync.CfnGraphQLApi", + "version": "0.0.0" + } + }, + "Schema": { + "id": "Schema", + "path": "aws-appsync-integ/Api/Schema", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppSync::GraphQLSchema", + "aws:cdk:cloudformation:props": { + "apiId": { + "Fn::GetAtt": [ + "ApiF70053CD", + "ApiId" + ] + }, + "definition": "type test @aws_iam {\n id: String!\n version: String!\n}\n\ntype Query {\n getTest(id: String!): test\n getTests: [ test! ]\n @aws_iam \n}\n\ninput TestInput {\n version: String!\n}\n\ntype Mutation {\n addTest(input: TestInput!): test\n @aws_iam\n}\n" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appsync.CfnGraphQLSchema", + "version": "0.0.0" + } + }, + "LogGroup": { + "id": "LogGroup", + "path": "aws-appsync-integ/Api/LogGroup", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appsync.GraphqlApi", + "version": "0.0.0" + } + }, + "ImportedApi": { + "id": "ImportedApi", + "path": "aws-appsync-integ/ImportedApi", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appsync.GraphqlApiBase", + "version": "0.0.0" + } + }, + "Exports": { + "id": "Exports", + "path": "aws-appsync-integ/Exports", + "children": { + "Output{\"Fn::GetAtt\":[\"ApiF70053CD\",\"ApiId\"]}": { + "id": "Output{\"Fn::GetAtt\":[\"ApiF70053CD\",\"ApiId\"]}", + "path": "aws-appsync-integ/Exports/Output{\"Fn::GetAtt\":[\"ApiF70053CD\",\"ApiId\"]}", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-appsync-integ/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-appsync-integ/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "imported-stack": { + "id": "imported-stack", + "path": "imported-stack", + "children": { + "lambdaRole": { + "id": "lambdaRole", + "path": "imported-stack/lambdaRole", + "children": { + "ImportlambdaRole": { + "id": "ImportlambdaRole", + "path": "imported-stack/lambdaRole/ImportlambdaRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "imported-stack/lambdaRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "imported-stack/lambdaRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "imported-stack/lambdaRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "appsync:GraphQL", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appsync:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":apis/", + { + "Fn::ImportValue": "aws-appsync-integ:ExportsOutputFnGetAttApiF70053CDApiIdF185726B" + }, + "/types/Query/fields/getPost" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "lambdaRoleDefaultPolicyA63A8A92", + "roles": [ + { + "Ref": "lambdaRoleC844FDB1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "mutatioLambdaRole": { + "id": "mutatioLambdaRole", + "path": "imported-stack/mutatioLambdaRole", + "children": { + "ImportmutatioLambdaRole": { + "id": "ImportmutatioLambdaRole", + "path": "imported-stack/mutatioLambdaRole/ImportmutatioLambdaRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "imported-stack/mutatioLambdaRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "imported-stack/mutatioLambdaRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "imported-stack/mutatioLambdaRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "appsync:GraphQL", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appsync:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":apis/", + { + "Fn::ImportValue": "aws-appsync-integ:ExportsOutputFnGetAttApiF70053CDApiIdF185726B" + }, + "/types/Mutation/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "mutatioLambdaRoleDefaultPolicyCB07404D", + "roles": [ + { + "Ref": "mutatioLambdaRole7F96C419" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "queryLambdaRole": { + "id": "queryLambdaRole", + "path": "imported-stack/queryLambdaRole", + "children": { + "ImportqueryLambdaRole": { + "id": "ImportqueryLambdaRole", + "path": "imported-stack/queryLambdaRole/ImportqueryLambdaRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "imported-stack/queryLambdaRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "imported-stack/queryLambdaRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "imported-stack/queryLambdaRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "appsync:GraphQL", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appsync:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":apis/", + { + "Fn::ImportValue": "aws-appsync-integ:ExportsOutputFnGetAttApiF70053CDApiIdF185726B" + }, + "/types/Query/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "queryLambdaRoleDefaultPolicyC3CBAE04", + "roles": [ + { + "Ref": "queryLambdaRole5A00034A" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "subscriptionLambdaRole": { + "id": "subscriptionLambdaRole", + "path": "imported-stack/subscriptionLambdaRole", + "children": { + "ImportsubscriptionLambdaRole": { + "id": "ImportsubscriptionLambdaRole", + "path": "imported-stack/subscriptionLambdaRole/ImportsubscriptionLambdaRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "imported-stack/subscriptionLambdaRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "imported-stack/subscriptionLambdaRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "imported-stack/subscriptionLambdaRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "appsync:GraphQL", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appsync:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":apis/", + { + "Fn::ImportValue": "aws-appsync-integ:ExportsOutputFnGetAttApiF70053CDApiIdF185726B" + }, + "/types/Subscription/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "subscriptionLambdaRoleDefaultPolicy11B257BC", + "roles": [ + { + "Ref": "subscriptionLambdaRoleB3A245CA" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "imported-stack/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "imported-stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "GraphqlGrantImportedApiInteg": { + "id": "GraphqlGrantImportedApiInteg", + "path": "GraphqlGrantImportedApiInteg", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "GraphqlGrantImportedApiInteg/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "GraphqlGrantImportedApiInteg/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "GraphqlGrantImportedApiInteg/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "GraphqlGrantImportedApiInteg/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "GraphqlGrantImportedApiInteg/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.ts new file mode 100644 index 0000000000000..fab8a1ce93027 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appsync/test/integ.graphql-grant-imported-api.ts @@ -0,0 +1,91 @@ +import { join } from 'path'; +import { UserPool } from 'aws-cdk-lib/aws-cognito'; +import { App, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; +import { + AuthorizationType, + GraphqlApi, + UserPoolDefaultAction, + SchemaFile, + IamResource, +} from 'aws-cdk-lib/aws-appsync'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import { ServicePrincipal, Role } from 'aws-cdk-lib/aws-iam'; + +class OriginalStack extends Stack { + + public readonly apiId: string; + + constructor(scope: App, id: string) { + super(scope, id); + + const userPool = new UserPool(this, 'Pool', { + userPoolName: 'myPool', + removalPolicy: RemovalPolicy.DESTROY, + }); + + const api = new GraphqlApi(this, 'Api', { + name: 'Integ_Test_IAM', + schema: SchemaFile.fromAsset(join(__dirname, 'integ.graphql-iam.graphql')), + authorizationConfig: { + defaultAuthorization: { + authorizationType: AuthorizationType.USER_POOL, + userPoolConfig: { + userPool, + defaultAction: UserPoolDefaultAction.ALLOW, + }, + }, + additionalAuthorizationModes: [ + { + authorizationType: AuthorizationType.IAM, + }, + ], + }, + }); + + this.apiId = api.apiId; + } +} + +interface ImportedStackProps extends StackProps { + apiId: string; +} + +class ImportedStack extends Stack { + constructor(scope: App, id: string, props: ImportedStackProps) { + super(scope, id); + + const importedApi = GraphqlApi.fromGraphqlApiAttributes(originalStack, 'ImportedApi', { + graphqlApiId: `${props.apiId}`, + }); + + const lambdaRole = new Role(this, 'lambdaRole', { + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), + }); + importedApi.grant(lambdaRole, IamResource.custom('types/Query/fields/getPost'), 'appsync:GraphQL'); + + const mutationLambdaRole = new Role(this, 'mutatioLambdaRole', { + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), + }); + importedApi.grantMutation(mutationLambdaRole); + + const queryLambdaRole = new Role(this, 'queryLambdaRole', { + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), + }); + importedApi.grantQuery(queryLambdaRole); + + const subscriptionLambdaRole = new Role(this, 'subscriptionLambdaRole', { + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), + }); + importedApi.grantSubscription(subscriptionLambdaRole); + } +} + +const app = new App(); +const originalStack = new OriginalStack(app, 'aws-appsync-integ'); +const importedStack = new ImportedStack(app, 'imported-stack', { + apiId: originalStack.apiId, +}); + +new IntegTest(app, 'GraphqlGrantImportedApiInteg', { + testCases: [importedStack], +}); diff --git a/packages/aws-cdk-lib/aws-appsync/README.md b/packages/aws-cdk-lib/aws-appsync/README.md index d24828f11ff80..119b139ff0721 100644 --- a/packages/aws-cdk-lib/aws-appsync/README.md +++ b/packages/aws-cdk-lib/aws-appsync/README.md @@ -632,7 +632,7 @@ Use the `grant` function for more granular authorization. const role = new iam.Role(this, 'Role', { assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), }); -declare const api: appsync.GraphqlApi; +declare const api: appsync.IGraphqlApi; api.grant(role, appsync.IamResource.custom('types/Mutation/fields/updateExample'), 'appsync:GraphQL'); ``` @@ -658,7 +658,7 @@ These include: - grantSubscription (use to grant access to Subscription fields) ```ts -declare const api: appsync.GraphqlApi; +declare const api: appsync.IGraphqlApi; declare const role: iam.Role; // For generic types diff --git a/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi-base.ts b/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi-base.ts index f4acb601aa7bd..a75657f80a1a8 100644 --- a/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi-base.ts +++ b/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi-base.ts @@ -13,11 +13,12 @@ import { Resolver, ExtendedResolverProps } from './resolver'; import { ITable } from '../../aws-dynamodb'; import { IDomain as IElasticsearchDomain } from '../../aws-elasticsearch'; import { IEventBus } from '../../aws-events'; +import { Grant, IGrantable } from '../../aws-iam'; import { IFunction } from '../../aws-lambda'; import { IDomain as IOpenSearchDomain } from '../../aws-opensearchservice'; import { IServerlessCluster } from '../../aws-rds'; import { ISecret } from '../../aws-secretsmanager'; -import { CfnResource, IResource, Resource } from '../../core'; +import { ArnFormat, CfnResource, IResource, Resource, Stack } from '../../core'; /** * Optional configuration for data sources @@ -50,6 +51,67 @@ export interface HttpDataSourceOptions extends DataSourceOptions { readonly authorizationConfig?: AwsIamConfig; } +/** + * A class used to generate resource arns for AppSync + */ +export class IamResource { + /** + * Generate the resource names given custom arns + * + * @param arns The custom arns that need to be permissioned + * + * Example: custom('/types/Query/fields/getExample') + */ + public static custom(...arns: string[]): IamResource { + if (arns.length === 0) { + throw new Error('At least 1 custom ARN must be provided.'); + } + return new IamResource(arns); + } + + /** + * Generate the resource names given a type and fields + * + * @param type The type that needs to be allowed + * @param fields The fields that need to be allowed, if empty grant permissions to ALL fields + * + * Example: ofType('Query', 'GetExample') + */ + public static ofType(type: string, ...fields: string[]): IamResource { + const arns = fields.length ? fields.map((field) => `types/${type}/fields/${field}`) : [`types/${type}/*`]; + return new IamResource(arns); + } + + /** + * Generate the resource names that accepts all types: `*` + */ + public static all(): IamResource { + return new IamResource(['*']); + } + + private arns: string[]; + + private constructor(arns: string[]) { + this.arns = arns; + } + + /** + * Return the Resource ARN + * + * @param api The GraphQL API to give permissions + */ + public resourceArns(api: GraphqlApiBase): string[] { + return this.arns.map((arn) => + Stack.of(api).formatArn({ + service: 'appsync', + resource: `apis/${api.apiId}`, + arnFormat: ArnFormat.SLASH_RESOURCE_NAME, + resourceName: `${arn}`, + }), + ); + } +} + /** * Interface for GraphQL */ @@ -161,6 +223,43 @@ export interface IGraphqlApi extends IResource { * @param construct the dependee */ addSchemaDependency(construct: CfnResource): boolean; + + /** + * Adds an IAM policy statement associated with this GraphQLApi to an IAM + * principal's policy. + * + * @param grantee The principal + * @param resources The set of resources to allow (i.e. ...:[region]:[accountId]:apis/GraphQLId/...) + * @param actions The actions that should be granted to the principal (i.e. appsync:graphql ) + */ + grant(grantee: IGrantable, resources: IamResource, ...actions: string[]): Grant; + + /** + * Adds an IAM policy statement for Mutation access to this GraphQLApi to an IAM + * principal's policy. + * + * @param grantee The principal + * @param fields The fields to grant access to that are Mutations (leave blank for all) + */ + grantMutation(grantee: IGrantable, ...fields: string[]): Grant; + + /** + * Adds an IAM policy statement for Query access to this GraphQLApi to an IAM + * principal's policy. + * + * @param grantee The principal + * @param fields The fields to grant access to that are Queries (leave blank for all) + */ + grantQuery(grantee: IGrantable, ...fields: string[]): Grant; + + /** + * Adds an IAM policy statement for Subscription access to this GraphQLApi to an IAM + * principal's policy. + * + * @param grantee The principal + * @param fields The fields to grant access to that are Subscriptions (leave blank for all) + */ + grantSubscription(grantee: IGrantable, ...fields: string[]): Grant; } /** @@ -335,4 +434,54 @@ export abstract class GraphqlApiBase extends Resource implements IGraphqlApi { construct; return false; } + + /** + * Adds an IAM policy statement associated with this GraphQLApi to an IAM + * principal's policy. + * + * @param grantee The principal + * @param resources The set of resources to allow (i.e. ...:[region]:[accountId]:apis/GraphQLId/...) + * @param actions The actions that should be granted to the principal (i.e. appsync:graphql ) + */ + public grant(grantee: IGrantable, resources: IamResource, ...actions: string[]): Grant { + return Grant.addToPrincipal({ + grantee, + actions, + resourceArns: resources.resourceArns(this), + scope: this, + }); + } + + /** + * Adds an IAM policy statement for Mutation access to this GraphQLApi to an IAM + * principal's policy. + * + * @param grantee The principal + * @param fields The fields to grant access to that are Mutations (leave blank for all) + */ + public grantMutation(grantee: IGrantable, ...fields: string[]): Grant { + return this.grant(grantee, IamResource.ofType('Mutation', ...fields), 'appsync:GraphQL'); + } + + /** + * Adds an IAM policy statement for Query access to this GraphQLApi to an IAM + * principal's policy. + * + * @param grantee The principal + * @param fields The fields to grant access to that are Queries (leave blank for all) + */ + public grantQuery(grantee: IGrantable, ...fields: string[]): Grant { + return this.grant(grantee, IamResource.ofType('Query', ...fields), 'appsync:GraphQL'); + } + + /** + * Adds an IAM policy statement for Subscription access to this GraphQLApi to an IAM + * principal's policy. + * + * @param grantee The principal + * @param fields The fields to grant access to that are Subscriptions (leave blank for all) + */ + public grantSubscription(grantee: IGrantable, ...fields: string[]): Grant { + return this.grant(grantee, IamResource.ofType('Subscription', ...fields), 'appsync:GraphQL'); + } } diff --git a/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts b/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts index 04b6ed99b793d..75420814843b7 100644 --- a/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts +++ b/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts @@ -5,10 +5,10 @@ import { ISchema, SchemaFile } from './schema'; import { MergeType, addSourceApiAutoMergePermission, addSourceGraphQLPermission } from './source-api-association'; import { ICertificate } from '../../aws-certificatemanager'; import { IUserPool } from '../../aws-cognito'; -import { ManagedPolicy, Role, IRole, ServicePrincipal, Grant, IGrantable } from '../../aws-iam'; +import { ManagedPolicy, Role, IRole, ServicePrincipal } from '../../aws-iam'; import { IFunction } from '../../aws-lambda'; import { ILogGroup, LogGroup, LogRetention, RetentionDays } from '../../aws-logs'; -import { ArnFormat, CfnResource, Duration, Expiration, FeatureFlags, IResolvable, Lazy, Stack, Token } from '../../core'; +import { CfnResource, Duration, Expiration, FeatureFlags, IResolvable, Lazy, Stack, Token } from '../../core'; import * as cxapi from '../../cx-api'; /** @@ -458,65 +458,6 @@ export interface GraphqlApiProps { readonly environmentVariables?: { [key: string]: string }; } -/** - * A class used to generate resource arns for AppSync - */ -export class IamResource { - /** - * Generate the resource names given custom arns - * - * @param arns The custom arns that need to be permissioned - * - * Example: custom('/types/Query/fields/getExample') - */ - public static custom(...arns: string[]): IamResource { - if (arns.length === 0) { - throw new Error('At least 1 custom ARN must be provided.'); - } - return new IamResource(arns); - } - - /** - * Generate the resource names given a type and fields - * - * @param type The type that needs to be allowed - * @param fields The fields that need to be allowed, if empty grant permissions to ALL fields - * - * Example: ofType('Query', 'GetExample') - */ - public static ofType(type: string, ...fields: string[]): IamResource { - const arns = fields.length ? fields.map((field) => `types/${type}/fields/${field}`) : [`types/${type}/*`]; - return new IamResource(arns); - } - - /** - * Generate the resource names that accepts all types: `*` - */ - public static all(): IamResource { - return new IamResource(['*']); - } - - private arns: string[]; - - private constructor(arns: string[]) { - this.arns = arns; - } - - /** - * Return the Resource ARN - * - * @param api The GraphQL API to give permissions - */ - public resourceArns(api: GraphqlApi): string[] { - return this.arns.map((arn) => Stack.of(api).formatArn({ - service: 'appsync', - resource: `apis/${api.apiId}`, - arnFormat: ArnFormat.SLASH_RESOURCE_NAME, - resourceName: `${arn}`, - })); - } -} - /** * Attributes for GraphQL imports */ @@ -785,56 +726,6 @@ export class GraphqlApi extends GraphqlApiBase { } } - /** - * Adds an IAM policy statement associated with this GraphQLApi to an IAM - * principal's policy. - * - * @param grantee The principal - * @param resources The set of resources to allow (i.e. ...:[region]:[accountId]:apis/GraphQLId/...) - * @param actions The actions that should be granted to the principal (i.e. appsync:graphql ) - */ - public grant(grantee: IGrantable, resources: IamResource, ...actions: string[]): Grant { - return Grant.addToPrincipal({ - grantee, - actions, - resourceArns: resources.resourceArns(this), - scope: this, - }); - } - - /** - * Adds an IAM policy statement for Mutation access to this GraphQLApi to an IAM - * principal's policy. - * - * @param grantee The principal - * @param fields The fields to grant access to that are Mutations (leave blank for all) - */ - public grantMutation(grantee: IGrantable, ...fields: string[]): Grant { - return this.grant(grantee, IamResource.ofType('Mutation', ...fields), 'appsync:GraphQL'); - } - - /** - * Adds an IAM policy statement for Query access to this GraphQLApi to an IAM - * principal's policy. - * - * @param grantee The principal - * @param fields The fields to grant access to that are Queries (leave blank for all) - */ - public grantQuery(grantee: IGrantable, ...fields: string[]): Grant { - return this.grant(grantee, IamResource.ofType('Query', ...fields), 'appsync:GraphQL'); - } - - /** - * Adds an IAM policy statement for Subscription access to this GraphQLApi to an IAM - * principal's policy. - * - * @param grantee The principal - * @param fields The fields to grant access to that are Subscriptions (leave blank for all) - */ - public grantSubscription(grantee: IGrantable, ...fields: string[]): Grant { - return this.grant(grantee, IamResource.ofType('Subscription', ...fields), 'appsync:GraphQL'); - } - private validateAuthorizationProps(modes: AuthorizationMode[]) { if (modes.filter((mode) => mode.authorizationType === AuthorizationType.LAMBDA).length > 1) { throw new Error('You can only have a single AWS Lambda function configured to authorize your API.'); diff --git a/packages/aws-cdk-lib/aws-appsync/test/appsync-grant.test.ts b/packages/aws-cdk-lib/aws-appsync/test/appsync-grant.test.ts index e6ad4a1741238..0c89126a34a90 100644 --- a/packages/aws-cdk-lib/aws-appsync/test/appsync-grant.test.ts +++ b/packages/aws-cdk-lib/aws-appsync/test/appsync-grant.test.ts @@ -578,4 +578,590 @@ describe('grantSubscription Permissions', () => { }, }); }); +}); + +describe('imported API', () => { + let importedApi: appsync.IGraphqlApi; + beforeEach(() => { + importedApi = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'imported', { + graphqlApiId: 'importedId', + }); + }); + + describe('grant Permissions', () => { + test('IamResource throws error when custom is called with no arguments', () => { + //THEN + expect(() => { + importedApi.grant(role, appsync.IamResource.custom(), 'appsync:GraphQL'); + }).toThrow('At least 1 custom ARN must be provided.'); + }); + + test('grant provides custom permissions when called with `custom` argument', () => { + // WHEN + importedApi.grant( + role, + appsync.IamResource.custom('types/Mutation/fields/addTest'), + 'appsync:GraphQL', + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Mutation/fields/addTest', + ], + ], + }, + }, + ], + }, + }); + }); + + test('grant provides [type parameter]/* permissions when called with `type` argument', () => { + // WHEN + importedApi.grant(role, appsync.IamResource.ofType('Mutation'), 'appsync:GraphQL'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Mutation/*', + ], + ], + }, + }, + ], + }, + }); + }); + + test('grant provides fields/[field param] permissions when called with `type` and `field` argument', () => { + // WHEN + importedApi.grant(role, appsync.IamResource.ofType('Mutation', 'addTest'), 'appsync:GraphQL'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Mutation/fields/addTest', + ], + ], + }, + }, + ], + }, + }); + }); + + test('grant provides all permissions when called with IamResource.all()', () => { + // WHEN + importedApi.grant(role, appsync.IamResource.all(), 'appsync:GraphQL'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/*', + ], + ], + }, + }, + ], + }, + }); + }); + + test('grant provides multiple permissions using one IamResource custom call', () => { + // WHEN + importedApi.grant(role, appsync.IamResource.custom('I', 'am', 'custom'), 'appsync:GraphQL'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/I', + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/am', + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/custom', + ], + ], + }, + ], + }, + ], + }, + }); + }); + + test('grant provides multiple permissions using one IamResource ofType call', () => { + // WHEN + importedApi.grant(role, appsync.IamResource.ofType('I', 'am', 'custom'), 'appsync:GraphQL'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/I/fields/am', + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/I/fields/custom', + ], + ], + }, + ], + }, + ], + }, + }); + }); + }); + + describe('grantMutation Permissions', () => { + test('grantMutation provides Mutation/* permissions when called with no `fields` argument', () => { + // WHEN + importedApi.grantMutation(role); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Mutation/*', + ], + ], + }, + }, + ], + }, + }); + }); + + test('grantMutation provides fields/[field param] permissions when called with `fields` argument', () => { + // WHEN + importedApi.grantMutation(role, 'addTest'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Mutation/fields/addTest', + ], + ], + }, + }, + ], + }, + }); + }); + + test('grantMutation provides multiple permissions when called with `fields` argument', () => { + // WHEN + importedApi.grantMutation(role, 'addTest', 'removeTest'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Mutation/fields/addTest', + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Mutation/fields/removeTest', + ], + ], + }, + ], + }, + ], + }, + }); + }); + }); + + describe('grantQuery Permissions', () => { + test('grantQuery provides Query/* permissions when called without the `fields` argument', () => { + // WHEN + importedApi.grantQuery(role); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Query/*', + ], + ], + }, + }, + ], + }, + }); + }); + + test('grantQuery provides fields/[field param] permissions when called with `fields` arugment', () => { + // WHEN + importedApi.grantQuery(role, 'getTest'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Query/fields/getTest', + ], + ], + }, + }, + ], + }, + }); + }); + + test('grantQuery provides multiple permissions when called with `fields` argument', () => { + // WHEN + importedApi.grantQuery(role, 'getTests', 'getTest'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Query/fields/getTests', + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Query/fields/getTest', + ], + ], + }, + ], + }, + ], + }, + }); + }); + }); + + describe('grantSubscription Permissions', () => { + test('grantSubscription provides Subscription/* permissions when called without `fields` argument', () => { + // WHEN + importedApi.grantSubscription(role); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Subscription/*', + ], + ], + }, + }, + ], + }, + }); + }); + + test('grantSubscription provides fields/[field param] when called with `field` argument', () => { + importedApi.grantSubscription(role, 'subscribe'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Subscription/fields/subscribe', + ], + ], + }, + }, + ], + }, + }); + }); + + test('grantSubscription provides multiple permissions when called with `fields` argument', () => { + // WHEN + importedApi.grantSubscription(role, 'subscribe', 'custom'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Subscription/fields/subscribe', + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/importedId/types/Subscription/fields/custom', + ], + ], + }, + ], + }, + ], + }, + }); + }); + }); }); \ No newline at end of file