-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(codepipeline): introduce the Action abstract class (#14009)
Add an officially supported class to the API of the CodePipeline module that contains some logic common to all implementations of the `IAction` interface, like dealing with variable namespaces, or methods like `onStateChange()`. There was previously a class like that in the codepipeline-actions module, but it was marked experimental, and had some strong opinions on the construction way of the subclasses (required passing the `actionProperties` through a `super()` call in the constructor, which is not very flexible). ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
- Loading branch information
Showing
7 changed files
with
144 additions
and
142 deletions.
There are no files selected for viewing
122 changes: 6 additions & 116 deletions
122
packages/@aws-cdk/aws-codepipeline-actions/lib/action.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,125 +1,15 @@ | ||
import * as codepipeline from '@aws-cdk/aws-codepipeline'; | ||
import * as events from '@aws-cdk/aws-events'; | ||
import { Lazy } from '@aws-cdk/core'; | ||
|
||
// keep this import separate from other imports to reduce chance for merge conflicts with v2-main | ||
// eslint-disable-next-line no-duplicate-imports, import/order | ||
import { Construct } from '@aws-cdk/core'; | ||
|
||
/** | ||
* Low-level class for generic CodePipeline Actions. | ||
* | ||
* WARNING: this class should not be externally exposed, but is currently visible | ||
* because of a limitation of jsii (https://github.com/aws/jsii/issues/524). | ||
* | ||
* This class will disappear in a future release and should not be used. | ||
* | ||
* @experimental | ||
* If you're implementing your own IAction, | ||
* prefer to use the Action class from the codepipeline module. | ||
*/ | ||
export abstract class Action implements codepipeline.IAction { | ||
public readonly actionProperties: codepipeline.ActionProperties; | ||
private _pipeline?: codepipeline.IPipeline; | ||
private _stage?: codepipeline.IStage; | ||
private _scope?: Construct; | ||
private readonly customerProvidedNamespace?: string; | ||
private readonly namespaceOrToken: string; | ||
private actualNamespace?: string; | ||
private variableReferenced = false; | ||
export abstract class Action extends codepipeline.Action { | ||
protected readonly providedActionProperties: codepipeline.ActionProperties; | ||
|
||
protected constructor(actionProperties: codepipeline.ActionProperties) { | ||
this.customerProvidedNamespace = actionProperties.variablesNamespace; | ||
this.namespaceOrToken = Lazy.string({ | ||
produce: () => { | ||
// make sure the action was bound (= added to a pipeline) | ||
if (this.actualNamespace !== undefined) { | ||
return this.customerProvidedNamespace !== undefined | ||
// if a customer passed a namespace explicitly, always use that | ||
? this.customerProvidedNamespace | ||
// otherwise, only return a namespace if any variable was referenced | ||
: (this.variableReferenced ? this.actualNamespace : undefined); | ||
} else { | ||
throw new Error(`Cannot reference variables of action '${this.actionProperties.actionName}', ` + | ||
'as that action was never added to a pipeline'); | ||
} | ||
}, | ||
}); | ||
this.actionProperties = { | ||
...actionProperties, | ||
variablesNamespace: this.namespaceOrToken, | ||
}; | ||
} | ||
|
||
public bind(scope: Construct, stage: codepipeline.IStage, options: codepipeline.ActionBindOptions): | ||
codepipeline.ActionConfig { | ||
this._pipeline = stage.pipeline; | ||
this._stage = stage; | ||
this._scope = scope; | ||
|
||
this.actualNamespace = this.customerProvidedNamespace === undefined | ||
// default a namespace name, based on the stage and action names | ||
? `${stage.stageName}_${this.actionProperties.actionName}_NS` | ||
: this.customerProvidedNamespace; | ||
|
||
return this.bound(scope, stage, options); | ||
} | ||
|
||
public onStateChange(name: string, target?: events.IRuleTarget, options?: events.RuleProps) { | ||
const rule = new events.Rule(this.scope, name, options); | ||
rule.addTarget(target); | ||
rule.addEventPattern({ | ||
detailType: ['CodePipeline Action Execution State Change'], | ||
source: ['aws.codepipeline'], | ||
resources: [this.pipeline.pipelineArn], | ||
detail: { | ||
stage: [this.stage.stageName], | ||
action: [this.actionProperties.actionName], | ||
}, | ||
}); | ||
return rule; | ||
} | ||
|
||
protected variableExpression(variableName: string): string { | ||
this.variableReferenced = true; | ||
return `#{${this.namespaceOrToken}.${variableName}}`; | ||
} | ||
|
||
/** | ||
* The method called when an Action is attached to a Pipeline. | ||
* This method is guaranteed to be called only once for each Action instance. | ||
* | ||
* @param options an instance of the {@link ActionBindOptions} class, | ||
* that contains the necessary information for the Action | ||
* to configure itself, like a reference to the Role, etc. | ||
*/ | ||
protected abstract bound(scope: Construct, stage: codepipeline.IStage, options: codepipeline.ActionBindOptions): | ||
codepipeline.ActionConfig; | ||
|
||
private get pipeline(): codepipeline.IPipeline { | ||
if (this._pipeline) { | ||
return this._pipeline; | ||
} else { | ||
throw new Error('Action must be added to a stage that is part of a pipeline before using onStateChange'); | ||
} | ||
} | ||
|
||
private get stage(): codepipeline.IStage { | ||
if (this._stage) { | ||
return this._stage; | ||
} else { | ||
throw new Error('Action must be added to a stage that is part of a pipeline before using onStateChange'); | ||
} | ||
} | ||
|
||
/** | ||
* Retrieves the Construct scope of this Action. | ||
* Only available after the Action has been added to a Stage, | ||
* and that Stage to a Pipeline. | ||
*/ | ||
private get scope(): Construct { | ||
if (this._scope) { | ||
return this._scope; | ||
} else { | ||
throw new Error('Action must be added to a stage that is part of a pipeline first'); | ||
} | ||
super(); | ||
this.providedActionProperties = actionProperties; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters