diff --git a/packages/@aws-cdk/aws-location-alpha/README.md b/packages/@aws-cdk/aws-location-alpha/README.md index 7caa3522d4b11..0afe2c9e1c40e 100644 --- a/packages/@aws-cdk/aws-location-alpha/README.md +++ b/packages/@aws-cdk/aws-location-alpha/README.md @@ -24,6 +24,36 @@ global, trusted providers Esri and HERE. With affordable data, tracking and geof capabilities, and built-in metrics for health monitoring, you can build sophisticated location-enabled applications. +## Map + +The Amazon Location Service Map resource gives you access to the underlying basemap data for a map. +You use the Map resource with a map rendering library to add an interactive map to your application. +You can add other functionality to your map, such as markers (or pins), routes, and polygon areas, as needed for your application. + +For information about how to use map resources in practice, see [Using Amazon Location Maps in your application](https://docs.aws.amazon.com/location/latest/developerguide/using-maps.html). + +To create a map, define a `Map`: + +```ts +new location.Map(this, 'Map', { + mapName: 'my-map', + style: location.Style.VECTOR_ESRI_NAVIGATION, + customLayers: [location.CustomLayer.POI], +}); +``` + +Use the `grant()` or `grantRendering()` method to grant the given identity permissions to perform actions +on the map: + +```ts +declare const role: iam.Role; + +const map = new location.Map(this, 'Map', { + style: location.Style.VECTOR_ESRI_NAVIGATION, +}); +map.grantRendering(role); +``` + ## Place Index A key function of Amazon Location Service is the ability to search the geolocation information. diff --git a/packages/@aws-cdk/aws-location-alpha/lib/index.ts b/packages/@aws-cdk/aws-location-alpha/lib/index.ts index fe42b8efee131..871e150d2d823 100644 --- a/packages/@aws-cdk/aws-location-alpha/lib/index.ts +++ b/packages/@aws-cdk/aws-location-alpha/lib/index.ts @@ -1,4 +1,5 @@ export * from './geofence-collection'; +export * from './map'; export * from './place-index'; export * from './route-calculator'; export * from './tracker'; diff --git a/packages/@aws-cdk/aws-location-alpha/lib/map.ts b/packages/@aws-cdk/aws-location-alpha/lib/map.ts new file mode 100644 index 0000000000000..3f9a9078163b7 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/lib/map.ts @@ -0,0 +1,333 @@ +import * as iam from 'aws-cdk-lib/aws-iam'; +import { ArnFormat, IResource, Lazy, Resource, Stack, Token } from 'aws-cdk-lib/core'; +import { Construct } from 'constructs'; +import { CfnMap } from 'aws-cdk-lib/aws-location'; +import { generateUniqueId } from './util'; + +/** + * Represents the Amazon Location Service Map + */ +export interface IMap extends IResource { + /** + * The name of the map + * + * @attribute + */ + readonly mapName: string; + + /** + * The Amazon Resource Name (ARN) of the Map + * + * @attribute Arn, MapArn + */ + readonly mapArn: string; +} + +/** + * Properties for the Amazon Location Service Map + */ +export interface MapProps { + /** + * A name for the map + * + * Must be between 1 and 100 characters and contain only alphanumeric characters, + * hyphens, periods and underscores. + * + * @default - A name is automatically generated + */ + readonly mapName?: string; + + /** + * A description for the map + * + * @default - no description + */ + readonly description?: string; + + /** + * Specifies the map style selected from an available data provider. + */ + readonly style: Style; + + /** + * Specifies the custom layers for the style. + * + * @default - no custom layes + */ + readonly customLayers?: CustomLayer[]; + + /** + * Specifies the map political view selected from an available data provider. + * + * The political view must be used in compliance with applicable laws, including those laws about mapping of the country or region where the maps, + * images, and other data and third-party content which you access through Amazon Location Service is made available. + * + * @see https://docs.aws.amazon.com/location/latest/developerguide/map-concepts.html#political-views + * + * @default - no political view + */ + readonly politicalView?: PoliticalView; +} + +/** + * An additional layer you can enable for a map style. + */ +export enum CustomLayer { + /** + * The POI custom layer adds a richer set of places, such as shops, services, restaurants, attractions, and other points of interest to your map. + * Currently only the VectorEsriNavigation map style supports the POI custom layer. + */ + POI = 'POI', +} + +/** + * The map style selected from an available data provider. + * + * @see https://docs.aws.amazon.com/location/latest/developerguide/what-is-data-provider.html + */ +export enum Style { + + /** + * The Esri Navigation map style, which provides a detailed basemap for the world symbolized + * with a custom navigation map style that's designed for use during the day in mobile devices. + * It also includes a richer set of places, such as shops, services, restaurants, attractions, + * and other points of interest. Enable the POI layer by setting it in CustomLayers to leverage + * the additional places data. + */ + VECTOR_ESRI_NAVIGATION = 'VectorEsriNavigation', + + /** + * The Esri Imagery map style. A raster basemap that provides one meter or better + * satellite and aerial imagery in many parts of the world and lower resolution + * satellite imagery worldwide. + */ + RASTER_ESRI_IMAGERY = 'RasterEsriImagery', + + /** + * The Esri Light Gray Canvas map style, which provides a detailed vector basemap + * with a light gray, neutral background style with minimal colors, labels, and features + * that's designed to draw attention to your thematic content. + */ + VECTOR_ESRI_LIGHT_GRAY_CANVAS = 'VectorEsriLightGrayCanvas', + + /** + * The Esri Light map style, which provides a detailed vector basemap + * with a classic Esri map style. + */ + VECTOR_ESRI_TOPOGRAPHIC = 'VectorEsriTopographic', + + /** + * The Esri Street Map style, which provides a detailed vector basemap for the world + * symbolized with a classic Esri street map style. The vector tile layer is similar + * in content and style to the World Street Map raster map. + */ + VECTOR_ESRI_STREETS = 'VectorEsriStreets', + + /** + * The Esri Dark Gray Canvas map style. A vector basemap with a dark gray, + * neutral background with minimal colors, labels, and features that's designed + * to draw attention to your thematic content. + */ + VECTOR_ESRI_DARK_GRAY_CANVAS = 'VectorEsriDarkGrayCanvas', + + /** + * A default HERE map style containing a neutral, global map and its features + * including roads, buildings, landmarks, and water features. It also now includes + * a fully designed map of Japan. + */ + VECTOR_HERE_EXPLORE = 'VectorHereExplore', + + /** + * A global map containing high resolution satellite imagery. + */ + RASTER_HERE_EXPLORE_SATELLITE = 'RasterHereExploreSatellite', + + /** + * A global map displaying the road network, street names, and city labels + * over satellite imagery. This style will automatically retrieve both raster + * and vector tiles, and your charges will be based on total tiles retrieved. + */ + HYBRID_HERE_EXPLORE_SATELLITE = 'HybridHereExploreSatellite', + + /** + * The HERE Contrast (Berlin) map style is a high contrast detailed base map + * of the world that blends 3D and 2D rendering. + */ + VECTOR_HERE_CONTRAST = 'VectorHereContrast', + + /** + * A global map containing truck restrictions and attributes (e.g. width / height / HAZMAT) + * symbolized with highlighted segments and icons on top of HERE Explore to support + * use cases within transport and logistics. + */ + VECTOR_HERE_EXPLORE_TRUCK = 'VectorHereExploreTruck', + + /** + * The Grab Standard Light map style provides a basemap with detailed land use coloring, + * area names, roads, landmarks, and points of interest covering Southeast Asia. + */ + VECTOR_GRAB_STANDARD_LIGHT = 'VectorGrabStandardLight', + + /** + * The Grab Standard Dark map style provides a dark variation of the standard basemap + * covering Southeast Asia. + */ + VECTOR_GRAB_STANDARD_DARK = 'VectorGrabStandardDark', + + /** + * The Open Data Standard Light map style provides a detailed basemap for the world + * suitable for website and mobile application use. The map includes highways major roads, + * minor roads, railways, water features, cities, parks, landmarks, building footprints, + * and administrative boundaries. + */ + VECTOR_OPEN_DATA_STANDARD_LIGHT = 'VectorOpenDataStandardLight', + + /** + * Open Data Standard Dark is a dark-themed map style that provides a detailed basemap + * for the world suitable for website and mobile application use. The map includes highways + * major roads, minor roads, railways, water features, cities, parks, landmarks, + * building footprints, and administrative boundaries. + */ + VECTOR_OPEN_DATA_STANDARD_DARK = 'VectorOpenDataStandardDark', + + /** + * The Open Data Visualization Light map style is a light-themed style with muted colors + * and fewer features that aids in understanding overlaid data. + */ + VECTOR_OPEN_DATA_VISUALIZATION_LIGHT = 'VectorOpenDataVisualizationLight', + + /** + * The Open Data Visualization Dark map style is a dark-themed style with muted colors + * and fewer features that aids in understanding overlaid data. + */ + VECTOR_OPEN_DATA_VISUALIZATION_DARK = 'VectorOpenDataVisualizationDark', + +} + +/** + * The map political view. + */ +export enum PoliticalView { + /** + * An India (IND) political view + */ + INDIA = 'IND', +} + +/** + * The Amazon Location Service Map + * + * @see https://docs.aws.amazon.com/location/latest/developerguide/map-concepts.html + */ +export class Map extends Resource implements IMap { + /** + * Use an existing map by name + */ + public static fromMapName(scope: Construct, id: string, mapName: string): IMap { + const mapArn = Stack.of(scope).formatArn({ + service: 'geo', + resource: 'map', + resourceName: mapName, + }); + + return Map.fromMapArn(scope, id, mapArn); + } + + /** + * Use an existing map by ARN + */ + public static fromMapArn(scope: Construct, id: string, mapArn: string): IMap { + const parsedArn = Stack.of(scope).splitArn(mapArn, ArnFormat.SLASH_RESOURCE_NAME); + + if (!parsedArn.resourceName) { + throw new Error(`Map Arn ${mapArn} does not have a resource name.`); + } + + class Import extends Resource implements IMap { + public readonly mapName = parsedArn.resourceName!; + public readonly mapArn = mapArn; + } + + return new Import(scope, id, { + account: parsedArn.account, + region: parsedArn.region, + }); + } + + public readonly mapName: string; + + public readonly mapArn: string; + + /** + * The timestamp for when the map resource was created in ISO 8601 format + * + * @attribute + */ + public readonly mapCreateTime: string; + + /** + * The timestamp for when the map resource was last updated in ISO 8601 format + * + * @attribute + */ + public readonly mapUpdateTime: string; + + constructor(scope: Construct, id: string, props: MapProps) { + if (props.description && !Token.isUnresolved(props.description) && props.description.length > 1000) { + throw new Error(`\`description\` must be between 0 and 1000 characters, got: ${props.description.length} characters.`); + } + + if (props.mapName !== undefined && !Token.isUnresolved(props.mapName)) { + if (props.mapName.length < 1 || props.mapName.length > 100) { + throw new Error(`\`mapName\` must be between 1 and 100 characters, got: ${props.mapName.length} characters.`); + } + + if (!/^[-._\w]+$/.test(props.mapName)) { + throw new Error(`\`mapName\` must contain only alphanumeric characters, hyphens, periods and underscores, got: ${props.mapName}.`); + } + } + + super(scope, id, { + physicalName: props.mapName ?? Lazy.string({ produce: () => generateUniqueId(this) }), + }); + + const map = new CfnMap(this, 'Resource', { + configuration: { + style: props.style, + customLayers: props.customLayers, + politicalView: props.politicalView, + }, + mapName: this.physicalName, + description: props.description, + }); + + this.mapName = map.ref; + this.mapArn = map.attrArn; + this.mapCreateTime = map.attrCreateTime; + this.mapUpdateTime = map.attrUpdateTime; + } + + /** + * Grant the given principal identity permissions to perform the actions on this map. + */ + public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { + return iam.Grant.addToPrincipal({ + grantee: grantee, + actions: actions, + resourceArns: [this.mapArn], + }); + } + + /** + * Grant the given identity permissions to rendering a map resource + * @See https://docs.aws.amazon.com/location/latest/developerguide/security_iam_id-based-policy-examples.html#security_iam_id-based-policy-examples-get-map-tiles + */ + public grantRendering(grantee: iam.IGrantable): iam.Grant { + return this.grant(grantee, + 'geo:GetMapTile', + 'geo:GetMapSprites', + 'geo:GetMapGlyphs', + 'geo:GetMapStyleDescriptor', + ); + } +} diff --git a/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/MapTestDefaultTestDeployAssert46CB2F9C.assets.json b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/MapTestDefaultTestDeployAssert46CB2F9C.assets.json new file mode 100644 index 0000000000000..9ad92102c9fa0 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/MapTestDefaultTestDeployAssert46CB2F9C.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "MapTestDefaultTestDeployAssert46CB2F9C.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/aws-location-alpha/test/integ.map.js.snapshot/MapTestDefaultTestDeployAssert46CB2F9C.template.json b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/MapTestDefaultTestDeployAssert46CB2F9C.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/MapTestDefaultTestDeployAssert46CB2F9C.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/aws-location-alpha/test/integ.map.js.snapshot/cdk-integ-location-map.assets.json b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/cdk-integ-location-map.assets.json new file mode 100644 index 0000000000000..db96b9213ce8c --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/cdk-integ-location-map.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "53db70e2c3a7b420db66cd2d99a17e4145e94dcb2126ef2904a9c4a5f790efe4": { + "source": { + "path": "cdk-integ-location-map.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "53db70e2c3a7b420db66cd2d99a17e4145e94dcb2126ef2904a9c4a5f790efe4.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/aws-location-alpha/test/integ.map.js.snapshot/cdk-integ-location-map.template.json b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/cdk-integ-location-map.template.json new file mode 100644 index 0000000000000..c3ced5977f561 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/cdk-integ-location-map.template.json @@ -0,0 +1,52 @@ +{ + "Resources": { + "Map207736EC": { + "Type": "AWS::Location::Map", + "Properties": { + "Configuration": { + "CustomLayers": [ + "POI" + ], + "PoliticalView": "IND", + "Style": "VectorEsriNavigation" + }, + "Description": "my map for test", + "MapName": "my-map" + } + } + }, + "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/aws-location-alpha/test/integ.map.js.snapshot/cdk.out b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/integ.json b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/integ.json new file mode 100644 index 0000000000000..f8ada3a714711 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "MapTest/DefaultTest": { + "stacks": [ + "cdk-integ-location-map" + ], + "assertionStack": "MapTest/DefaultTest/DeployAssert", + "assertionStackName": "MapTestDefaultTestDeployAssert46CB2F9C" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/manifest.json b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/manifest.json new file mode 100644 index 0000000000000..53433cfb7cd0d --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/manifest.json @@ -0,0 +1,113 @@ +{ + "version": "36.0.0", + "artifacts": { + "cdk-integ-location-map.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "cdk-integ-location-map.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "cdk-integ-location-map": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "cdk-integ-location-map.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}/53db70e2c3a7b420db66cd2d99a17e4145e94dcb2126ef2904a9c4a5f790efe4.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "cdk-integ-location-map.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": [ + "cdk-integ-location-map.assets" + ], + "metadata": { + "/cdk-integ-location-map/Map/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Map207736EC" + } + ], + "/cdk-integ-location-map/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/cdk-integ-location-map/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "cdk-integ-location-map" + }, + "MapTestDefaultTestDeployAssert46CB2F9C.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "MapTestDefaultTestDeployAssert46CB2F9C.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "MapTestDefaultTestDeployAssert46CB2F9C": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "MapTestDefaultTestDeployAssert46CB2F9C.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": [ + "MapTestDefaultTestDeployAssert46CB2F9C.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": [ + "MapTestDefaultTestDeployAssert46CB2F9C.assets" + ], + "metadata": { + "/MapTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/MapTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "MapTest/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/tree.json b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/tree.json new file mode 100644 index 0000000000000..71920df42ef38 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.map.js.snapshot/tree.json @@ -0,0 +1,133 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "cdk-integ-location-map": { + "id": "cdk-integ-location-map", + "path": "cdk-integ-location-map", + "children": { + "Map": { + "id": "Map", + "path": "cdk-integ-location-map/Map", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-integ-location-map/Map/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Location::Map", + "aws:cdk:cloudformation:props": { + "configuration": { + "style": "VectorEsriNavigation", + "customLayers": [ + "POI" + ], + "politicalView": "IND" + }, + "description": "my map for test", + "mapName": "my-map" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_location.CfnMap", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "cdk-integ-location-map/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "cdk-integ-location-map/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "MapTest": { + "id": "MapTest", + "path": "MapTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "MapTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "MapTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "MapTest/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "MapTest/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "MapTest/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/aws-location-alpha/test/integ.map.ts b/packages/@aws-cdk/aws-location-alpha/test/integ.map.ts new file mode 100644 index 0000000000000..76137c52ad533 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.map.ts @@ -0,0 +1,24 @@ +import { App, Stack } from 'aws-cdk-lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import { Construct } from 'constructs'; +import { CustomLayer, Map, PoliticalView, Style } from '../lib'; + +class TestStack extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + new Map(this, 'Map', { + mapName: 'my-map', + description: 'my map for test', + style: Style.VECTOR_ESRI_NAVIGATION, + customLayers: [CustomLayer.POI], + politicalView: PoliticalView.INDIA, + }); + } +} + +const app = new App(); + +new integ.IntegTest(app, 'MapTest', { + testCases: [new TestStack(app, 'cdk-integ-location-map')], +}); diff --git a/packages/@aws-cdk/aws-location-alpha/test/map.test.ts b/packages/@aws-cdk/aws-location-alpha/test/map.test.ts new file mode 100644 index 0000000000000..89f182a169f87 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/map.test.ts @@ -0,0 +1,122 @@ +import { Match, Template } from 'aws-cdk-lib/assertions'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import { Stack } from 'aws-cdk-lib'; +import { CustomLayer, Map, PoliticalView, Style } from '../lib'; + +let stack: Stack; +beforeEach(() => { + stack = new Stack(); +}); + +test('create a map', () => { + new Map(stack, 'Map', { + mapName: 'my-map', + description: 'my map for test', + style: Style.VECTOR_ESRI_NAVIGATION, + customLayers: [CustomLayer.POI], + politicalView: PoliticalView.INDIA, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Location::Map', { + MapName: 'my-map', + Description: 'my map for test', + Configuration: { + CustomLayers: ['POI'], + PoliticalView: 'IND', + Style: 'VectorEsriNavigation', + }, + }); +}); + +test('creates a map with empty description', () => { + new Map(stack, 'Map', { + description: '', + style: Style.VECTOR_ESRI_NAVIGATION, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Location::Map', { + Description: '', + }); +}); + +test('throws with invalid description', () => { + expect(() => new Map(stack, 'Map', { + description: 'a'.repeat(1001), + style: Style.VECTOR_ESRI_NAVIGATION, + })).toThrow('`description` must be between 0 and 1000 characters, got: 1001 characters.'); +}); + +test('throws with invalid name', () => { + expect(() => new Map(stack, 'Map', { + mapName: 'inv@lid', + style: Style.VECTOR_ESRI_NAVIGATION, + })).toThrow('`mapName` must contain only alphanumeric characters, hyphens, periods and underscores, got: inv@lid.'); +}); + +test.each(['', 'a'.repeat(101)])('throws with invalid name, got: %s', (mapName) => { + expect(() => new Map(stack, 'Map', { + mapName, + style: Style.VECTOR_ESRI_NAVIGATION, + })).toThrow(`\`mapName\` must be between 1 and 100 characters, got: ${mapName.length} characters.`); +}); + +test('grant rendering ', () => { + const map = new Map(stack, 'Map', { + style: Style.VECTOR_ESRI_NAVIGATION, + }); + + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('foo'), + }); + + map.grantRendering(role); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', Match.objectLike({ + PolicyDocument: Match.objectLike({ + Statement: [ + { + Action: [ + 'geo:GetMapTile', + 'geo:GetMapSprites', + 'geo:GetMapGlyphs', + 'geo:GetMapStyleDescriptor', + ], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'Map207736EC', + 'Arn', + ], + }, + }, + ], + }), + })); +}); + +test('import from arn', () => { + const mapArn = stack.formatArn({ + service: 'geo', + resource: 'map', + resourceName: 'MyMap', + }); + const map = Map.fromMapArn(stack, 'Map', mapArn); + + // THEN + expect(map.mapName).toEqual('MyMap'); + expect(map.mapArn).toEqual(mapArn); +}); + +test('import from name', () => { + // WHEN + const mapName = 'MyMap'; + const map = Map.fromMapName(stack, 'Map', mapName); + + // THEN + expect(map.mapName).toEqual(mapName); + expect(map.mapArn).toEqual(stack.formatArn({ + service: 'geo', + resource: 'map', + resourceName: 'MyMap', + })); +});