From 3660ca113bd7c2f6f2e8ae28a1a88e86f3a3383c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Paw=C5=82aszek?= Date: Sun, 7 Jan 2024 14:32:51 +0100 Subject: [PATCH 1/3] fix: removing TS dict as it is JSII-unfriendly --- API.md | 54 +++++++++++++++++------- src/core/tasks/filters.ts | 78 +++++++++++++++++------------------ src/core/tasks/mappings.ts | 35 ++++++++-------- src/core/tasks/tasks.ts | 12 +++--- src/core/tasks/transforms.ts | 29 +++++++------ src/core/tasks/validations.ts | 4 +- 6 files changed, 118 insertions(+), 94 deletions(-) diff --git a/API.md b/API.md index 5e13b823..57576076 100644 --- a/API.md +++ b/API.md @@ -11407,18 +11407,44 @@ public readonly type: ConnectorType; --- -### TaskProperties +### TaskProperty -A generic bucket for the task properties. +#### Initializer -#### Initializer +```typescript +import { TaskProperty } from '@cdklabs/cdk-appflow' + +const taskProperty: TaskProperty = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| key | string | *No description.* | +| value | string | *No description.* | + +--- + +##### `key`Required ```typescript -import { TaskProperties } from '@cdklabs/cdk-appflow' +public readonly key: string; +``` + +- *Type:* string + +--- -const taskProperties: TaskProperties = { ... } +##### `value`Required + +```typescript +public readonly value: string; ``` +- *Type:* string + +--- ### TriggerConfig @@ -12187,14 +12213,14 @@ A representation of a filter operation condtiion on a source record field. ```typescript import { FilterCondition } from '@cdklabs/cdk-appflow' -new FilterCondition(field: Field, filter: string, properties: TaskProperties) +new FilterCondition(field: Field, filter: string, properties: TaskProperty[]) ``` | **Name** | **Type** | **Description** | | --- | --- | --- | | field | Field | *No description.* | | filter | string | *No description.* | -| properties | TaskProperties | *No description.* | +| properties | TaskProperty[] | *No description.* | --- @@ -12212,7 +12238,7 @@ new FilterCondition(field: Field, filter: string, properties: TaskProperties) ##### `properties`Required -- *Type:* TaskProperties +- *Type:* TaskProperty[] --- @@ -12644,7 +12670,7 @@ FilterCondition.timestampNotEquals(field: Field, val: Date | Date[]) | --- | --- | --- | | field | Field | *No description.* | | filter | string | *No description.* | -| properties | TaskProperties | *No description.* | +| properties | TaskProperty[] | *No description.* | --- @@ -12671,10 +12697,10 @@ public readonly filter: string; ##### `properties`Required ```typescript -public readonly properties: TaskProperties; +public readonly properties: TaskProperty[]; ``` -- *Type:* TaskProperties +- *Type:* TaskProperty[] --- @@ -14327,7 +14353,7 @@ A representation of a unitary action on the record fields. ```typescript import { Task } from '@cdklabs/cdk-appflow' -new Task(type: string, sourceFields: string[], connectorOperator: TaskConnectorOperator, properties: TaskProperties, destinationField?: string) +new Task(type: string, sourceFields: string[], connectorOperator: TaskConnectorOperator, properties: TaskProperty[], destinationField?: string) ``` | **Name** | **Type** | **Description** | @@ -14335,7 +14361,7 @@ new Task(type: string, sourceFields: string[], connectorOperator: TaskConnectorO | type | string | *No description.* | | sourceFields | string[] | *No description.* | | connectorOperator | TaskConnectorOperator | *No description.* | -| properties | TaskProperties | *No description.* | +| properties | TaskProperty[] | *No description.* | | destinationField | string | *No description.* | --- @@ -14360,7 +14386,7 @@ new Task(type: string, sourceFields: string[], connectorOperator: TaskConnectorO ##### `properties`Required -- *Type:* TaskProperties +- *Type:* TaskProperty[] --- diff --git a/src/core/tasks/filters.ts b/src/core/tasks/filters.ts index 69638b5a..3ece528c 100644 --- a/src/core/tasks/filters.ts +++ b/src/core/tasks/filters.ts @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 */ import { Field } from './field'; import { IOperation, OperationBase } from './operation'; -import { Task, TaskProperties } from './tasks'; +import { Task, TaskProperty } from './tasks'; /** * A representation of a filter operation condtiion on a source record field @@ -101,71 +101,71 @@ export class FilterCondition { } public static timestampBetween(field: Field, lower: Date, upper: Date) { - return new FilterCondition(field, 'BETWEEN', { - DATA_TYPE: field.dataType!, - LOWER_BOUND: this.valueToString(lower), - UPPER_BOUND: this.valueToString(upper), - }); + return new FilterCondition(field, 'BETWEEN', [ + { key: 'DATA_TYPE', value: field.dataType! }, + { key: 'LOWER_BOUND', value: this.valueToString(lower) }, + { key: 'UPPER_BOUND', value: this.valueToString(upper) }, + ] ); } private static stringCondition(field: Field, val: string | string[], filter: string) { if (Array.isArray(val)) { - return new FilterCondition(field, filter, { - DATA_TYPE: field.dataType!, - VALUES: val.map(m => this.valueToString(m)).join(','), - }); + return new FilterCondition(field, filter, [ + { key: 'DATA_TYPE', value: field.dataType! }, + { key: 'VALUES', value: val.map(m => this.valueToString(m)).join(',') }, + ]); } - return new FilterCondition(field, filter, { - DATA_TYPE: field.dataType!, - VALUE: this.valueToString(val), - }); + return new FilterCondition(field, filter, [ + { key: 'DATA_TYPE', value: field.dataType! }, + { key: 'VALUE', value: this.valueToString(val) }, + ]); } private static numberCondition(field: Field, val: number | number[], filter: string) { if (Array.isArray(val)) { - return new FilterCondition(field, filter, { - DATA_TYPE: field.dataType!, - VALUES: val.map(m => this.valueToString(m)).join(','), - }); + return new FilterCondition(field, filter, [ + { key: 'DATA_TYPE', value: field.dataType! }, + { key: 'VALUES', value: val.map(m => this.valueToString(m)).join(',') }, + ]); } - return new FilterCondition(field, filter, { - DATA_TYPE: field.dataType!, - VALUE: this.valueToString(val), - }); + return new FilterCondition(field, filter, [ + { key: 'DATA_TYPE', value: field.dataType! }, + { key: 'VALUE', value: this.valueToString(val) }, + ]); } private static booleanCondition(field: Field, val: boolean | boolean[], filter: string) { if (Array.isArray(val)) { - return new FilterCondition(field, filter, { - DATA_TYPE: field.dataType!, - VALUES: val.map(m => this.valueToString(m)).join(','), - }); + return new FilterCondition(field, filter, [ + { key: 'DATA_TYPE', value: field.dataType! }, + { key: 'VALUES', value: val.map(m => this.valueToString(m)).join(',') }, + ]); } - return new FilterCondition(field, filter, { - DATA_TYPE: field.dataType!, - VALUE: this.valueToString(val), - }); + return new FilterCondition(field, filter, [ + { key: 'DATA_TYPE', value: field.dataType! }, + { key: 'VALUE', value: this.valueToString(val) }, + ]); } private static timestampCondition(field: Field, val: Date | Date[], filter: string) { if (Array.isArray(val)) { - return new FilterCondition(field, filter, { - DATA_TYPE: field.dataType!, - VALUES: val.map(m => this.valueToString(m)).join(','), - }); + return new FilterCondition(field, filter, [ + { key: 'DATA_TYPE', value: field.dataType! }, + { key: 'VALUES', value: val.map(m => this.valueToString(m)).join(',') }, + ]); } - return new FilterCondition(field, filter, { - DATA_TYPE: field.dataType!, - VALUE: this.valueToString(val), - }); + return new FilterCondition(field, filter, [ + { key: 'DATA_TYPE', value: field.dataType! }, + { key: 'VALUE', value: this.valueToString(val) }, + ]); } private static valueToString(value: string | number | boolean | Date): string { @@ -185,7 +185,7 @@ export class FilterCondition { constructor( public readonly field: Field, public readonly filter: string, - public readonly properties: TaskProperties) { + public readonly properties: TaskProperty[]) { if (!field.dataType) { throw new Error('field dataType required'); } diff --git a/src/core/tasks/mappings.ts b/src/core/tasks/mappings.ts index b4e8d921..260ae396 100644 --- a/src/core/tasks/mappings.ts +++ b/src/core/tasks/mappings.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: Apache-2.0 import { Fn } from 'aws-cdk-lib'; import { Field } from './field'; import { IOperation, OperationBase } from './operation'; -import { Task, TaskProperties } from './tasks'; +import { Task, TaskProperty } from './tasks'; const TT_MAPALL = 'Map_all'; const TT_MAP = 'Map'; @@ -43,18 +43,19 @@ export class Mapping extends OperationBase implements IMapping { new Task( TT_MAPALL, [], { operation: OP_NOOP }, - { EXCLUDE_SOURCE_FIELDS_LIST: config ? `["${Fn.join('","', config.exclude)}"]` : '[]' }), + [{ key: 'EXCLUDE_SOURCE_FIELDS_LIST', value: config ? `["${Fn.join('","', config.exclude)}"]` : '[]' }], + ), ]); } public static map(from: Field, to: Field): IMapping { - const props: TaskProperties = {}; + const props: TaskProperty[] = []; if (from.dataType) { - props[TP_SOURCE_DATA_TYPE] = from.dataType; + props.push({ key: TP_SOURCE_DATA_TYPE, value: from.dataType }); } if (to.dataType) { - props[TP_DESTINATION_DATA_TYPE] = to.dataType; + props.push({ key: TP_DESTINATION_DATA_TYPE, value: to.dataType }); } return new Mapping([ @@ -78,11 +79,11 @@ export class Mapping extends OperationBase implements IMapping { const tmpField = from.map(f => f.name).join(','); return new Mapping([ new Task(TT_MERGE, from.map(f => f.name), { operation: OP_NOOP }, - { CONCAT_FORMAT: format }, tmpField), - new Task(TT_MAP, [tmpField], { operation: OP_NOOP }, { - DESTINATION_DATA_TYPE: to.dataType, - SOURCE_DATA_TYPE: 'string', - }), + [{ key: 'CONCAT_FORMAT', value: format }], tmpField), + new Task(TT_MAP, [tmpField], { operation: OP_NOOP }, [ + { key: 'DESTINATION_DATA_TYPE', value: to.dataType }, + { key: 'SOURCE_DATA_TYPE', value: 'string' }, + ]), ]); } @@ -145,13 +146,13 @@ export class Mapping extends OperationBase implements IMapping { const tmpField = `${sourceField1.name},${sourceField2.name}`; return new Mapping([ - new Task(TT_ARITHMETIC, [sourceField1.name, sourceField2.name], { operation: operation }, { - MATH_OPERATION_FIELDS_ORDER: tmpField, - }, tmpField), - new Task(TT_MAP, [tmpField], { operation: OP_NOOP }, { - DESTINATION_DATA_TYPE: to.dataType, - SOURCE_DATA_TYPE: 'string', - }, + new Task(TT_ARITHMETIC, [sourceField1.name, sourceField2.name], { operation: operation }, [{ + key: 'MATH_OPERATION_FIELDS_ORDER', value: tmpField, + }], tmpField), + new Task(TT_MAP, [tmpField], { operation: OP_NOOP }, [ + { key: 'DESTINATION_DATA_TYPE', value: to.dataType }, + { key: 'SOURCE_DATA_TYPE', value: 'string' }, + ], to.name), ]); } diff --git a/src/core/tasks/tasks.ts b/src/core/tasks/tasks.ts index 75bae134..1be5b52e 100644 --- a/src/core/tasks/tasks.ts +++ b/src/core/tasks/tasks.ts @@ -14,11 +14,9 @@ export interface ITask { bind(flow: IFlow, source: ISource): CfnFlow.TaskProperty; } -/** - * A generic bucket for the task properties - */ -export interface TaskProperties { - [key: string]: string; +export interface TaskProperty { + readonly key: string; + readonly value: string; } /** @@ -36,7 +34,7 @@ export class Task implements ITask { constructor(protected type: string, protected sourceFields: string[], protected connectorOperator: TaskConnectorOperator, - protected properties: TaskProperties, + protected properties: TaskProperty[], protected destinationField?: string) { } public bind(_flow: IFlow, source: ISource): CfnFlow.TaskProperty { @@ -44,7 +42,7 @@ export class Task implements ITask { return { taskType: this.type, sourceFields: this.sourceFields, - taskProperties: Object.entries(this.properties).map(([key, value]) => ({ key: key, value: value })), + taskProperties: this.properties.map(({ key, value }) => ({ key: key, value: value })), connectorOperator: this.buildOperatorFor(source), destinationField: this.destinationField, }; diff --git a/src/core/tasks/transforms.ts b/src/core/tasks/transforms.ts index 2dded3f8..5ddc0978 100644 --- a/src/core/tasks/transforms.ts +++ b/src/core/tasks/transforms.ts @@ -39,9 +39,8 @@ export class Transform extends OperationBase implements ITransform { 'Truncate', [typeof field === 'string' ? field : field.name], { operation: OP_NOOP }, - { TRUNCATE_LENGTH: `${length}` }), - ], - ); + [{ key: 'TRUNCATE_LENGTH', value: `${length}` }]), + ]); } /** @@ -60,11 +59,11 @@ export class Transform extends OperationBase implements ITransform { 'Mask', [typeof field === 'string' ? field : field.name], { operation: 'MASK_ALL' }, - { - // TODO: test this. The AWS Console generated transform has length, but what for? - MASK_LENGTH: '5', - MASK_VALUE: mask ?? '*', + [{ + // TODO: test this. The AWS Console generated transform has length, but what for? + key: 'MASK_LENGTH', value: '5', }, + { key: 'MASK_VALUE', value: mask ?? '*' }], ), ]); } @@ -87,10 +86,10 @@ export class Transform extends OperationBase implements ITransform { 'Mask', [typeof field === 'string' ? field : field.name], { operation: 'MASK_FIRST_N' }, - { - MASK_LENGTH: `${length}`, - MASK_VALUE: mask ?? '*', - }, + [ + { key: 'MASK_LENGTH', value: `${length}` }, + { key: 'MASK_VALUE', value: mask ?? '*' }, + ], ), ]); } @@ -114,10 +113,10 @@ export class Transform extends OperationBase implements ITransform { 'Mask', [typeof field === 'string' ? field : field.name], { operation: 'MASK_LAST_N' }, - { - MASK_LENGTH: `${length}`, - MASK_VALUE: mask ?? '*', - }, + [ + { key: 'MASK_LENGTH', value: `${length}` }, + { key: 'MASK_VALUE', value: mask ?? '*' }, + ], ), ]); } diff --git a/src/core/tasks/validations.ts b/src/core/tasks/validations.ts index 8748608a..18a321a3 100644 --- a/src/core/tasks/validations.ts +++ b/src/core/tasks/validations.ts @@ -86,7 +86,7 @@ export class Validation extends OperationBase implements IValidation { * @param action a @see ValidationAction for the validation * @returns a Validation instance */ - public static when(condition: ValidationCondition, action: ValidationAction) : IValidation { + public static when(condition: ValidationCondition, action: ValidationAction): IValidation { return new Validation(condition, action); } @@ -96,6 +96,6 @@ export class Validation extends OperationBase implements IValidation { super([new Task('Validate', [condition.field], { operation: condition.validation }, - { VALIDATION_ACTION: action.action })]); + [{ key: 'VALIDATION_ACTION', value: action.action }])]); } } From 0c03151531e5e1f11be478990be5942fc46215d2 Mon Sep 17 00:00:00 2001 From: kozub Date: Mon, 8 Jan 2024 12:18:05 +0100 Subject: [PATCH 2/3] Update jsii dependency versions --- API.md | 257 --------------------------------------------------- package.json | 4 +- yarn.lock | 85 ++++++----------- 3 files changed, 28 insertions(+), 318 deletions(-) diff --git a/API.md b/API.md index 57576076..ad490d07 100644 --- a/API.md +++ b/API.md @@ -1,260 +1,3 @@ -# Amazon AppFlow Construct Library - -*Note:* this library is currently in technical preview. - -## Introduction - -Amazon AppFlow is a service that enables creating managed, bi-directional data transfer integrations between various SaaS applications and AWS services. - -For more information, see the [Amazon AppFlow User Guide](https://docs.aws.amazon.com/appflow/latest/userguide/what-is-appflow.html). - -## Example - -```ts -import { SecretValue } from 'aws-cdk-lib'; -import { Bucket } from 'aws-cdk-lib/aws-s3'; -import { ISecret } from 'aws-cdk-lib/aws-secretsmanager'; -import { - ISource, - IDestination, - Filter, - FilterCondition, - Mapping, - OnDemandFlow, - S3Destination, - SalesforceConnectorProfile, - SalesforceSource, - Transform, - Validation, - ValidationAction, - ValidationCondition, -} from '@cdklabs/cdk-appflow'; - -declare const clientSecret: ISecret; -declare const accessToken: SecretValue; -declare const refreshToken: SecretValue; -declare const instanceUrl: string; - -const profile = new SalesforceConnectorProfile(this, 'MyConnectorProfile', { - oAuth: { - accessToken: accessToken, - flow: { - refreshTokenGrant: { - refreshToken: refreshToken, - client: clientSecret, - }, - }, - }, - instanceUrl: instanceUrl, - isSandbox: false, -}); - -const source = new SalesforceSource({ - profile: profile, - object: 'Account', -}); - -const bucket = new Bucket(this, 'DestinationBucket'); - -const destination = new S3Destination({ - location: { bucket }, -}); - -new OnDemandFlow(this, 'SfAccountToS3', { - source: source, - destination: destination, - mappings: [Mapping.mapAll()], - transforms: [ - Transform.mask({ name: 'Name' }, '*'), - ], - validations: [ - Validation.when(ValidationCondition.isNull('Name'), ValidationAction.ignoreRecord()), - ], - filters: [ - Filter.when(FilterCondition.timestampLessThanEquals({ name: 'LastModifiedDate', dataType: 'datetime' }, new Date(Date.parse('2022-02-02')))), - ], -}); - -``` -# Concepts - -Amazon AppFlow introduces several concepts that abstract away the technicalities of setting up and managing data integrations. - -An `Application` is any SaaS data integration component that can be either a *source* or a *destination* for Amazon AppFlow. A source is an application from which Amazon AppFlow will retrieve data, whereas a destination is an application to which Amazon AppFlow will send data. - -A `Flow` is Amazon AppFlow's integration between a source and a destination. - -A `ConnectorProfile` is Amazon AppFlow's abstraction over authentication/authorization with a particular SaaS application. The per-SaaS application permissions given to a particular `ConnectorProfile` will determine whether the connector profile can support the application as a source or as a destination (see whether a particular application is supported as either a source or a destination in [the documentation](https://docs.aws.amazon.com/appflow/latest/userguide/app-specific.html)). - -## Types of Flows - -The library introduces three, separate types of flows: - -- `OnDemandFlow` - a construct representing a flow that can be triggered programmatically with the use of a [StartFlow API call](https://docs.aws.amazon.com/appflow/1.0/APIReference/API_StartFlow.html). - -- `OnEventFlow` - a construct representing a flow that is triggered by a SaaS application event published to AppFlow. At the time of writing only a Salesforce source is able to publish events that can be consumed by AppFlow flows. - -- `OnScheduleFlow` - a construct representing a flow that is triggered on a [`Schedule`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_events.Schedule.html) - -## Tasks - -Tasks are steps that can be taken upon fields. Tasks compose higher level objects that in this library are named `Operations`. There are four operations identified: - -- Transforms - 1-1 transforms on source fields, like truncation or masking - -- Mappings - 1-1 or many-to-1 operations from source fields to a destination field - -- Filters - operations that limit the source data on a particular conditions - -- Validations - operations that work on a per-record level and can have either a record-level consequence (i.e. dropping the record) or a global one (terminating the flow). - -Each flow exposes dedicated properties to each of the operation types that one can use like in the example below: - -```ts -import { - Filter, - FilterCondition, - IDestination, - ISource, - Mapping, - OnDemandFlow, - S3Destination, - SalesforceConnectorProfile, - SalesforceSource, - Transform, - Validation, - ValidationAction, - ValidationCondition, -} from '@cdklabs/cdk-appflow'; - -declare const stack: Stack; -declare const source: ISource; -declare const destination: IDestination; - -const flow = new OnDemandFlow(stack, 'OnDemandFlow', { - source: source, - destination: destination, - transforms: [ - Transform.mask({ name: 'Name' }, '*'), - ], - mappings: [ - Mapping.map({ name: 'Name', dataType: 'String' }, { name: 'Name', dataType: 'string' }), - ], - filters: [ - Filter.when(FilterCondition.timestampLessThanEquals({ name: 'LastModifiedDate', dataType: 'datetime' }, new Date(Date.parse('2022-02-02')))), - ], - validations: [ - Validation.when(ValidationCondition.isNull('Name'), ValidationAction.ignoreRecord()), - ] -}); -``` - -## Monitoring - - -### Metrcis - -Each flow allows to access metrics through the methods: -- `metricFlowExecutionsStarted` -- `metricFlowExecutionsFailed` -- `metricFlowExecutionsSucceeded` -- `metricFlowExecutionTime` -- `metricFlowExecutionRecordsProcessed` - - -For detailed information about AppFlow metrics refer to [the documentation](https://docs.aws.amazon.com/appflow/latest/userguide/monitoring-cloudwatch.html). - -It can be consume by CloudWatch alert using as in the example below: - - -```ts -import { IFlow } from '@cdklabs/cdk-appflow'; - -declare const flow: IFlow; -declare const stack: Stack; - -const metric = flow.metricFlowExecutionsStarted(); - -metric.createAlarm(stack, "FlowExecutionsStartedAlarm", { - threshold: 1000, - evaluationPeriods: 2 -}); -``` - - -### EventBridge notifications - -Each flow publishes events to the default EventBridge bus: - -- `onRunStarted` -- `onRunCompleted` -- `onDeactivated` (only for the `OnEventFlow` and the `OnScheduleFlow`) -- `onStatus` (only for the `OnEventFlow` ) - -This way one can consume the notifications as in the example below: - -```ts -import { ITopic } from 'aws-cdk-lib/aws-sns'; -import { SnsTopic } from 'aws-cdk-lib/aws-events-targets'; -import { IFlow } from '@cdklabs/cdk-appflow'; - -declare const flow: IFlow; -declare const myTopic: ITopic; - -flow.onRunCompleted('OnRunCompleted', { - target: new SnsTopic(myTopic), -}); -``` - -# Notable distinctions from CloudFormation specification - -## `OnScheduleFlow` and `incrementalPullConfig` - -In CloudFormation the definition of the `incrementalPullConfig` (which effectively gives a name of the field used for tracking the last pulled timestamp) is on the [`SourceFlowConfig`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-sourceflowconfig.html#cfn-appflow-flow-sourceflowconfig-incrementalpullconfig) property. In the library this has been moved to the `OnScheduleFlow` constructor properties. - -## `S3Destination` and Glue Catalog - -Although in CloudFormation the Glue Catalog configuration is settable on the flow level - it works only when the destination is S3. That is why the library shifts the Glue Catalog properties definition to the `S3Destination`, which in turn requires using Lazy for populating `metadataCatalogConfig` in the flow. - -# Security considerations - -It is *recommended* to follow [data protection mechanisms for Amazon AppFlow](https://docs.aws.amazon.com/appflow/latest/userguide/data-protection.html). - -## Confidential information - -Amazon AppFlow application integration is done using `ConnectionProfiles`. A `ConnectionProfile` requires providing sensitive information in the form of e.g. access and refresh tokens. It is *recommended* that such information is stored securely and passed to AWS CDK securely. All sensitive fields are effectively `IResolvable` and this means they can be resolved at deploy time. With that one should follow the [best practices for credentials with CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/security-best-practices.html#creds). In this library, the sensitive fields are typed as `SecretValue` to emphasize these should not be plain strings. - -An example of using a predefined AWS Secrets Manager secret for storing sensitive information can be found below: - -```ts -import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; -import { GoogleAnalytics4ConnectorProfile } from '@cdklabs/cdk-appflow'; - -declare const stack: Stack; - -const secret = Secret.fromSecretNameV2(stack, 'GA4Secret', 'appflow/ga4'); - -const profile = new GoogleAnalytics4ConnectorProfile(stack, 'GA4Connector', { - oAuth: { - flow: { - refreshTokenGrant: { - refreshToken: secret.secretValueFromJson('refreshToken'), - clientId: secret.secretValueFromJson('clientId'), - clientSecret: secret.secretValueFromJson('clientSecret'), - }, - }, - }, -}); - -``` - -## An approach to managing permissions - -This library relies on an internal `AppFlowPermissionsManager` class to automatically infer and apply appropriate resource policy statements to the S3 Bucket, KMS Key, and Secrets Manager Secret resources. `AppFlowPermissionsManager` places the statements exactly once for the `appflow.amazonaws.com` principal no matter how many times a resource is reused in the code. - -### Confused Deputy Problem - -Amazon AppFlow is an account-bound and a regional service. With this it is invurlnerable to the confused deputy problem (see, e.g. [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html)). However, `AppFlowPermissionsManager` still introduces the `aws:SourceAccount` condtition to the resource policies as a *best practice*. # API Reference ## Constructs diff --git a/package.json b/package.json index 733e0986..9b21180d 100644 --- a/package.json +++ b/package.json @@ -132,9 +132,9 @@ "jest-junit": "^15", "jsii": "1.x", "jsii-diff": "^1.93.0", - "jsii-docgen": "^8.0.56", + "jsii-docgen": "^10.3.3", "jsii-pacmak": "^1.93.0", - "jsii-rosetta": "^5.2.7", + "jsii-rosetta": "^5.3.2", "projen": "^0.77.6", "standard-version": "^9", "ts-jest": "^27", diff --git a/yarn.lock b/yarn.lock index a1c98470..dfdf515b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -763,14 +763,6 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@jsii/check-node@1.92.0": - version "1.92.0" - resolved "https://registry.yarnpkg.com/@jsii/check-node/-/check-node-1.92.0.tgz#e05908d2c0875a728db14d73bb30459a73bd008e" - integrity sha512-MQnFvDIn/VOz4FzchobZ4dfrl6qfuZIlYviNbGXhPMSeJ92BVB2F+NEyem9Sg/Csy2ehhtO1FGaUj4mO9/7Ntg== - dependencies: - chalk "^4.1.2" - semver "^7.5.4" - "@jsii/check-node@1.93.0": version "1.93.0" resolved "https://registry.yarnpkg.com/@jsii/check-node/-/check-node-1.93.0.tgz#3adcc6012654bb69fb8dc508e757b83ea9cd1708" @@ -779,7 +771,7 @@ chalk "^4.1.2" semver "^7.5.4" -"@jsii/spec@1.93.0", "@jsii/spec@^1.84.0", "@jsii/spec@^1.92.0", "@jsii/spec@^1.93.0": +"@jsii/spec@1.93.0", "@jsii/spec@^1.93.0": version "1.93.0" resolved "https://registry.yarnpkg.com/@jsii/spec/-/spec-1.93.0.tgz#e56c5971efbd349592de86081b3cbfd04fc0bb77" integrity sha512-PIXcTHUsFOoxSE7KMpJ3iJ3iYGSo2x46ZX4bHDDD6C7M3ij+7Z3Ujumg/OsIrESCHKWXGXlgl9EmkNJraeYkRQ== @@ -2732,7 +2724,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob-promise@^6.0.3: +glob-promise@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/glob-promise/-/glob-promise-6.0.5.tgz#3d56331b324fd7d097b43ba9e9959e9c7e200e2c" integrity sha512-uUzvxo60yo/vMLXZHCNAlfdM5U5A07jCnUO8xTK44Z0Vc58poGDXhDx8ju1DmPdprOORh+4Lpog64hl+AJ5piA== @@ -3708,19 +3700,18 @@ jsii-diff@^1.93.0: log4js "^6.9.1" yargs "^16.2.0" -jsii-docgen@^8.0.56: - version "8.0.56" - resolved "https://registry.yarnpkg.com/jsii-docgen/-/jsii-docgen-8.0.56.tgz#f19cc27dc40996bf8d9b41ef3b917167e0cc987d" - integrity sha512-SN+c+RPkhMLC8TyAi1j6eI3LCzq9SAJkf/IXnmrzGL4dQZAjp1kFs6xWkLb/Nr5B471/gAz6ipfx5ZdGs3G83A== +jsii-docgen@^10.3.3: + version "10.3.4" + resolved "https://registry.yarnpkg.com/jsii-docgen/-/jsii-docgen-10.3.4.tgz#c90cb3919ce62cc03032b8f14d82809c6279ed3c" + integrity sha512-v8bXr5BoNC/yKSX8K9JxoRf4L0U0R0C8eAd046N8hcIdaDT8Fm8feKxIgC7Q8QQJ9IiRm0UGghnl2cIvnzNaPw== dependencies: - "@jsii/spec" "^1.84.0" + "@jsii/spec" "^1.93.0" case "^1.6.3" fs-extra "^10.1.0" glob "^8.1.0" - glob-promise "^6.0.3" - jsii-reflect "^1.84.0" - jsii-rosetta "^1.84.0" - semver "^7.5.2" + glob-promise "^6.0.5" + jsii-reflect "^1.93.0" + semver "^7.5.4" yargs "^16.2.0" jsii-pacmak@^1.93.0: @@ -3742,7 +3733,7 @@ jsii-pacmak@^1.93.0: xmlbuilder "^15.1.1" yargs "^16.2.0" -jsii-reflect@^1.84.0, jsii-reflect@^1.93.0: +jsii-reflect@^1.93.0: version "1.93.0" resolved "https://registry.yarnpkg.com/jsii-reflect/-/jsii-reflect-1.93.0.tgz#5b2dcb964a25e5886b3d5d23020485d02630d301" integrity sha512-obf74y7RFXFNfPmgJYMQoRVPeR40czub0MM+rKfyEape5+qqvTU1pyUN384kVzpEzUfFIRsFMWqfxrW4zqwuPQ== @@ -3754,7 +3745,7 @@ jsii-reflect@^1.84.0, jsii-reflect@^1.93.0: oo-ascii-tree "^1.93.0" yargs "^16.2.0" -jsii-rosetta@^1.84.0, jsii-rosetta@^1.93.0: +jsii-rosetta@^1.93.0: version "1.93.0" resolved "https://registry.yarnpkg.com/jsii-rosetta/-/jsii-rosetta-1.93.0.tgz#951e8ae27ceaf0504abd74c15866f6050c97ef82" integrity sha512-5HFoC6Cp3Y3usCGuTRDTL/ovgz9MxI6/kY4Re8agVShXR6MPSX6F6Sc1qGMUjf3ynFfPz+DMsBY0Z164cxVKBA== @@ -3772,10 +3763,10 @@ jsii-rosetta@^1.84.0, jsii-rosetta@^1.93.0: workerpool "^6.5.1" yargs "^16.2.0" -jsii-rosetta@^5.2.7: - version "5.2.7" - resolved "https://registry.yarnpkg.com/jsii-rosetta/-/jsii-rosetta-5.2.7.tgz#fdff8dcc8c0b2a6fc4add600324da1cff2a93d43" - integrity sha512-l7FuVxbAzySIQdedG20LqKplUHyY/f1MfgdinXLfEgxj8XY/o/7EXtGd0ZaG4anLaCQedUPjF7GRbI9NVo+eRA== +jsii-rosetta@^5.3.2: + version "5.3.3" + resolved "https://registry.yarnpkg.com/jsii-rosetta/-/jsii-rosetta-5.3.3.tgz#581f43a507a7e025abf19d57f37489072970b659" + integrity sha512-p00zNkx1yicyAyWGWnpIXutryqx3Q1Hko3yPWrhOsSEqy/WiKGAKme87S/EZVnpz0TAse/mX4OymlBCqDXagJw== dependencies: "@jsii/check-node" "1.93.0" "@jsii/spec" "^1.93.0" @@ -3783,15 +3774,15 @@ jsii-rosetta@^5.2.7: chalk "^4" commonmark "^0.30.0" fast-glob "^3.3.2" - jsii "~5.2.5" + jsii "~5.3.0" semver "^7.5.4" semver-intersect "^1.5.0" stream-json "^1.8.0" - typescript "~5.2.2" + typescript "~5.3" workerpool "^6.5.1" yargs "^17.7.2" -jsii@1.93.0: +jsii@1.93.0, jsii@1.x: version "1.93.0" resolved "https://registry.yarnpkg.com/jsii/-/jsii-1.93.0.tgz#a30e077883235c7fdd09772e0637eeefeef975d9" integrity sha512-J6In5MDWcmVosOwZxdwcW+NisQZ2p9g2zWFwCO3RpMoHmpzYasChZSvRvpgR5iFB7m10QRebU+45R2WCGsadfg== @@ -3810,29 +3801,10 @@ jsii@1.93.0: typescript "~3.9.10" yargs "^16.2.0" -jsii@1.x: - version "1.92.0" - resolved "https://registry.yarnpkg.com/jsii/-/jsii-1.92.0.tgz#dc89cf48b2cf681fe9c6a77ee2d94911178348ae" - integrity sha512-UHuPVMkUXBcbnSAsylQSea7BFNkr6hkx6NhGGoZ5Xnb3fZV7wwr9DHnE14yQJUIsrCL8WcqhCU79QTbWmnKpag== - dependencies: - "@jsii/check-node" "1.92.0" - "@jsii/spec" "^1.92.0" - case "^1.6.3" - chalk "^4" - fast-deep-equal "^3.1.3" - fs-extra "^10.1.0" - log4js "^6.9.1" - semver "^7.5.4" - semver-intersect "^1.4.0" - sort-json "^2.0.1" - spdx-license-list "^6.8.0" - typescript "~3.9.10" - yargs "^16.2.0" - -jsii@~5.2.5: - version "5.2.44" - resolved "https://registry.yarnpkg.com/jsii/-/jsii-5.2.44.tgz#7a768412f1a28f5f1ff3e92ab5f5b7e7430c3ae1" - integrity sha512-Z7sTqYzQ5yoJU/ie+svjqSzrOF5rl4pW/bojvCb/7MfJ+SaGqhMUQMxQGTfqmSvauME8JoVYqwMH89x6qreJ8A== +jsii@~5.3.0: + version "5.3.3" + resolved "https://registry.yarnpkg.com/jsii/-/jsii-5.3.3.tgz#49e12615543c9e0a6cbd2ed82dae347eb993c10c" + integrity sha512-M+kAUKJiLXXJXKYmBB0Q2n1aGoeNHyzMCLAx7402JqXSLxH4JGh6kOf4EH3U3LmQKzv2kxOHMRCg3Ssh82KtrQ== dependencies: "@jsii/check-node" "1.93.0" "@jsii/spec" "^1.93.0" @@ -3845,7 +3817,7 @@ jsii@~5.2.5: semver-intersect "^1.5.0" sort-json "^2.0.1" spdx-license-list "^6.8.0" - typescript "~5.2" + typescript "~5.3" yargs "^17.7.2" json-buffer@3.0.1: @@ -4776,7 +4748,7 @@ semver-intersect@^1.4.0, semver-intersect@^1.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@7.x, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: +semver@7.x, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.5.3, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -5378,7 +5350,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@^5.2.2: +typescript@^5.2.2, typescript@~5.3: version "5.3.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== @@ -5393,11 +5365,6 @@ typescript@~3.9.10: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== -typescript@~5.2, typescript@~5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" - integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== - uglify-js@^3.1.4: version "3.17.4" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" From 8a2e537dc02df0671ab50fed97348ca3515e64ef Mon Sep 17 00:00:00 2001 From: kozub Date: Mon, 8 Jan 2024 17:33:09 +0100 Subject: [PATCH 3/3] Revert "Update jsii dependency versions" This reverts commit 0c03151531e5e1f11be478990be5942fc46215d2. --- API.md | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 4 +- yarn.lock | 85 +++++++++++------ 3 files changed, 318 insertions(+), 28 deletions(-) diff --git a/API.md b/API.md index ad490d07..57576076 100644 --- a/API.md +++ b/API.md @@ -1,3 +1,260 @@ +# Amazon AppFlow Construct Library + +*Note:* this library is currently in technical preview. + +## Introduction + +Amazon AppFlow is a service that enables creating managed, bi-directional data transfer integrations between various SaaS applications and AWS services. + +For more information, see the [Amazon AppFlow User Guide](https://docs.aws.amazon.com/appflow/latest/userguide/what-is-appflow.html). + +## Example + +```ts +import { SecretValue } from 'aws-cdk-lib'; +import { Bucket } from 'aws-cdk-lib/aws-s3'; +import { ISecret } from 'aws-cdk-lib/aws-secretsmanager'; +import { + ISource, + IDestination, + Filter, + FilterCondition, + Mapping, + OnDemandFlow, + S3Destination, + SalesforceConnectorProfile, + SalesforceSource, + Transform, + Validation, + ValidationAction, + ValidationCondition, +} from '@cdklabs/cdk-appflow'; + +declare const clientSecret: ISecret; +declare const accessToken: SecretValue; +declare const refreshToken: SecretValue; +declare const instanceUrl: string; + +const profile = new SalesforceConnectorProfile(this, 'MyConnectorProfile', { + oAuth: { + accessToken: accessToken, + flow: { + refreshTokenGrant: { + refreshToken: refreshToken, + client: clientSecret, + }, + }, + }, + instanceUrl: instanceUrl, + isSandbox: false, +}); + +const source = new SalesforceSource({ + profile: profile, + object: 'Account', +}); + +const bucket = new Bucket(this, 'DestinationBucket'); + +const destination = new S3Destination({ + location: { bucket }, +}); + +new OnDemandFlow(this, 'SfAccountToS3', { + source: source, + destination: destination, + mappings: [Mapping.mapAll()], + transforms: [ + Transform.mask({ name: 'Name' }, '*'), + ], + validations: [ + Validation.when(ValidationCondition.isNull('Name'), ValidationAction.ignoreRecord()), + ], + filters: [ + Filter.when(FilterCondition.timestampLessThanEquals({ name: 'LastModifiedDate', dataType: 'datetime' }, new Date(Date.parse('2022-02-02')))), + ], +}); + +``` +# Concepts + +Amazon AppFlow introduces several concepts that abstract away the technicalities of setting up and managing data integrations. + +An `Application` is any SaaS data integration component that can be either a *source* or a *destination* for Amazon AppFlow. A source is an application from which Amazon AppFlow will retrieve data, whereas a destination is an application to which Amazon AppFlow will send data. + +A `Flow` is Amazon AppFlow's integration between a source and a destination. + +A `ConnectorProfile` is Amazon AppFlow's abstraction over authentication/authorization with a particular SaaS application. The per-SaaS application permissions given to a particular `ConnectorProfile` will determine whether the connector profile can support the application as a source or as a destination (see whether a particular application is supported as either a source or a destination in [the documentation](https://docs.aws.amazon.com/appflow/latest/userguide/app-specific.html)). + +## Types of Flows + +The library introduces three, separate types of flows: + +- `OnDemandFlow` - a construct representing a flow that can be triggered programmatically with the use of a [StartFlow API call](https://docs.aws.amazon.com/appflow/1.0/APIReference/API_StartFlow.html). + +- `OnEventFlow` - a construct representing a flow that is triggered by a SaaS application event published to AppFlow. At the time of writing only a Salesforce source is able to publish events that can be consumed by AppFlow flows. + +- `OnScheduleFlow` - a construct representing a flow that is triggered on a [`Schedule`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_events.Schedule.html) + +## Tasks + +Tasks are steps that can be taken upon fields. Tasks compose higher level objects that in this library are named `Operations`. There are four operations identified: + +- Transforms - 1-1 transforms on source fields, like truncation or masking + +- Mappings - 1-1 or many-to-1 operations from source fields to a destination field + +- Filters - operations that limit the source data on a particular conditions + +- Validations - operations that work on a per-record level and can have either a record-level consequence (i.e. dropping the record) or a global one (terminating the flow). + +Each flow exposes dedicated properties to each of the operation types that one can use like in the example below: + +```ts +import { + Filter, + FilterCondition, + IDestination, + ISource, + Mapping, + OnDemandFlow, + S3Destination, + SalesforceConnectorProfile, + SalesforceSource, + Transform, + Validation, + ValidationAction, + ValidationCondition, +} from '@cdklabs/cdk-appflow'; + +declare const stack: Stack; +declare const source: ISource; +declare const destination: IDestination; + +const flow = new OnDemandFlow(stack, 'OnDemandFlow', { + source: source, + destination: destination, + transforms: [ + Transform.mask({ name: 'Name' }, '*'), + ], + mappings: [ + Mapping.map({ name: 'Name', dataType: 'String' }, { name: 'Name', dataType: 'string' }), + ], + filters: [ + Filter.when(FilterCondition.timestampLessThanEquals({ name: 'LastModifiedDate', dataType: 'datetime' }, new Date(Date.parse('2022-02-02')))), + ], + validations: [ + Validation.when(ValidationCondition.isNull('Name'), ValidationAction.ignoreRecord()), + ] +}); +``` + +## Monitoring + + +### Metrcis + +Each flow allows to access metrics through the methods: +- `metricFlowExecutionsStarted` +- `metricFlowExecutionsFailed` +- `metricFlowExecutionsSucceeded` +- `metricFlowExecutionTime` +- `metricFlowExecutionRecordsProcessed` + + +For detailed information about AppFlow metrics refer to [the documentation](https://docs.aws.amazon.com/appflow/latest/userguide/monitoring-cloudwatch.html). + +It can be consume by CloudWatch alert using as in the example below: + + +```ts +import { IFlow } from '@cdklabs/cdk-appflow'; + +declare const flow: IFlow; +declare const stack: Stack; + +const metric = flow.metricFlowExecutionsStarted(); + +metric.createAlarm(stack, "FlowExecutionsStartedAlarm", { + threshold: 1000, + evaluationPeriods: 2 +}); +``` + + +### EventBridge notifications + +Each flow publishes events to the default EventBridge bus: + +- `onRunStarted` +- `onRunCompleted` +- `onDeactivated` (only for the `OnEventFlow` and the `OnScheduleFlow`) +- `onStatus` (only for the `OnEventFlow` ) + +This way one can consume the notifications as in the example below: + +```ts +import { ITopic } from 'aws-cdk-lib/aws-sns'; +import { SnsTopic } from 'aws-cdk-lib/aws-events-targets'; +import { IFlow } from '@cdklabs/cdk-appflow'; + +declare const flow: IFlow; +declare const myTopic: ITopic; + +flow.onRunCompleted('OnRunCompleted', { + target: new SnsTopic(myTopic), +}); +``` + +# Notable distinctions from CloudFormation specification + +## `OnScheduleFlow` and `incrementalPullConfig` + +In CloudFormation the definition of the `incrementalPullConfig` (which effectively gives a name of the field used for tracking the last pulled timestamp) is on the [`SourceFlowConfig`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-sourceflowconfig.html#cfn-appflow-flow-sourceflowconfig-incrementalpullconfig) property. In the library this has been moved to the `OnScheduleFlow` constructor properties. + +## `S3Destination` and Glue Catalog + +Although in CloudFormation the Glue Catalog configuration is settable on the flow level - it works only when the destination is S3. That is why the library shifts the Glue Catalog properties definition to the `S3Destination`, which in turn requires using Lazy for populating `metadataCatalogConfig` in the flow. + +# Security considerations + +It is *recommended* to follow [data protection mechanisms for Amazon AppFlow](https://docs.aws.amazon.com/appflow/latest/userguide/data-protection.html). + +## Confidential information + +Amazon AppFlow application integration is done using `ConnectionProfiles`. A `ConnectionProfile` requires providing sensitive information in the form of e.g. access and refresh tokens. It is *recommended* that such information is stored securely and passed to AWS CDK securely. All sensitive fields are effectively `IResolvable` and this means they can be resolved at deploy time. With that one should follow the [best practices for credentials with CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/security-best-practices.html#creds). In this library, the sensitive fields are typed as `SecretValue` to emphasize these should not be plain strings. + +An example of using a predefined AWS Secrets Manager secret for storing sensitive information can be found below: + +```ts +import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; +import { GoogleAnalytics4ConnectorProfile } from '@cdklabs/cdk-appflow'; + +declare const stack: Stack; + +const secret = Secret.fromSecretNameV2(stack, 'GA4Secret', 'appflow/ga4'); + +const profile = new GoogleAnalytics4ConnectorProfile(stack, 'GA4Connector', { + oAuth: { + flow: { + refreshTokenGrant: { + refreshToken: secret.secretValueFromJson('refreshToken'), + clientId: secret.secretValueFromJson('clientId'), + clientSecret: secret.secretValueFromJson('clientSecret'), + }, + }, + }, +}); + +``` + +## An approach to managing permissions + +This library relies on an internal `AppFlowPermissionsManager` class to automatically infer and apply appropriate resource policy statements to the S3 Bucket, KMS Key, and Secrets Manager Secret resources. `AppFlowPermissionsManager` places the statements exactly once for the `appflow.amazonaws.com` principal no matter how many times a resource is reused in the code. + +### Confused Deputy Problem + +Amazon AppFlow is an account-bound and a regional service. With this it is invurlnerable to the confused deputy problem (see, e.g. [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html)). However, `AppFlowPermissionsManager` still introduces the `aws:SourceAccount` condtition to the resource policies as a *best practice*. # API Reference ## Constructs diff --git a/package.json b/package.json index 9b21180d..733e0986 100644 --- a/package.json +++ b/package.json @@ -132,9 +132,9 @@ "jest-junit": "^15", "jsii": "1.x", "jsii-diff": "^1.93.0", - "jsii-docgen": "^10.3.3", + "jsii-docgen": "^8.0.56", "jsii-pacmak": "^1.93.0", - "jsii-rosetta": "^5.3.2", + "jsii-rosetta": "^5.2.7", "projen": "^0.77.6", "standard-version": "^9", "ts-jest": "^27", diff --git a/yarn.lock b/yarn.lock index dfdf515b..a1c98470 100644 --- a/yarn.lock +++ b/yarn.lock @@ -763,6 +763,14 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jsii/check-node@1.92.0": + version "1.92.0" + resolved "https://registry.yarnpkg.com/@jsii/check-node/-/check-node-1.92.0.tgz#e05908d2c0875a728db14d73bb30459a73bd008e" + integrity sha512-MQnFvDIn/VOz4FzchobZ4dfrl6qfuZIlYviNbGXhPMSeJ92BVB2F+NEyem9Sg/Csy2ehhtO1FGaUj4mO9/7Ntg== + dependencies: + chalk "^4.1.2" + semver "^7.5.4" + "@jsii/check-node@1.93.0": version "1.93.0" resolved "https://registry.yarnpkg.com/@jsii/check-node/-/check-node-1.93.0.tgz#3adcc6012654bb69fb8dc508e757b83ea9cd1708" @@ -771,7 +779,7 @@ chalk "^4.1.2" semver "^7.5.4" -"@jsii/spec@1.93.0", "@jsii/spec@^1.93.0": +"@jsii/spec@1.93.0", "@jsii/spec@^1.84.0", "@jsii/spec@^1.92.0", "@jsii/spec@^1.93.0": version "1.93.0" resolved "https://registry.yarnpkg.com/@jsii/spec/-/spec-1.93.0.tgz#e56c5971efbd349592de86081b3cbfd04fc0bb77" integrity sha512-PIXcTHUsFOoxSE7KMpJ3iJ3iYGSo2x46ZX4bHDDD6C7M3ij+7Z3Ujumg/OsIrESCHKWXGXlgl9EmkNJraeYkRQ== @@ -2724,7 +2732,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob-promise@^6.0.5: +glob-promise@^6.0.3: version "6.0.5" resolved "https://registry.yarnpkg.com/glob-promise/-/glob-promise-6.0.5.tgz#3d56331b324fd7d097b43ba9e9959e9c7e200e2c" integrity sha512-uUzvxo60yo/vMLXZHCNAlfdM5U5A07jCnUO8xTK44Z0Vc58poGDXhDx8ju1DmPdprOORh+4Lpog64hl+AJ5piA== @@ -3700,18 +3708,19 @@ jsii-diff@^1.93.0: log4js "^6.9.1" yargs "^16.2.0" -jsii-docgen@^10.3.3: - version "10.3.4" - resolved "https://registry.yarnpkg.com/jsii-docgen/-/jsii-docgen-10.3.4.tgz#c90cb3919ce62cc03032b8f14d82809c6279ed3c" - integrity sha512-v8bXr5BoNC/yKSX8K9JxoRf4L0U0R0C8eAd046N8hcIdaDT8Fm8feKxIgC7Q8QQJ9IiRm0UGghnl2cIvnzNaPw== +jsii-docgen@^8.0.56: + version "8.0.56" + resolved "https://registry.yarnpkg.com/jsii-docgen/-/jsii-docgen-8.0.56.tgz#f19cc27dc40996bf8d9b41ef3b917167e0cc987d" + integrity sha512-SN+c+RPkhMLC8TyAi1j6eI3LCzq9SAJkf/IXnmrzGL4dQZAjp1kFs6xWkLb/Nr5B471/gAz6ipfx5ZdGs3G83A== dependencies: - "@jsii/spec" "^1.93.0" + "@jsii/spec" "^1.84.0" case "^1.6.3" fs-extra "^10.1.0" glob "^8.1.0" - glob-promise "^6.0.5" - jsii-reflect "^1.93.0" - semver "^7.5.4" + glob-promise "^6.0.3" + jsii-reflect "^1.84.0" + jsii-rosetta "^1.84.0" + semver "^7.5.2" yargs "^16.2.0" jsii-pacmak@^1.93.0: @@ -3733,7 +3742,7 @@ jsii-pacmak@^1.93.0: xmlbuilder "^15.1.1" yargs "^16.2.0" -jsii-reflect@^1.93.0: +jsii-reflect@^1.84.0, jsii-reflect@^1.93.0: version "1.93.0" resolved "https://registry.yarnpkg.com/jsii-reflect/-/jsii-reflect-1.93.0.tgz#5b2dcb964a25e5886b3d5d23020485d02630d301" integrity sha512-obf74y7RFXFNfPmgJYMQoRVPeR40czub0MM+rKfyEape5+qqvTU1pyUN384kVzpEzUfFIRsFMWqfxrW4zqwuPQ== @@ -3745,7 +3754,7 @@ jsii-reflect@^1.93.0: oo-ascii-tree "^1.93.0" yargs "^16.2.0" -jsii-rosetta@^1.93.0: +jsii-rosetta@^1.84.0, jsii-rosetta@^1.93.0: version "1.93.0" resolved "https://registry.yarnpkg.com/jsii-rosetta/-/jsii-rosetta-1.93.0.tgz#951e8ae27ceaf0504abd74c15866f6050c97ef82" integrity sha512-5HFoC6Cp3Y3usCGuTRDTL/ovgz9MxI6/kY4Re8agVShXR6MPSX6F6Sc1qGMUjf3ynFfPz+DMsBY0Z164cxVKBA== @@ -3763,10 +3772,10 @@ jsii-rosetta@^1.93.0: workerpool "^6.5.1" yargs "^16.2.0" -jsii-rosetta@^5.3.2: - version "5.3.3" - resolved "https://registry.yarnpkg.com/jsii-rosetta/-/jsii-rosetta-5.3.3.tgz#581f43a507a7e025abf19d57f37489072970b659" - integrity sha512-p00zNkx1yicyAyWGWnpIXutryqx3Q1Hko3yPWrhOsSEqy/WiKGAKme87S/EZVnpz0TAse/mX4OymlBCqDXagJw== +jsii-rosetta@^5.2.7: + version "5.2.7" + resolved "https://registry.yarnpkg.com/jsii-rosetta/-/jsii-rosetta-5.2.7.tgz#fdff8dcc8c0b2a6fc4add600324da1cff2a93d43" + integrity sha512-l7FuVxbAzySIQdedG20LqKplUHyY/f1MfgdinXLfEgxj8XY/o/7EXtGd0ZaG4anLaCQedUPjF7GRbI9NVo+eRA== dependencies: "@jsii/check-node" "1.93.0" "@jsii/spec" "^1.93.0" @@ -3774,15 +3783,15 @@ jsii-rosetta@^5.3.2: chalk "^4" commonmark "^0.30.0" fast-glob "^3.3.2" - jsii "~5.3.0" + jsii "~5.2.5" semver "^7.5.4" semver-intersect "^1.5.0" stream-json "^1.8.0" - typescript "~5.3" + typescript "~5.2.2" workerpool "^6.5.1" yargs "^17.7.2" -jsii@1.93.0, jsii@1.x: +jsii@1.93.0: version "1.93.0" resolved "https://registry.yarnpkg.com/jsii/-/jsii-1.93.0.tgz#a30e077883235c7fdd09772e0637eeefeef975d9" integrity sha512-J6In5MDWcmVosOwZxdwcW+NisQZ2p9g2zWFwCO3RpMoHmpzYasChZSvRvpgR5iFB7m10QRebU+45R2WCGsadfg== @@ -3801,10 +3810,29 @@ jsii@1.93.0, jsii@1.x: typescript "~3.9.10" yargs "^16.2.0" -jsii@~5.3.0: - version "5.3.3" - resolved "https://registry.yarnpkg.com/jsii/-/jsii-5.3.3.tgz#49e12615543c9e0a6cbd2ed82dae347eb993c10c" - integrity sha512-M+kAUKJiLXXJXKYmBB0Q2n1aGoeNHyzMCLAx7402JqXSLxH4JGh6kOf4EH3U3LmQKzv2kxOHMRCg3Ssh82KtrQ== +jsii@1.x: + version "1.92.0" + resolved "https://registry.yarnpkg.com/jsii/-/jsii-1.92.0.tgz#dc89cf48b2cf681fe9c6a77ee2d94911178348ae" + integrity sha512-UHuPVMkUXBcbnSAsylQSea7BFNkr6hkx6NhGGoZ5Xnb3fZV7wwr9DHnE14yQJUIsrCL8WcqhCU79QTbWmnKpag== + dependencies: + "@jsii/check-node" "1.92.0" + "@jsii/spec" "^1.92.0" + case "^1.6.3" + chalk "^4" + fast-deep-equal "^3.1.3" + fs-extra "^10.1.0" + log4js "^6.9.1" + semver "^7.5.4" + semver-intersect "^1.4.0" + sort-json "^2.0.1" + spdx-license-list "^6.8.0" + typescript "~3.9.10" + yargs "^16.2.0" + +jsii@~5.2.5: + version "5.2.44" + resolved "https://registry.yarnpkg.com/jsii/-/jsii-5.2.44.tgz#7a768412f1a28f5f1ff3e92ab5f5b7e7430c3ae1" + integrity sha512-Z7sTqYzQ5yoJU/ie+svjqSzrOF5rl4pW/bojvCb/7MfJ+SaGqhMUQMxQGTfqmSvauME8JoVYqwMH89x6qreJ8A== dependencies: "@jsii/check-node" "1.93.0" "@jsii/spec" "^1.93.0" @@ -3817,7 +3845,7 @@ jsii@~5.3.0: semver-intersect "^1.5.0" sort-json "^2.0.1" spdx-license-list "^6.8.0" - typescript "~5.3" + typescript "~5.2" yargs "^17.7.2" json-buffer@3.0.1: @@ -4748,7 +4776,7 @@ semver-intersect@^1.4.0, semver-intersect@^1.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@7.x, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.5.3, semver@^7.5.4: +semver@7.x, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -5350,7 +5378,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@^5.2.2, typescript@~5.3: +typescript@^5.2.2: version "5.3.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== @@ -5365,6 +5393,11 @@ typescript@~3.9.10: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== +typescript@~5.2, typescript@~5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== + uglify-js@^3.1.4: version "3.17.4" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c"