From 922a9dcf07174334ac67b9fcbacb01aafdfd9c6a Mon Sep 17 00:00:00 2001 From: Ben Chaimberg Date: Fri, 11 Mar 2022 17:29:27 -0500 Subject: [PATCH] feat(appsync): add OpenSearch domain data source (#16529) Deprecate `ElasticsearchDataSource` and introduce `OpenSearchDataSource` as a replacement. closes #16528 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-appsync/README.md | 17 +- .../@aws-cdk/aws-appsync/lib/data-source.ts | 49 +++- .../aws-appsync/lib/graphqlapi-base.ts | 38 +++- packages/@aws-cdk/aws-appsync/package.json | 2 + .../test/appsync-elasticsearch.test.ts | 17 +- .../test/appsync-opensearch.test.ts | 150 +++++++++++++ .../integ.graphql-opensearch.expected.json | 210 ++++++++++++++++++ .../test/integ.graphql-opensearch.ts | 66 ++++++ ...3_AppSync_OpenSearch_DataSource_patch.json | 31 +++ 9 files changed, 554 insertions(+), 26 deletions(-) create mode 100644 packages/@aws-cdk/aws-appsync/test/appsync-opensearch.test.ts create mode 100644 packages/@aws-cdk/aws-appsync/test/integ.graphql-opensearch.expected.json create mode 100644 packages/@aws-cdk/aws-appsync/test/integ.graphql-opensearch.ts create mode 100644 packages/@aws-cdk/cfnspec/spec-source/903_AppSync_OpenSearch_DataSource_patch.json diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index 8e78c92bd4b7b..4d7c6f1014a45 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -240,18 +240,19 @@ httpDs.createResolver({ }); ``` -### Elasticsearch +### Amazon OpenSearch Service -AppSync has builtin support for Elasticsearch from domains that are provisioned -through your AWS account. You can use AppSync resolvers to perform GraphQL operations -such as queries, mutations, and subscriptions. +AppSync has builtin support for Amazon OpenSearch Service (successor to Amazon +Elasticsearch Service) from domains that are provisioned through your AWS account. You can +use AppSync resolvers to perform GraphQL operations such as queries, mutations, and +subscriptions. ```ts -import * as es from '@aws-cdk/aws-elasticsearch'; +import * as opensearch from '@aws-cdk/aws-opensearchservice'; const user = new iam.User(this, 'User'); -const domain = new es.Domain(this, 'Domain', { - version: es.ElasticsearchVersion.V7_1, +const domain = new opensearch.Domain(this, 'Domain', { + version: opensearch.EngineVersion.OPENSEARCH_1_1, removalPolicy: RemovalPolicy.DESTROY, fineGrainedAccessControl: { masterUserArn: user.userArn }, encryptionAtRest: { enabled: true }, @@ -260,7 +261,7 @@ const domain = new es.Domain(this, 'Domain', { }); declare const api: appsync.GraphqlApi; -const ds = api.addElasticsearchDataSource('ds', domain); +const ds = api.addOpenSearchDataSource('ds', domain); ds.createResolver({ typeName: 'Query', diff --git a/packages/@aws-cdk/aws-appsync/lib/data-source.ts b/packages/@aws-cdk/aws-appsync/lib/data-source.ts index c89479c4ef69b..7781f0c57d0af 100644 --- a/packages/@aws-cdk/aws-appsync/lib/data-source.ts +++ b/packages/@aws-cdk/aws-appsync/lib/data-source.ts @@ -1,7 +1,8 @@ import { ITable } from '@aws-cdk/aws-dynamodb'; -import { IDomain } from '@aws-cdk/aws-elasticsearch'; +import { IDomain as IElasticsearchDomain } from '@aws-cdk/aws-elasticsearch'; import { Grant, IGrantable, IPrincipal, IRole, Role, ServicePrincipal } from '@aws-cdk/aws-iam'; import { IFunction } from '@aws-cdk/aws-lambda'; +import { IDomain as IOpenSearchDomain } from '@aws-cdk/aws-opensearchservice'; import { IServerlessCluster } from '@aws-cdk/aws-rds'; import { ISecret } from '@aws-cdk/aws-secretsmanager'; import { IResolvable, Lazy, Stack } from '@aws-cdk/core'; @@ -64,11 +65,18 @@ export interface ExtendedDataSourceProps { */ readonly dynamoDbConfig?: CfnDataSource.DynamoDBConfigProperty | IResolvable; /** - * configuration for Elasticsearch Datasource + * configuration for Elasticsearch data source * + * @deprecated - use `openSearchConfig` * @default - No config */ readonly elasticsearchConfig?: CfnDataSource.ElasticsearchConfigProperty | IResolvable; + /** + * configuration for OpenSearch data source + * + * @default - No config + */ + readonly openSearchServiceConfig?: CfnDataSource.OpenSearchServiceConfigProperty | IResolvable; /** * configuration for HTTP Datasource * @@ -370,17 +378,21 @@ export class RdsDataSource extends BackedDataSource { } /** - * Properities for the Elasticsearch Data Source + * Properties for the Elasticsearch Data Source + * + * @deprecated - use `OpenSearchDataSourceProps` with `OpenSearchDataSource` */ export interface ElasticsearchDataSourceProps extends BackedDataSourceProps { /** * The elasticsearch domain containing the endpoint for the data source */ - readonly domain: IDomain; + readonly domain: IElasticsearchDomain; } /** * An Appsync datasource backed by Elasticsearch + * + * @deprecated - use `OpenSearchDataSource` */ export class ElasticsearchDataSource extends BackedDataSource { constructor(scope: Construct, id: string, props: ElasticsearchDataSourceProps) { @@ -394,4 +406,31 @@ export class ElasticsearchDataSource extends BackedDataSource { props.domain.grantReadWrite(this); } -} \ No newline at end of file +} + +/** + * Properties for the OpenSearch Data Source + */ +export interface OpenSearchDataSourceProps extends BackedDataSourceProps { + /** + * The OpenSearch domain containing the endpoint for the data source + */ + readonly domain: IOpenSearchDomain; +} + +/** + * An Appsync datasource backed by OpenSearch + */ +export class OpenSearchDataSource extends BackedDataSource { + constructor(scope: Construct, id: string, props: OpenSearchDataSourceProps) { + super(scope, id, props, { + type: 'AMAZON_OPENSEARCH_SERVICE', + openSearchServiceConfig: { + awsRegion: props.domain.stack.region, + endpoint: `https://${props.domain.domainEndpoint}`, + }, + }); + + props.domain.grantReadWrite(this); + } +} diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts index dfd596b929903..0737be10c8fc3 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts @@ -1,10 +1,11 @@ import { ITable } from '@aws-cdk/aws-dynamodb'; -import { IDomain } from '@aws-cdk/aws-elasticsearch'; +import { IDomain as IElasticsearchDomain } from '@aws-cdk/aws-elasticsearch'; import { IFunction } from '@aws-cdk/aws-lambda'; +import { IDomain as IOpenSearchDomain } from '@aws-cdk/aws-opensearchservice'; import { IServerlessCluster } from '@aws-cdk/aws-rds'; import { ISecret } from '@aws-cdk/aws-secretsmanager'; import { CfnResource, IResource, Resource } from '@aws-cdk/core'; -import { DynamoDbDataSource, HttpDataSource, LambdaDataSource, NoneDataSource, RdsDataSource, AwsIamConfig, ElasticsearchDataSource } from './data-source'; +import { DynamoDbDataSource, HttpDataSource, LambdaDataSource, NoneDataSource, RdsDataSource, AwsIamConfig, ElasticsearchDataSource, OpenSearchDataSource } from './data-source'; import { Resolver, ExtendedResolverProps } from './resolver'; /** @@ -114,11 +115,21 @@ export interface IGraphqlApi extends IResource { /** * add a new elasticsearch data source to this API * + * @deprecated - use `addOpenSearchDataSource` * @param id The data source's id * @param domain The elasticsearch domain for this data source * @param options The optional configuration for this data source */ - addElasticsearchDataSource(id: string, domain: IDomain, options?: DataSourceOptions): ElasticsearchDataSource; + addElasticsearchDataSource(id: string, domain: IElasticsearchDomain, options?: DataSourceOptions): ElasticsearchDataSource; + + /** + * Add a new OpenSearch data source to this API + * + * @param id The data source's id + * @param domain The OpenSearch domain for this data source + * @param options The optional configuration for this data source + */ + addOpenSearchDataSource(id: string, domain: IOpenSearchDomain, options?: DataSourceOptions): OpenSearchDataSource; /** * creates a new resolver for this datasource and API using the given properties @@ -241,11 +252,12 @@ export abstract class GraphqlApiBase extends Resource implements IGraphqlApi { /** * add a new elasticsearch data source to this API * + * @deprecated - use `addOpenSearchDataSource` * @param id The data source's id * @param domain The elasticsearch domain for this data source * @param options The optional configuration for this data source */ - public addElasticsearchDataSource(id: string, domain: IDomain, options?: DataSourceOptions): ElasticsearchDataSource { + public addElasticsearchDataSource(id: string, domain: IElasticsearchDomain, options?: DataSourceOptions): ElasticsearchDataSource { return new ElasticsearchDataSource(this, id, { api: this, name: options?.name, @@ -254,6 +266,22 @@ export abstract class GraphqlApiBase extends Resource implements IGraphqlApi { }); } + /** + * add a new OpenSearch data source to this API + * + * @param id The data source's id + * @param domain The OpenSearch domain for this data source + * @param options The optional configuration for this data source + */ + public addOpenSearchDataSource(id: string, domain: IOpenSearchDomain, options?: DataSourceOptions): OpenSearchDataSource { + return new OpenSearchDataSource(this, id, { + api: this, + name: options?.name, + description: options?.description, + domain, + }); + } + /** * creates a new resolver for this datasource and API using the given properties */ @@ -273,4 +301,4 @@ export abstract class GraphqlApiBase extends Resource implements IGraphqlApi { construct; return false; } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index f158ec0f97599..db0d74aa48788 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -95,6 +95,7 @@ "@aws-cdk/aws-elasticsearch": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/aws-opensearchservice": "0.0.0", "@aws-cdk/aws-rds": "0.0.0", "@aws-cdk/aws-s3-assets": "0.0.0", "@aws-cdk/aws-secretsmanager": "0.0.0", @@ -109,6 +110,7 @@ "@aws-cdk/aws-elasticsearch": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/aws-opensearchservice": "0.0.0", "@aws-cdk/aws-rds": "0.0.0", "@aws-cdk/aws-s3-assets": "0.0.0", "@aws-cdk/aws-secretsmanager": "0.0.0", diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-elasticsearch.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-elasticsearch.test.ts index 89199d65ca629..ad5057cf5d999 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-elasticsearch.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-elasticsearch.test.ts @@ -1,6 +1,7 @@ import * as path from 'path'; import { Template } from '@aws-cdk/assertions'; import * as es from '@aws-cdk/aws-elasticsearch'; +import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import * as cdk from '@aws-cdk/core'; import * as appsync from '../lib'; @@ -20,7 +21,7 @@ beforeEach(() => { }); describe('Elasticsearch Data Source Configuration', () => { - test('Elasticsearch configure properly', () => { + testDeprecated('Elasticsearch configure properly', () => { // WHEN api.addElasticsearchDataSource('ds', domain); @@ -51,7 +52,7 @@ describe('Elasticsearch Data Source Configuration', () => { }); }); - test('Elastic search configuration contains fully qualified url', () => { + testDeprecated('Elastic search configuration contains fully qualified url', () => { // WHEN api.addElasticsearchDataSource('ds', domain); @@ -67,7 +68,7 @@ describe('Elasticsearch Data Source Configuration', () => { }); }); - test('default configuration produces name identical to the id', () => { + testDeprecated('default configuration produces name identical to the id', () => { // WHEN api.addElasticsearchDataSource('ds', domain); @@ -78,7 +79,7 @@ describe('Elasticsearch Data Source Configuration', () => { }); }); - test('appsync configures name correctly', () => { + testDeprecated('appsync configures name correctly', () => { // WHEN api.addElasticsearchDataSource('ds', domain, { name: 'custom', @@ -91,7 +92,7 @@ describe('Elasticsearch Data Source Configuration', () => { }); }); - test('appsync configures name and description correctly', () => { + testDeprecated('appsync configures name and description correctly', () => { // WHEN api.addElasticsearchDataSource('ds', domain, { name: 'custom', @@ -106,7 +107,7 @@ describe('Elasticsearch Data Source Configuration', () => { }); }); - test('appsync errors when creating multiple elasticsearch data sources with no configuration', () => { + testDeprecated('appsync errors when creating multiple elasticsearch data sources with no configuration', () => { // WHEN const when = () => { api.addElasticsearchDataSource('ds', domain); @@ -119,7 +120,7 @@ describe('Elasticsearch Data Source Configuration', () => { }); describe('adding elasticsearch data source from imported api', () => { - test('imported api can add ElasticsearchDataSource from id', () => { + testDeprecated('imported api can add ElasticsearchDataSource from id', () => { // WHEN const importedApi = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'importedApi', { graphqlApiId: api.apiId, @@ -133,7 +134,7 @@ describe('adding elasticsearch data source from imported api', () => { }); }); - test('imported api can add ElasticsearchDataSource from attributes', () => { + testDeprecated('imported api can add ElasticsearchDataSource from attributes', () => { // WHEN const importedApi = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'importedApi', { graphqlApiId: api.apiId, diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-opensearch.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-opensearch.test.ts new file mode 100644 index 0000000000000..92cf5d26c0abf --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/test/appsync-opensearch.test.ts @@ -0,0 +1,150 @@ +import * as path from 'path'; +import { Template } from '@aws-cdk/assertions'; +import * as opensearch from '@aws-cdk/aws-opensearchservice'; +import * as cdk from '@aws-cdk/core'; +import * as appsync from '../lib'; + +// GLOBAL GIVEN +let stack: cdk.Stack; +let api: appsync.GraphqlApi; +let domain: opensearch.Domain; +beforeEach(() => { + stack = new cdk.Stack(); + api = new appsync.GraphqlApi(stack, 'baseApi', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), + }); + domain = new opensearch.Domain(stack, 'OsDomain', { + version: opensearch.EngineVersion.OPENSEARCH_1_1, + }); +}); + +describe('OpenSearch Data Source Configuration', () => { + test('OpenSearch configure properly', () => { + // WHEN + api.addOpenSearchDataSource('ds', domain); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Version: '2012-10-17', + Statement: [{ + Action: [ + 'es:ESHttpGet', + 'es:ESHttpHead', + 'es:ESHttpDelete', + 'es:ESHttpPost', + 'es:ESHttpPut', + 'es:ESHttpPatch', + ], + Effect: 'Allow', + Resource: [{ + 'Fn::GetAtt': ['OsDomain5D09FC6A', 'Arn'], + }, + { + 'Fn::Join': ['', [{ + 'Fn::GetAtt': ['OsDomain5D09FC6A', 'Arn'], + }, '/*']], + }], + }], + }, + }); + }); + + test('OpenSearch configuration contains fully qualified url', () => { + // WHEN + api.addOpenSearchDataSource('ds', domain); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppSync::DataSource', { + OpenSearchServiceConfig: { + Endpoint: { + 'Fn::Join': ['', ['https://', { + 'Fn::GetAtt': ['OsDomain5D09FC6A', 'DomainEndpoint'], + }]], + }, + }, + }); + }); + + test('default configuration produces name identical to the id', () => { + // WHEN + api.addOpenSearchDataSource('ds', domain); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppSync::DataSource', { + Type: 'AMAZON_OPENSEARCH_SERVICE', + Name: 'ds', + }); + }); + + test('appsync configures name correctly', () => { + // WHEN + api.addOpenSearchDataSource('ds', domain, { + name: 'custom', + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppSync::DataSource', { + Type: 'AMAZON_OPENSEARCH_SERVICE', + Name: 'custom', + }); + }); + + test('appsync configures name and description correctly', () => { + // WHEN + api.addOpenSearchDataSource('ds', domain, { + name: 'custom', + description: 'custom description', + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppSync::DataSource', { + Type: 'AMAZON_OPENSEARCH_SERVICE', + Name: 'custom', + Description: 'custom description', + }); + }); + + test('appsync errors when creating multiple openSearch data sources with no configuration', () => { + // WHEN + const when = () => { + api.addOpenSearchDataSource('ds', domain); + api.addOpenSearchDataSource('ds', domain); + }; + + // THEN + expect(when).toThrow('There is already a Construct with name \'ds\' in GraphqlApi [baseApi]'); + }); +}); + +describe('adding openSearch data source from imported api', () => { + test('imported api can add OpenSearchDataSource from id', () => { + // WHEN + const importedApi = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'importedApi', { + graphqlApiId: api.apiId, + }); + importedApi.addOpenSearchDataSource('ds', domain); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppSync::DataSource', { + Type: 'AMAZON_OPENSEARCH_SERVICE', + ApiId: { 'Fn::GetAtt': ['baseApiCDA4D43A', 'ApiId'] }, + }); + }); + + test('imported api can add OpenSearchDataSource from attributes', () => { + // WHEN + const importedApi = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'importedApi', { + graphqlApiId: api.apiId, + graphqlApiArn: api.arn, + }); + importedApi.addOpenSearchDataSource('ds', domain); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppSync::DataSource', { + Type: 'AMAZON_OPENSEARCH_SERVICE', + ApiId: { 'Fn::GetAtt': ['baseApiCDA4D43A', 'ApiId'] }, + }); + }); +}); diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql-opensearch.expected.json b/packages/@aws-cdk/aws-appsync/test/integ.graphql-opensearch.expected.json new file mode 100644 index 0000000000000..9de84b5ba5944 --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql-opensearch.expected.json @@ -0,0 +1,210 @@ +{ + "Resources": { + "User00B015A1": { + "Type": "AWS::IAM::User" + }, + "Domain66AC69E0": { + "Type": "AWS::OpenSearchService::Domain", + "Properties": { + "AdvancedSecurityOptions": { + "Enabled": true, + "InternalUserDatabaseEnabled": false, + "MasterUserOptions": { + "MasterUserARN": { + "Fn::GetAtt": [ + "User00B015A1", + "Arn" + ] + } + } + }, + "ClusterConfig": { + "DedicatedMasterEnabled": false, + "InstanceCount": 1, + "InstanceType": "r5.large.search", + "ZoneAwarenessEnabled": false + }, + "CognitoOptions": { + "Enabled": false + }, + "DomainEndpointOptions": { + "EnforceHTTPS": true, + "TLSSecurityPolicy": "Policy-Min-TLS-1-0-2019-07" + }, + "EBSOptions": { + "EBSEnabled": true, + "VolumeSize": 10, + "VolumeType": "gp2" + }, + "EncryptionAtRestOptions": { + "Enabled": true + }, + "EngineVersion": "OpenSearch_1.1", + "LogPublishingOptions": {}, + "NodeToNodeEncryptionOptions": { + "Enabled": true + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "apiC8550315": { + "Type": "AWS::AppSync::GraphQLApi", + "Properties": { + "AuthenticationType": "API_KEY", + "Name": "api" + } + }, + "apiSchema0EA92056": { + "Type": "AWS::AppSync::GraphQLSchema", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "apiC8550315", + "ApiId" + ] + }, + "Definition": "type test {\n version: String!\n}\ntype Query {\n getTests: [test]!\n}\ntype Mutation {\n addTest(version: String!): test\n}\n" + } + }, + "apiDefaultApiKey6AB8D7C4": { + "Type": "AWS::AppSync::ApiKey", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "apiC8550315", + "ApiId" + ] + } + }, + "DependsOn": [ + "apiSchema0EA92056" + ] + }, + "apidsServiceRoleBDB08107": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appsync.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "apidsServiceRoleDefaultPolicy5634EFD0": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "es:ESHttpGet", + "es:ESHttpHead", + "es:ESHttpDelete", + "es:ESHttpPost", + "es:ESHttpPut", + "es:ESHttpPatch" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "Domain66AC69E0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Domain66AC69E0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "apidsServiceRoleDefaultPolicy5634EFD0", + "Roles": [ + { + "Ref": "apidsServiceRoleBDB08107" + } + ] + } + }, + "apids4328272F": { + "Type": "AWS::AppSync::DataSource", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "apiC8550315", + "ApiId" + ] + }, + "Name": "ds", + "Type": "AMAZON_OPENSEARCH_SERVICE", + "OpenSearchServiceConfig": { + "AwsRegion": { + "Ref": "AWS::Region" + }, + "Endpoint": { + "Fn::Join": [ + "", + [ + "https://", + { + "Fn::GetAtt": [ + "Domain66AC69E0", + "DomainEndpoint" + ] + } + ] + ] + } + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "apidsServiceRoleBDB08107", + "Arn" + ] + } + } + }, + "apidsQuerygetTestsResolver5C6FBB59": { + "Type": "AWS::AppSync::Resolver", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "apiC8550315", + "ApiId" + ] + }, + "FieldName": "getTests", + "TypeName": "Query", + "DataSourceName": "ds", + "Kind": "UNIT", + "RequestMappingTemplate": "{\"version\":\"2017-02-28\",\"operation\":\"GET\",\"path\":\"/id/post/_search\",\"params\":{\"headers\":{},\"queryString\":{},\"body\":{\"from\":0,\"size\":50}}}", + "ResponseMappingTemplate": "{\"version\":\"2017-02-28\",\"operation\":\"GET\",\"path\":\"/id/post/_search\",\"params\":{\"headers\":{},\"queryString\":{},\"body\":{\"from\":0,\"size\":50,\"query\":{\"term\":{\"author\":\"$util.toJson($context.arguments.author)\"}}}}}" + }, + "DependsOn": [ + "apids4328272F", + "apiSchema0EA92056" + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql-opensearch.ts b/packages/@aws-cdk/aws-appsync/test/integ.graphql-opensearch.ts new file mode 100644 index 0000000000000..e055bfe15c83b --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql-opensearch.ts @@ -0,0 +1,66 @@ +import * as path from 'path'; +import { User } from '@aws-cdk/aws-iam'; +import * as opensearch from '@aws-cdk/aws-opensearchservice'; +import * as cdk from '@aws-cdk/core'; +import * as appsync from '../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'appsync-opensearch'); +const user = new User(stack, 'User'); +const domain = new opensearch.Domain(stack, 'Domain', { + version: opensearch.EngineVersion.OPENSEARCH_1_1, + removalPolicy: cdk.RemovalPolicy.DESTROY, + fineGrainedAccessControl: { + masterUserArn: user.userArn, + }, + encryptionAtRest: { + enabled: true, + }, + nodeToNodeEncryption: true, + enforceHttps: true, +}); + +const api = new appsync.GraphqlApi(stack, 'api', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), +}); + +const ds = api.addOpenSearchDataSource('ds', domain); + +ds.createResolver({ + typeName: 'Query', + fieldName: 'getTests', + requestMappingTemplate: appsync.MappingTemplate.fromString(JSON.stringify({ + version: '2017-02-28', + operation: 'GET', + path: '/id/post/_search', + params: { + headers: {}, + queryString: {}, + body: { + from: 0, + size: 50, + }, + }, + })), + responseMappingTemplate: appsync.MappingTemplate.fromString(JSON.stringify({ + version: '2017-02-28', + operation: 'GET', + path: '/id/post/_search', + params: { + headers: {}, + queryString: {}, + body: { + from: 0, + size: 50, + query: { + term: { + author: '$util.toJson($context.arguments.author)', + }, + }, + }, + }, + })), +}); + +app.synth(); diff --git a/packages/@aws-cdk/cfnspec/spec-source/903_AppSync_OpenSearch_DataSource_patch.json b/packages/@aws-cdk/cfnspec/spec-source/903_AppSync_OpenSearch_DataSource_patch.json new file mode 100644 index 0000000000000..1fd421d63c42c --- /dev/null +++ b/packages/@aws-cdk/cfnspec/spec-source/903_AppSync_OpenSearch_DataSource_patch.json @@ -0,0 +1,31 @@ +{ + "PropertyTypes": { + "patch": { + "description": "Copy Elasticsearch AppSync DataSource property to OpenSearch", + "operations": [ + { + "op": "copy", + "from": "/AWS::AppSync::DataSource.ElasticsearchConfig", + "path": "/AWS::AppSync::DataSource.OpenSearchConfig" + } + ] + } + }, + "ResourceTypes": { + "patch": { + "description": "Copy Elasticsearch AppSync DataSource resource to OpenSearch", + "operations": [ + { + "op": "copy", + "from": "/AWS::AppSync::DataSource/Properties/ElasticsearchConfig", + "path": "/AWS::AppSync::DataSource/Properties/OpenSearchConfig" + }, + { + "op": "replace", + "path": "/AWS::AppSync::DataSource/Properties/OpenSearchConfig/Type", + "value": "OpenSearchConfig" + } + ] + } + } +}