diff --git a/packages/@aws-cdk/aws-glue/README.md b/packages/@aws-cdk/aws-glue/README.md index 60dbd498d99f2..e0483002092d9 100644 --- a/packages/@aws-cdk/aws-glue/README.md +++ b/packages/@aws-cdk/aws-glue/README.md @@ -216,6 +216,26 @@ new glue.Table(this, 'MyTable', { By default, an S3 bucket will be created to store the table's data and stored in the bucket root. You can also manually pass the `bucket` and `s3Prefix`: +### View + +A Glue view is a special glue Table that reference other existing glue tables or views. + +```ts +declare const myDatabase: glue.Database; +new glue.View(this, 'MyView', { + database: myDatabase, + tableName: 'my_view', + columns: [{ + name: 'col', + type: glue.Schema.STRING, + }], + sql: 'select "1" as col', + }); +``` + +As the Glue view is nothing more than a special glue table it needs a `tableName` property. +In order to make the view working, ensure `sql` property validity, and constitency with view `columns`. + ### Partition Keys To improve query performance, a table can specify `partitionKeys` on which data is stored and queried separately. For example, you might partition a table by `year` and `month` to optimize queries based on a time window: diff --git a/packages/@aws-cdk/aws-glue/lib/schema.ts b/packages/@aws-cdk/aws-glue/lib/schema.ts index aba682da7e0df..7d4a8db9589e0 100644 --- a/packages/@aws-cdk/aws-glue/lib/schema.ts +++ b/packages/@aws-cdk/aws-glue/lib/schema.ts @@ -33,6 +33,11 @@ export interface Type { * Glue InputString for this type. */ readonly inputString: string; + + /** + * Glue type converted in Presto type for View usage + */ + readonly prestoInputString: string; } /** @@ -42,11 +47,13 @@ export class Schema { public static readonly BOOLEAN: Type = { isPrimitive: true, inputString: 'boolean', + prestoInputString: 'boolean', }; public static readonly BINARY: Type = { isPrimitive: true, inputString: 'binary', + prestoInputString: 'varbinary', }; /** @@ -55,16 +62,19 @@ export class Schema { public static readonly BIG_INT: Type = { isPrimitive: true, inputString: 'bigint', + prestoInputString: 'bigint', }; public static readonly DOUBLE: Type = { isPrimitive: true, inputString: 'double', + prestoInputString: 'double', }; public static readonly FLOAT: Type = { isPrimitive: true, inputString: 'float', + prestoInputString: 'real', }; /** @@ -73,6 +83,7 @@ export class Schema { public static readonly INTEGER: Type = { isPrimitive: true, inputString: 'int', + prestoInputString: 'integer', }; /** @@ -81,6 +92,7 @@ export class Schema { public static readonly SMALL_INT: Type = { isPrimitive: true, inputString: 'smallint', + prestoInputString: 'smallint', }; /** @@ -89,6 +101,7 @@ export class Schema { public static readonly TINY_INT: Type = { isPrimitive: true, inputString: 'tinyint', + prestoInputString: 'tinyint', }; /** @@ -97,6 +110,7 @@ export class Schema { public static readonly DATE: Type = { isPrimitive: true, inputString: 'date', + prestoInputString: 'date', }; /** @@ -105,6 +119,7 @@ export class Schema { public static readonly TIMESTAMP: Type = { isPrimitive: true, inputString: 'timestamp', + prestoInputString: 'timestamp', }; /** @@ -113,6 +128,7 @@ export class Schema { public static readonly STRING: Type = { isPrimitive: true, inputString: 'string', + prestoInputString: 'varchar', }; /** @@ -127,6 +143,7 @@ export class Schema { return { isPrimitive: true, inputString: scale !== undefined ? `decimal(${precision},${scale})` : `decimal(${precision})`, + prestoInputString: scale !== undefined ? `decimal(${precision},${scale})` : `decimal(${precision})`, }; } @@ -145,6 +162,7 @@ export class Schema { return { isPrimitive: true, inputString: `char(${length})`, + prestoInputString: `char(${length})`, }; } @@ -163,6 +181,7 @@ export class Schema { return { isPrimitive: true, inputString: `varchar(${length})`, + prestoInputString: `varchar(${length})`, }; } @@ -175,6 +194,7 @@ export class Schema { return { isPrimitive: false, inputString: `array<${itemType.inputString}>`, + prestoInputString: `array(${itemType.inputString})`, }; } @@ -191,6 +211,7 @@ export class Schema { return { isPrimitive: false, inputString: `map<${keyType.inputString},${valueType.inputString}>`, + prestoInputString: `map(${keyType.inputString},${valueType.inputString})`, }; } @@ -209,6 +230,7 @@ export class Schema { return `${column.name}:${column.type.inputString} COMMENT '${column.comment}'`; } }).join(',')}>`, + prestoInputString: `struct(${columns.map(column => `"${column.name}" ${column.type.inputString}`).join(',')}>`, }; } } diff --git a/packages/@aws-cdk/aws-glue/lib/table.ts b/packages/@aws-cdk/aws-glue/lib/table.ts index ea958879d816b..d9b6264d973b2 100644 --- a/packages/@aws-cdk/aws-glue/lib/table.ts +++ b/packages/@aws-cdk/aws-glue/lib/table.ts @@ -1,7 +1,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as s3 from '@aws-cdk/aws-s3'; -import { ArnFormat, Fn, IResource, Names, Resource, Stack } from '@aws-cdk/core'; +import { ArnFormat, Fn, IResource, Lazy, Names, Resource, Stack } from '@aws-cdk/core'; import * as cr from '@aws-cdk/custom-resources'; import { AwsCustomResource } from '@aws-cdk/custom-resources'; import { Construct } from 'constructs'; @@ -185,6 +185,8 @@ export interface TableProps { /** * A Glue table. + * + * @resource AWS::Glue::Table */ export class Table extends Resource implements ITable { @@ -558,7 +560,7 @@ const writePermissions = [ 'glue:UpdatePartition', ]; -function renderColumns(columns?: Array) { +function renderColumns(columns?: Array) { if (columns === undefined) { return undefined; } @@ -570,3 +572,145 @@ function renderColumns(columns?: Array) { }; }); } + +function renderPrestoColumns(columns: Array): string { + return JSON.stringify(columns.map(col => ({ + name: col.name, + type: col.type.prestoInputString, + }))); +} + +/** + * View Properties + */ +export interface ViewProps { + /** + * Name of the view + */ + readonly tableName: string; + + /** + * Description of the table. + * + * @default generated + */ + readonly description?: string; + + /** + * Database in which to store the view. + */ + readonly database: IDatabase; + + /** + * Columns of the view. + */ + readonly columns: Column[]; + + /** + * View SQL. + */ + readonly sql: string; +} + +/** + * A Glue View + * + * @resource AWS::Glue::Table + */ +export class View extends Resource implements ITable { + + /** + * Database this table belongs to. + */ + public readonly database: IDatabase; + + /** + * Name of the underlying glue table. + */ + public readonly tableName: string; + + /** + * ARN of the underlying glue table. + */ + public readonly tableArn: string; + + /** + * This table's columns. + */ + public readonly columns: Column[]; + + constructor(scope: Construct, id: string, props: ViewProps) { + super(scope, id, { + physicalName: props.tableName, + }); + + this.database = props.database; + this.columns = props.columns; + + const tableResource = new CfnTable(this, 'View', { + catalogId: props.database.catalogId, + + databaseName: props.database.databaseName, + + tableInput: { + name: this.physicalName, + description: props.description || `${props.tableName} generated by CDK`, + + partitionKeys: [], + + parameters: { + comment: 'Presto View', + presto_view: 'true', + }, + + storageDescriptor: { + location: '', + compressed: false, + columns: renderColumns(props.columns), + serdeInfo: {}, + }, + tableType: 'VIRTUAL_VIEW', + viewExpandedText: '/* Presto View */', + viewOriginalText: Lazy.string({ + produce: () => + Fn.sub('/* Presto View: ${PrestoBase64} */', { + PrestoBase64: Fn.base64( + Fn.sub('{"originalSql":${OriginalSql},"catalog":"${Catalog}","schema":"${Schema}","columns":${Columns}}', { + Catalog: 'awsdatacatalog', + Schema: props.database.databaseName, + OriginalSql: JSON.stringify(props.sql), + Columns: renderPrestoColumns(props.columns), + }), + ), + }), + }), + }, + }); + + this.tableName = this.getResourceNameAttribute(tableResource.ref); + this.tableArn = this.stack.formatArn({ + service: 'glue', + resource: 'table', + resourceName: `${this.database.databaseName}/${this.tableName}`, + }); + } + + /** + * Grant read permissions ti the View to an IAM principal + * @param grantee + */ + public grantRead(grantee: iam.IGrantable): iam.Grant { + return this.grant(grantee, readPermissions); + } + + /** + * Grant the given identity custom permissions. + */ + public grant(grantee: iam.IGrantable, actions: string[]) { + return iam.Grant.addToPrincipal({ + grantee, + resourceArns: [this.tableArn], + actions, + }); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-glue/test/integ.connection.js.snapshot/cdk.out b/packages/@aws-cdk/aws-glue/test/integ.connection.js.snapshot/cdk.out index 588d7b269d34f..8ecc185e9dbee 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.connection.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-glue/test/integ.connection.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/aws-cdk-glue.assets.json b/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/aws-cdk-glue.assets.json index 5381bea5e40ca..ecf5b6fdab5e6 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/aws-cdk-glue.assets.json +++ b/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/aws-cdk-glue.assets.json @@ -1,7 +1,7 @@ { - "version": "20.0.0", + "version": "21.0.0", "files": { - "eef5abdc0f1ee16e5be447f60688757df6726f3c2d1d06c136e9bbdb99d96e1f": { + "349027dd5d4861050937f4ea890519b645bd3fee009105c0af11a2afeacc8b83": { "source": { "path": "aws-cdk-glue.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "eef5abdc0f1ee16e5be447f60688757df6726f3c2d1d06c136e9bbdb99d96e1f.json", + "objectKey": "349027dd5d4861050937f4ea890519b645bd3fee009105c0af11a2afeacc8b83.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/aws-cdk-glue.template.json b/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/aws-cdk-glue.template.json index 92dfc8e76e2d5..80e37223def24 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/aws-cdk-glue.template.json +++ b/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/aws-cdk-glue.template.json @@ -493,6 +493,133 @@ } } }, + "SimpleView8C06BC7B": { + "Type": "AWS::Glue::Table", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": { + "Ref": "MyDatabase1E2517DB" + }, + "TableInput": { + "Description": "simple_view generated by CDK", + "Name": "simple_view", + "Parameters": { + "comment": "Presto View", + "presto_view": "true" + }, + "PartitionKeys": [], + "StorageDescriptor": { + "Columns": [ + { + "Name": "col1", + "Type": "string" + } + ], + "Compressed": false, + "Location": "", + "SerdeInfo": {} + }, + "TableType": "VIRTUAL_VIEW", + "ViewExpandedText": "/* Presto View */", + "ViewOriginalText": { + "Fn::Sub": [ + "/* Presto View: ${PrestoBase64} */", + { + "PrestoBase64": { + "Fn::Base64": { + "Fn::Sub": [ + "{\"originalSql\":${OriginalSql},\"catalog\":\"${Catalog}\",\"schema\":\"${Schema}\",\"columns\":${Columns}}", + { + "Catalog": "awsdatacatalog", + "Schema": { + "Ref": "MyDatabase1E2517DB" + }, + "OriginalSql": "\"select \\\"value1\\\" as col1\"", + "Columns": "[{\"name\":\"col1\",\"type\":\"varchar\"}]" + } + ] + } + } + } + ] + } + } + } + }, + "ViewWithComplexTypeView42CAEEBF": { + "Type": "AWS::Glue::Table", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": { + "Ref": "MyDatabase1E2517DB" + }, + "TableInput": { + "Description": "complex_view generated by CDK", + "Name": "complex_view", + "Parameters": { + "comment": "Presto View", + "presto_view": "true" + }, + "PartitionKeys": [], + "StorageDescriptor": { + "Columns": [ + { + "Name": "col1", + "Type": "string" + }, + { + "Comment": "col2 comment", + "Name": "col2", + "Type": "string" + }, + { + "Name": "col3", + "Type": "array" + }, + { + "Name": "col4", + "Type": "map" + }, + { + "Name": "col5", + "Type": "struct" + } + ], + "Compressed": false, + "Location": "", + "SerdeInfo": {} + }, + "TableType": "VIRTUAL_VIEW", + "ViewExpandedText": "/* Presto View */", + "ViewOriginalText": { + "Fn::Sub": [ + "/* Presto View: ${PrestoBase64} */", + { + "PrestoBase64": { + "Fn::Base64": { + "Fn::Sub": [ + "{\"originalSql\":${OriginalSql},\"catalog\":\"${Catalog}\",\"schema\":\"${Schema}\",\"columns\":${Columns}}", + { + "Catalog": "awsdatacatalog", + "Schema": { + "Ref": "MyDatabase1E2517DB" + }, + "OriginalSql": "\"select \\\"v1\\\" as col1, \\\"v2\\\" as col2, array(\\\"v3.1\\\",\\\"v3.2\\\") as col3\"", + "Columns": "[{\"name\":\"col1\",\"type\":\"varchar\"},{\"name\":\"col2\",\"type\":\"varchar\"},{\"name\":\"col3\",\"type\":\"array(string)\"},{\"name\":\"col4\",\"type\":\"map(string,string)\"},{\"name\":\"col5\",\"type\":\"struct(\\\"col1\\\" string>\"}]" + } + ] + } + } + } + ] + } + } + } + }, "MyUserDC45028B": { "Type": "AWS::IAM::User" }, @@ -802,6 +929,74 @@ ] } ] + }, + { + "Action": [ + "glue:BatchGetPartition", + "glue:GetPartition", + "glue:GetPartitions", + "glue:GetTable", + "glue:GetTableVersion", + "glue:GetTableVersions", + "glue:GetTables" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":glue:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":table/", + { + "Ref": "MyDatabase1E2517DB" + }, + "/", + { + "Ref": "SimpleView8C06BC7B" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":glue:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":table/", + { + "Ref": "MyDatabase1E2517DB" + }, + "/", + { + "Ref": "ViewWithComplexTypeView42CAEEBF" + } + ] + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/integ.json b/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/integ.json index 1f604630bc610..1c9ba89d58333 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "testCases": { "integ.table": { "stacks": [ diff --git a/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/manifest.json b/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/manifest.json index 931c2276f10a3..d72c6ffb3b221 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -23,7 +23,7 @@ "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}/eef5abdc0f1ee16e5be447f60688757df6726f3c2d1d06c136e9bbdb99d96e1f.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/349027dd5d4861050937f4ea890519b645bd3fee009105c0af11a2afeacc8b83.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -105,6 +105,18 @@ "data": "MyPartitionFilteredTable324BA27A" } ], + "/aws-cdk-glue/SimpleView/View": [ + { + "type": "aws:cdk:logicalId", + "data": "SimpleView8C06BC7B" + } + ], + "/aws-cdk-glue/ViewWithComplexType/View": [ + { + "type": "aws:cdk:logicalId", + "data": "ViewWithComplexTypeView42CAEEBF" + } + ], "/aws-cdk-glue/MyUser/Resource": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/tree.json b/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/tree.json index 441cf0ac8901f..ef7cd6a5d3a7c 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-glue/test/integ.table.js.snapshot/tree.json @@ -9,7 +9,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.108" } }, "aws-cdk-glue": { @@ -701,6 +701,169 @@ "version": "0.0.0" } }, + "SimpleView": { + "id": "SimpleView", + "path": "aws-cdk-glue/SimpleView", + "children": { + "View": { + "id": "View", + "path": "aws-cdk-glue/SimpleView/View", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Glue::Table", + "aws:cdk:cloudformation:props": { + "catalogId": { + "Ref": "AWS::AccountId" + }, + "databaseName": { + "Ref": "MyDatabase1E2517DB" + }, + "tableInput": { + "name": "simple_view", + "description": "simple_view generated by CDK", + "partitionKeys": [], + "parameters": { + "comment": "Presto View", + "presto_view": "true" + }, + "storageDescriptor": { + "location": "", + "compressed": false, + "columns": [ + { + "name": "col1", + "type": "string" + } + ], + "serdeInfo": {} + }, + "tableType": "VIRTUAL_VIEW", + "viewExpandedText": "/* Presto View */", + "viewOriginalText": { + "Fn::Sub": [ + "/* Presto View: ${PrestoBase64} */", + { + "PrestoBase64": { + "Fn::Base64": { + "Fn::Sub": [ + "{\"originalSql\":${OriginalSql},\"catalog\":\"${Catalog}\",\"schema\":\"${Schema}\",\"columns\":${Columns}}", + { + "Catalog": "awsdatacatalog", + "Schema": { + "Ref": "MyDatabase1E2517DB" + }, + "OriginalSql": "\"select \\\"value1\\\" as col1\"", + "Columns": "[{\"name\":\"col1\",\"type\":\"varchar\"}]" + } + ] + } + } + } + ] + } + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-glue.CfnTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-glue.View", + "version": "0.0.0" + } + }, + "ViewWithComplexType": { + "id": "ViewWithComplexType", + "path": "aws-cdk-glue/ViewWithComplexType", + "children": { + "View": { + "id": "View", + "path": "aws-cdk-glue/ViewWithComplexType/View", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Glue::Table", + "aws:cdk:cloudformation:props": { + "catalogId": { + "Ref": "AWS::AccountId" + }, + "databaseName": { + "Ref": "MyDatabase1E2517DB" + }, + "tableInput": { + "name": "complex_view", + "description": "complex_view generated by CDK", + "partitionKeys": [], + "parameters": { + "comment": "Presto View", + "presto_view": "true" + }, + "storageDescriptor": { + "location": "", + "compressed": false, + "columns": [ + { + "name": "col1", + "type": "string" + }, + { + "name": "col2", + "type": "string", + "comment": "col2 comment" + }, + { + "name": "col3", + "type": "array" + }, + { + "name": "col4", + "type": "map" + }, + { + "name": "col5", + "type": "struct" + } + ], + "serdeInfo": {} + }, + "tableType": "VIRTUAL_VIEW", + "viewExpandedText": "/* Presto View */", + "viewOriginalText": { + "Fn::Sub": [ + "/* Presto View: ${PrestoBase64} */", + { + "PrestoBase64": { + "Fn::Base64": { + "Fn::Sub": [ + "{\"originalSql\":${OriginalSql},\"catalog\":\"${Catalog}\",\"schema\":\"${Schema}\",\"columns\":${Columns}}", + { + "Catalog": "awsdatacatalog", + "Schema": { + "Ref": "MyDatabase1E2517DB" + }, + "OriginalSql": "\"select \\\"v1\\\" as col1, \\\"v2\\\" as col2, array(\\\"v3.1\\\",\\\"v3.2\\\") as col3\"", + "Columns": "[{\"name\":\"col1\",\"type\":\"varchar\"},{\"name\":\"col2\",\"type\":\"varchar\"},{\"name\":\"col3\",\"type\":\"array(string)\"},{\"name\":\"col4\",\"type\":\"map(string,string)\"},{\"name\":\"col5\",\"type\":\"struct(\\\"col1\\\" string>\"}]" + } + ] + } + } + } + ] + } + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-glue.CfnTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-glue.View", + "version": "0.0.0" + } + }, "MyUser": { "id": "MyUser", "path": "aws-cdk-glue/MyUser", @@ -1067,6 +1230,74 @@ ] } ] + }, + { + "Action": [ + "glue:BatchGetPartition", + "glue:GetPartition", + "glue:GetPartitions", + "glue:GetTable", + "glue:GetTableVersion", + "glue:GetTableVersions", + "glue:GetTables" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":glue:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":table/", + { + "Ref": "MyDatabase1E2517DB" + }, + "/", + { + "Ref": "SimpleView8C06BC7B" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":glue:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":table/", + { + "Ref": "MyDatabase1E2517DB" + }, + "/", + { + "Ref": "ViewWithComplexTypeView42CAEEBF" + } + ] + ] + } + ] } ], "Version": "2012-10-17" @@ -1098,14 +1329,14 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-glue/test/integ.table.ts b/packages/@aws-cdk/aws-glue/test/integ.table.ts index d9d543a124d16..a14b5c5dd2fa8 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.table.ts +++ b/packages/@aws-cdk/aws-glue/test/integ.table.ts @@ -95,6 +95,23 @@ new glue.Table(stack, 'MyPartitionFilteredTable', { enablePartitionFiltering: true, }); +const myView = new glue.View(stack, 'SimpleView', { + database, + tableName: 'simple_view', + columns: [{ + name: 'col1', + type: glue.Schema.STRING, + }], + sql: 'select "value1" as col1', +}); + +const myViewWithMoreComplexType = new glue.View(stack, 'ViewWithComplexType', { + database, + tableName: 'complex_view', + columns, + sql: 'select "v1" as col1, "v2" as col2, array("v3.1","v3.2") as col3', +}); + const user = new iam.User(stack, 'MyUser'); csvTable.grantReadWrite(user); encryptedTable.grantReadWrite(user); @@ -103,5 +120,8 @@ const anotherUser = new iam.User(stack, 'AnotherUser'); avroTable.grantReadWrite(anotherUser); jsonTable.grantReadWrite(anotherUser); parquetTable.grantReadWrite(anotherUser); +myView.grantRead(anotherUser); +myViewWithMoreComplexType.grantRead(anotherUser); + app.synth(); diff --git a/packages/@aws-cdk/aws-glue/test/table.test.ts b/packages/@aws-cdk/aws-glue/test/table.test.ts index 152c489dd520c..2a36899a993e1 100644 --- a/packages/@aws-cdk/aws-glue/test/table.test.ts +++ b/packages/@aws-cdk/aws-glue/test/table.test.ts @@ -7,6 +7,78 @@ import * as glue from '../lib'; import { PartitionIndex } from '../lib'; import { CfnTable } from '../lib/glue.generated'; +test('glue View', ()=> { + const app = new cdk.App(); + const dbStack = new cdk.Stack(app, 'db'); + const database = new glue.Database(dbStack, 'Database', { + databaseName: 'database', + }); + + const viewStack = new cdk.Stack(app, 'view'); + new glue.View(viewStack, 'View', { + database, + tableName: 'view', + columns: [{ + name: 'col', + type: glue.Schema.STRING, + }], + sql: 'select "1" as col', + }); + + Template.fromStack(viewStack).hasResourceProperties('AWS::Glue::Table', { + CatalogId: { + Ref: 'AWS::AccountId', + }, + DatabaseName: { + 'Fn::ImportValue': 'db:ExportsOutputRefDatabaseB269D8BB88F4B1C4', + }, + TableInput: { + Name: 'view', + Description: 'view generated by CDK', + Parameters: { + comment: 'Presto View', + presto_view: 'true', + }, + StorageDescriptor: { + Columns: [ + { + Name: 'col', + Type: 'string', + }, + ], + Compressed: false, + Location: '', + SerdeInfo: {}, + }, + TableType: 'VIRTUAL_VIEW', + ViewExpandedText: '/* Presto View */', + ViewOriginalText: { + 'Fn::Sub': [ + '/* Presto View: ${PrestoBase64} */', + { + PrestoBase64: { + 'Fn::Base64': { + 'Fn::Sub': [ + '{"originalSql":${OriginalSql},"catalog":"${Catalog}","schema":"${Schema}","columns":${Columns}}', + { + Catalog: 'awsdatacatalog', + Schema: { + 'Fn::ImportValue': 'db:ExportsOutputRefDatabaseB269D8BB88F4B1C4', + }, + OriginalSql: JSON.stringify('select "1" as col'), + Columns: '[{"name":"col","type":"varchar"}]', + }, + ], + }, + }, + }, + ], + }, + }, + }); + +}); + test('unpartitioned JSON table', () => { const app = new cdk.App(); const dbStack = new cdk.Stack(app, 'db');