{"_id":"@alexa/ask-cdk","_rev":"6-fc804cbb1fc502a5a3fd750cfa335b54","name":"@alexa/ask-cdk","dist-tags":{"latest":"0.0.2"},"versions":{"0.0.1":{"name":"@alexa/ask-cdk","version":"0.0.1","description":"Alexa Skills Kit CDK Construct Library","main":"dist/cjs/index.js","types":"dist/cjs/index.d.ts","license":"Apache-2.0","author":{"name":"Sam Goodwin","email":"samgood@amazon.com"},"scripts":{"build":"tsc && ./scripts/bundle.sh","bump":"standard-version  --bumpFiles=package.json","clean":"rm -rf dist lib","format":"prettier --write . --ignore=**/{build,dist,generated,lib,node_modules} --loglevel=error","release":"./scripts/release.sh","test":"echo TODO","watch":"tsc -w","update":"ncu -u"},"devDependencies":{"@alexa/acdl":"^0.1.0","@aws-cdk/aws-codepipeline-actions":"^1.128.0","@aws-cdk/aws-codepipeline":"^1.128.0","@aws-cdk/aws-iam":"^1.128.0","@aws-cdk/aws-lambda-nodejs":"^1.128.0","@aws-cdk/aws-lambda":"^1.128.0","@aws-cdk/aws-s3-deployment":"^1.128.0","@aws-cdk/aws-s3":"^1.128.0","@aws-cdk/aws-secretsmanager":"^1.128.0","@aws-cdk/aws-ssm":"^1.128.0","@aws-cdk/aws-stepfunctions-tasks":"^1.128.0","@aws-cdk/aws-stepfunctions":"^1.128.0","@aws-cdk/core":"^1.128.0","@aws-cdk/pipelines":"^1.128.0","@types/archiver":"^5.1.1","@types/aws-lambda":"^8.10.84","@types/unzipper":"^0.10.4","archiver":"^5.3.0","ask-sdk-model":"^1.36.1","ask-smapi-model":"^1.18.1","ask-smapi-sdk":"^1.3.0","aws-sdk":"^2.1012.0","constructs":"^3.3.161","esbuild":"0.13.8","fs-extra":"^10.0.0","npm-check-updates":"^11.8.5","prettier":"^2.4.1","standard-version":"^9.3.2","typescript":"^4.4.4","unzipper":"^0.10.11"},"peerDependencies":{"@alexa/acdl":"^0.1.0","@aws-cdk/aws-codepipeline-actions":"^1.128.0","@aws-cdk/aws-codepipeline":"^1.128.0","@aws-cdk/aws-iam":"^1.128.0","@aws-cdk/aws-lambda-nodejs":"^1.128.0","@aws-cdk/aws-lambda":"^1.128.0","@aws-cdk/aws-s3-deployment":"^1.128.0","@aws-cdk/aws-s3":"^1.128.0","@aws-cdk/aws-secretsmanager":"^1.128.0","@aws-cdk/aws-ssm":"^1.128.0","@aws-cdk/aws-stepfunctions-tasks":"^1.128.0","@aws-cdk/aws-stepfunctions":"^1.128.0","@aws-cdk/core":"^1.128.0","@aws-cdk/pipelines":"^1.128.0","constructs":"^3.3.161"},"_id":"@alexa/ask-cdk@0.0.1","_npmVersion":"5.6.0","_nodeVersion":"10.1.0","_npmUser":{"name":"amazonasksdk","email":"ask-sdk@amazon.com"},"dist":{"integrity":"sha512-q+hMndJdfcrbbMza81Ff2f/ce34VNWxkFRnEPg1Ed90iyFu5Wa9xcYod5+FFuXXHhPHjY01KbGdTy1dcQC0xTw==","shasum":"d8e58123be79df0c495e0e1f069f9695b214bb6c","tarball":"https://registry.npmjs.org/@alexa/ask-cdk/-/ask-cdk-0.0.1.tgz","fileCount":71,"unpackedSize":16562060,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCprYt+nSx6sW8FD3JS/rm8GrhBcUNZ5G0quo5Q1eVkpAIhAPD+efYJdjxUVHomtlvndMjqJVw3t+ARgxQz88uHY9rn"}]},"directories":{},"maintainers":[{"name":"amazonasksdk","email":"ask-sdk@amazon.com"},{"name":"savasp","email":"savas@parastatidis.name"},{"name":"savasp-amazon","email":"savasp@amazon.com"},{"name":"samgood","email":"samgood@amazon.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ask-cdk_0.0.1_1634798551295_0.9641512631192726"},"_hasShrinkwrap":false},"0.0.2":{"name":"@alexa/ask-cdk","version":"0.0.2","description":"Alexa Skills Kit CDK Construct Library","main":"dist/cjs/index.js","types":"dist/cjs/index.d.ts","license":"Apache-2.0","author":{"name":"Sam Goodwin","email":"samgood@amazon.com"},"scripts":{"build":"tsc && ./scripts/bundle.sh","bump":"standard-version  --bumpFiles=package.json","clean":"rm -rf dist lib","format":"prettier --write . --ignore=**/{build,dist,generated,lib,node_modules} --loglevel=error","release":"./scripts/release.sh","test":"echo TODO","watch":"tsc -w","update":"ncu -u"},"devDependencies":{"@alexa/acdl":"^0.1.3","@aws-cdk/aws-codepipeline-actions":"^1.132.0","@aws-cdk/aws-codepipeline":"^1.132.0","@aws-cdk/aws-iam":"^1.132.0","@aws-cdk/aws-lambda-nodejs":"^1.132.0","@aws-cdk/aws-lambda":"^1.132.0","@aws-cdk/aws-s3-deployment":"^1.132.0","@aws-cdk/aws-s3":"^1.132.0","@aws-cdk/aws-secretsmanager":"^1.132.0","@aws-cdk/aws-ssm":"^1.132.0","@aws-cdk/aws-stepfunctions-tasks":"^1.132.0","@aws-cdk/aws-stepfunctions":"^1.132.0","@aws-cdk/core":"^1.132.0","@aws-cdk/pipelines":"^1.132.0","@types/archiver":"^5.1.1","@types/aws-lambda":"^8.10.85","@types/unzipper":"^0.10.4","archiver":"^5.3.0","ask-sdk-model":"^1.37.0","ask-smapi-model":"^1.19.0","ask-smapi-sdk":"^1.3.0","aws-sdk":"^2.1032.0","constructs":"^3.3.161","esbuild":"0.13.14","fs-extra":"^10.0.0","npm-check-updates":"^12.0.2","prettier":"^2.4.1","standard-version":"^9.3.2","typescript":"^4.5.2","unzipper":"^0.10.11"},"peerDependencies":{"@alexa/acdl":"^0.1.3","@aws-cdk/aws-codepipeline-actions":"^1.132.0","@aws-cdk/aws-codepipeline":"^1.132.0","@aws-cdk/aws-iam":"^1.132.0","@aws-cdk/aws-lambda-nodejs":"^1.132.0","@aws-cdk/aws-lambda":"^1.132.0","@aws-cdk/aws-s3-deployment":"^1.132.0","@aws-cdk/aws-s3":"^1.132.0","@aws-cdk/aws-secretsmanager":"^1.132.0","@aws-cdk/aws-ssm":"^1.132.0","@aws-cdk/aws-stepfunctions-tasks":"^1.132.0","@aws-cdk/aws-stepfunctions":"^1.132.0","@aws-cdk/core":"^1.132.0","@aws-cdk/pipelines":"^1.132.0","constructs":"^3.3.161"},"_id":"@alexa/ask-cdk@0.0.2","_npmVersion":"5.6.0","_nodeVersion":"10.1.0","_npmUser":{"name":"amazonasksdk","email":"ask-sdk@amazon.com"},"dist":{"integrity":"sha512-/NMikEQiKtGl7XV3VAeVDZI1flg5rSdkIKlzXS03Aw+aXRZZihZV/Gk3CvJ8mbjv85oROKXX5RmXgnVf7fxovg==","shasum":"edc53259d8fabd70d855b73b857951be6ba56ae7","tarball":"https://registry.npmjs.org/@alexa/ask-cdk/-/ask-cdk-0.0.2.tgz","fileCount":71,"unpackedSize":16844530,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhl/I5CRA9TVsSAnZWagAAClEP/2ylr+5doXju9imWFpvX\nSAx35BoVUlIdu25Mh/Pi7FXgJEwbqcXDSLwAnLpmGn8X2xJT06u0oHerEj+y\nBCc09eZl81Oeanrk8XTm7LbRb+nSz6oleEWOXLtlcOczcYSHv9cHwmfK8FPS\nwKjF24ajkPppwVBuvsrgidN23KFVVuxG6cNqApSVxITAVGGH/eXsMG6kCw5I\nysdqoYJTDcnMDOi0ce+aHeQEG/ULig6Hzo9cHRc27PrMT0wsNqnVHtZe1YsV\n1x2RPXKWRHNZ4RZzrBXAe0mG/f0a/oPdEfoBWHzTUAgFRzMa+IW7u9zh2glu\nOEsQhnLdmpbVW1bDlm1ltAwwXBMGJVMLNG9KygaaLM+JobswLLAEX04hiIbx\nPvz10HiFtDRf6ZPlu+V2VWPnrE+AUUGjTR9kRqqWN9mOwfIWYwr0QrO4BbD0\nd9BU8l1cDKksantIt8bMF/8X62OsuwyVT3rnVMfQ1/r9D59aVyKRz+VXRBnX\nVKUaiLn6BIN76RGy4hVqCqEo3ap60LgDDWuYRrpZvIu8ENRMJna1jDpuMZKx\nIT5xugaBpK+qkYUnEr2dKFS+XcfdNwiKcj76JuXprvfhnQyUEofOFsIrdYAU\n7kQdxPKvO+YKnGdNRQV4mI95+OsESFKW6eixYEGH4QM88b7OoBOeq6E2xwg7\n0O/H\r\n=PQWj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFDcUOTYwDpIf6ngHQk7UCcptRZU6z1ePp7YerAQv9m+AiEA9hWQGscfgvad61oPoYLFOn9an69X2BBpcjqrJRY8oj8="}]},"directories":{},"maintainers":[{"name":"amazonasksdk","email":"ask-sdk@amazon.com"},{"name":"savasp","email":"savas@parastatidis.name"},{"name":"savasp-amazon","email":"savasp@amazon.com"},{"name":"samgood","email":"samgood@amazon.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ask-cdk_0.0.2_1637347896872_0.6281107221210682"},"_hasShrinkwrap":false}},"time":{"created":"2021-10-21T06:42:31.252Z","0.0.1":"2021-10-21T06:42:31.501Z","modified":"2023-03-24T20:53:58.844Z","0.0.2":"2021-11-19T18:51:37.139Z"},"maintainers":[{"email":"ask-tools@amazon.com","name":"ask-dev-tools"},{"email":"ask-sdk@amazon.com","name":"amazonasksdk"}],"description":"Alexa Skills Kit CDK Construct Library","author":{"name":"Sam Goodwin","email":"samgood@amazon.com"},"license":"Apache-2.0","readme":"# @alexa/ask-cdk\n\nThis package provides CDK Constructs for managing and deploying Alexa Skills with the AWS Cloud Development Kit. It integrates with the Alexa Conversations Description Language (ACDL) Compiler to prepare a Skill Package during build time and then deploy it via a Custom CloudFormation Resource.\n\nThe Skill Package is built locally during the CDK synthesize step (see https://docs.aws.amazon.com/cdk/latest/guide/apps.html). That Skill Package ZIP is then uploaded to S3 and later processed within a Custom Resource to modify the Skill Manifest and then imported into SMAPI (see https://developer.amazon.com/en-US/docs/alexa/smapi/skill-package-api-reference.html).\n\n## Setup\n\nTODO: provide a template for getting started, e.g. `ask new --cdk`.\n\nThe Skill Deployer requires a one-time setup process for each AWS Account and Region you wish to deploy and manage Alexa Skills with. We assume some basic knowledge of the AWS Cloud Development Kit and AWS CLI, so please refer to the following reference documentation:\n\n1. [Getting Started with the AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html).\n2. [Configuring the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html).\n\nFirst, make sure you've configured the ASK CLI, AWS CLI and bootstrapped your AWS accounts with the CDK:\n\n```shell\n# login with amazon and configure the ASK CLI\nask configure\n\n# configure your AWS CLI with access to your AWS account\naws configure\n\n# run the CDK bootstrap process for each AWS account and region you wish to use\ncdk bootstrap aws://<your-aws-account>/<aws-region>\n```\n\nIn order to deploy skills you need to store your ASK Login-With-Amazon credentials in a Secret so that they can be accessed by the Custom Resource. The `acc` CLI (available in NPM: `@alexa/acdl`) provides a light-weight utility to help with this process:\n\n```shell\nnpx acc bootstrap\n```\n\nThis command creates a CloudFormation Stack with a AWS Secrets Manager Secret. By default it creates a Stack and Secret with the name `ask-config-<vendor-id>`, where `vendor-id` is loaded from your ASK profile. After the Stack is created, the command then uploads you ASK config from `~/.ask/cli_config` to the Secret.\n\nYou may also specify profiles and a different name for the Stack and AWS Secret:\n\nTODO: put this script in the `ask-cli`.\n\n```shell\nnpx acc bootstrap \\\n --secret-name <secret-name>\\\n --profile <your-ask-profile>\\\n --aws-profile <your-aws-profile>\\\n --regions <comma-separated-list-of-regions>\n```\n\n## Skill Construct\n\nYou can use the `Skill` Construct to configure and deploy Skills within an ordinary AWS Cloud Development Kit Application.\n\nWe recommend you create a class extending `cdk.Stack` for your Skill:\n\n```ts\n// src/skill-stack.ts\nimport * as path from \"path\";\nimport * as ask from \"@alexa/ask-cdk\";\nimport * as cdk from \"@aws-cdk/core\";\nimport * as lambda from \"@aws-cdk/aws-lambda\";\nimport * as node from \"@aws-cdk/aws-lambda-nodejs\";\n\nexport interface MySkillStackProps extends cdk.StackProps {\n  skillName: string;\n  vendorId: string;\n}\n\nexport class MySkillStack extends cdk.Stack {\n  readonly skill: ask.Skill;\n  readonly endpoint: lambda.Function;\n\n  constructor(scope: cdk.App, id: string, props: MySkillStackProps) {\n    super(scope, id, props);\n\n    // create a Lambda Function referencing your Skill Handler code\n    this.endpoint = new node.NodejsFunction(this, \"Endpoint\", {\n      // point to the file that contains your skill's handler logic\n      entry: path.resolve(__dirname, \"pizzabot-handler.ts\"),\n      // name of the method within your code that Lambda calls\n      handler: \"index.handler\",\n      runtime: lambda.Runtime.NODEJS_14_X,\n      memorySize: 512,\n    });\n\n    // create an ask.Binding to the Lambda Function\n    const binding = new ask.LambdaBinding(this.endpoint);\n\n    // create and deploy an Alexa Skill Resource - this will Create and Update a Skill with SMAPI.\n    this.skill = new ask.Skill(this, \"MySkill\", {\n      // instance of the Skill Deployer to use when managing this Skill\n      skillDeployer: props.skillDeployer,\n      // name of your Alexa Skill\n      skillName: props.skillName,\n      // path to the Alexa Skills Project - this is the root directory containing `package.json`.\n      projectPath: path.resolve(__dirname, \"../\"),\n      // the default binding to use to handle requests\n      defaultBinding: binding,\n      // your amazon developer vendor id\n      vendorId: props.vendorId,\n    });\n  }\n}\n```\n\nAlternatively, instead of using the ACDL compiler and referencing a project path directly, you can reference a pre-compiled (or hand-written) Skill Package Path.\n\n```ts\nthis.skill = new ask.Skill(this, \"MySkill\", {\n  // ...\n  // path to your skill package\n  skillPackagePath: path.join(__dirname, \"path/to/skill-package\"),\n});\n```\n\n### AWS Secret\n\nThe `Skill` Construct needs your ASK LWA credentials in order to deploy the skill. As mentioned earlier, `acc bootstrap` will place your LWA credentials in a secure AWS Secret.\n\nBy default the Skill Construct will import a secret from `ask-config-<vendor-id>`, which is the default name of the secret from `acc bootstrap`. If you create a secret with a custom name, you can use the `secretName` prop in the `Skill` construct, and it will import the secret from `secretName` instead.\n\n```ts\nthis.skill = new ask.Skill(this, \"MySkill\", {\n  // ...\n  // changing the default secret import name\n  secretName: \"<your-secret-name>\",\n});\n```\n\nYou may also provide your own Secret instead of having the `Skill` Construct import it.\n\n```ts\nimport * as secrets from \"@aws-cdk/aws-secretsmanager\";\n\nconst secret = secrets.Secret.fromSecretNameV2(\n  this,\n  `MySecret`,\n  `<secret-name>`\n);\n\nthis.skill = new ask.Skill(this, \"MySkill\", {\n  // ...\n  // providing a secret\n  secret: secret,\n});\n```\n\nNote: If you provide a secret without bootstrapping the secret or uploading your ASK credentials, `cdk deploy` will fail.\n\n## Instantiating a Skill Stack\n\nNow that we have written `MySkillStack`, we can create an Alexa Skill for each AWS Account and Region:\n\n```ts\n// src/app.ts\nimport * as secrets from \"@aws-cdk/secretsmanager\";\nimport { MySkillStack } from \"./my-skill-stack\";\n\nconst app = new cdk.App();\n\n// deploy a dev skill\nconst devSkill = new MySkillStack(app, \"MySkill-dev\", {\n  skillName: \"MySkill-dev\",\n  vendorId: \"<your-vendor-id>\",\n  env: {\n    account: \"<dev-account>\",\n    region: \"us-west-2\",\n  },\n});\n\n// and a production skill\nconst prodSkill = new MySkillStack(app, \"MySkill-prod\", {\n  skillName: \"MySkill\",\n  vendorId: \"<your-vendor-id>\",\n  env: {\n    account: \"<prod-account>\",\n    region: \"us-east-1\",\n  },\n});\n```\n\nNote: you can find `<your-vendor-id>` on the [console](https://developer.amazon.com/settings/console/mycid) or the CLI (`cat ~/.ask/cli_config`):\n\nFinally, deploy the stacks to AWS:\n\n```shell\n# deploy all stacks\ncdk deploy\n\n# just the dev skill\ncdk deploy MySkill-dev\n\n# just the prod skill\ncdk deploy MySkill-prod\n```\n\nNote how easy it is to deploy copies of the same Skill Package to different AWS Accounts and Regions. In this example, we created two Skills with SMAPI - one for a dev account and another for prod. The Skill Resource is designed to encapsulate all the information for a Skill in a repeatable/re-usable way. Rather than fixing a single Skill project to a specific Skill ID (checked into code), the same Skill configuration can be used to create/update/delete in many Amazon Developer Accounts.\n\nFor example, we plan to support deploying a Skill on demand to test a GitHub Pull Request in isolation before approving the merge. The Skill would be updated on each commit and deleted once the pull request is merged. See (TODO: link issue).\n\n### Multi-Region Skills\n\nTOOD: add support for deploying a Skill with endpoints configured in `NA`, `FE` and `EU` regions. See (TODO: link issue).\n\n### Build Timeout\n\nSometimes an Alexa Conversations build can take more than an hour, exceeding the maximum timeout for an AWS CloudFormation Custom Resource (60 minutes). For now, our resource deals with this by waiting for up to 55 minutes before assuming the build will succeed. The low fidelity build usually completes much sooner, in a few minutes, so if the build hasn't failed within 55 minutes, it is safe to assume the build has succeeded.\n\n55 minutes is the default and 5 minutes is the minimum wait time. To specify, use the `maxBuildTimeMinutes` property when instantiating the `Skill`:\n\n```ts\nnew ask.Skill(this, \"MySkill\", {\n  // ...\n  maxBuildTimeMinutes: 10,\n});\n```\n\n## Skill.deployer\n\nThe `Skill` Construct contains a nested `SkillDeployer` construct for managing the AWS Resources that will Create/Update/Delete your skill. In your CloudFormation Stack you will see the following resources for managing your skill:\n\n1. CloudFormation Handler - Lambda Function that creates/deletes a barebones Skill to retrieve a Skill ID and then triggers a Step Function workflow to import a SKill Package.\n2. SMAPI Deploy Workflow - Standard Step Function Workflow to import a Skill Package and wait for the build to complete. Builds can be long running (sometimes exceeding Lambda's 15 minute timeout) especially for Alexa Conversations, so a Step Function workflow is used to reliably wait for the state of the build to succeed or fail.\n3. SMAPI Lambda Function - called by the Step Function to interact with SMAPI to import a Skill Package and check its status.\n\n## CloudFormation Workflow\n\nA Skill is orchestrated within CloudFormation with two Custom Resources which safely orchestrate the creation of a Skill, configuration of the Lambda and the import of a Skill Package into SMAPI.\n\n1. `Custom::AlexaSkillId`\n\n`Custom::AlexaSkillId` creates a bare-bones Skill by calling the `createSkillForVendorV1` API with a template Skill Manifest. The successful creation of this Resource creates a Skill ID so that we can properly configure the Lambda Trigger for our Alexa Skill before importing the Skill Package and pointing it at your Lambda Function.\n\nThis is analogous to using the Skill Developer Console to create a new skill from an empty template.\n\n2. `Custom::AlexaSkill`\n\n`Custom::AlexaSkill` downloads your Skill Package from S3 (compiled with the `@alexa/acdl` compiler and uploaded by the CDK toolchain), modifies the endpoint in `skill.json` and calls the `importSkillPackage` SMAPI API to initiate the Skill Package import. A Standard Step Function workflow will then wait for the build to complete before calling back to CloudFormation.\n\nIn between the creation of `Custom::AlexaSkillId` and `Custom::AlexaSkill`, the Lambda trigger is configured, allowing the service principal, `alexa-appkit.amazon.com`, to invoke the Skill. Without this, the Skill Package Import will fail with a Skill Manifest error.\n\nTo monitor the progress of a build, you can go to the Step Function AWS console to see the state transitions and current state of the build. You can also view the logs of the Step Function and the associated Lambda Functions.\n\n## CI/CD with CDK Pipelines\n\nTo create a CI/CD pipeline, we suggest you use the [CDK Pipeline library](https://docs.aws.amazon.com/cdk/api/latest/docs/pipelines-readme.html). With CDK Pipelines, it’s as straightforward to deploy to a different account and Region as it is to deploy to the same account.\n\n### Initial GitHub setup\n\nTo enable your CI/CD pipeline to react to GitHub changes, we need to create an oauth token in AWS Secrets. For creating an oauth token please follow this [GitHub tutorial](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token). Ensure the oauth token has `repo` and `admin:repo_hook` scopes.\n\nAfter creating your token, create an AWS secret with the name `github-token` (You may specify another name if you wish) in the region you want to deploy your pipeline(s). You can use the AWS console or the AWS CLI to do this.\n\n```shell\naws create-secret --name github-token --secret-string <oauth-token>\n```\n\n### Creating a Pipeline Stack\n\n```ts\n// src/pipeline.ts\nimport * as cdk from \"@aws-cdk/core\";\nimport * as pipelines from \"@aws-cdk/pipelines\";\n\ninterface PipelineProps extends cdk.StackProps {\n  branch: string;\n}\n\nexport class Pipeline extends cdk.Stack {\n  public readonly pipeline: pipelines.CodePipeline;\n\n  constructor(scope: cdk.Construct, id: string, props: PipelineProps) {\n    super(scope, id, props);\n\n    // Connects your pipeline to GitHub to listen for changes.\n    const source = pipelines.CodePipelineSource.gitHub(\n      \"<Your-Name>/<Your-Repo>\",\n      props.branch\n    );\n\n    this.pipeline = new pipelines.CodePipeline(this, \"Pipeline\", {\n      synth: new pipelines.ShellStep(\"Synth\", {\n        input: source,\n        commands: [\"npm i\", \"npm run build\", \"npx cdk synth\"],\n      }),\n      dockerEnabledForSynth: true, // allows for bundling of assets\n      selfMutation: true, // allows the pipeline to change itself\n    });\n  }\n}\n```\n\nYou may also use the `ask.Pipeline` Stack which provides the same functionality:\n\n```ts\nconst myPipeline = new ask.Pipeline(app, \"SkillPipeline\", {\n  owner: \"<github-account-name>\",\n  repo: \"<repo-name>\",\n  branch: \"<branch>\",\n  vendorId: \"<vendor-id>\"\n});\n\n// access the underlying CDK CodePipeline using the pipeline property\nmyPipeline.pipeline.addStage(...);\n```\n\n### Creating the Skill Stage\n\nFirst we need to create a Stage which wraps our `MySkillStack`. This represents an entire skill application. Doing this lets us easily deploy our skill to different stages in a pipeline.\n\n```ts\n// src/skill.ts\nexport interface SkillStageProps extends cdk.StageProps {\n  skillName: string;\n}\n\nexport class SkillStage extends cdk.Stage {\n  public readonly skillStack: MySkillStack;\n\n  constructor(scope: cdk.Construct, id: string, props: SkillStageProps) {\n    super(scope, id, props);\n\n    this.skillStack = new MySkillStack(this, props.skillName, {\n      env: props.env,\n      skillName: props.skillName,\n    });\n  }\n}\n```\n\n### Instantiating the Pipeline\n\nCreate your pipeline by setting the environment you want it created in and by choosing a GitHub branch you want to listen to updates for. For example, we can create a pipeline for managing the `main` (or your choice) branch of our GitHub repo.\n\n```ts\nimport * as cdk from \"@aws-cdk/core\";\nimport * as ask from \"@alexa/ask-cdk\";\n\nimport { SkillStage } from \"./skill\";\nimport { Pipeline } from \"./pipeline\";\n\nconst app = new cdk.App();\n\nconst devEnvironment = {\n  account: \"<dev-aws-account>\",\n  region: \"<dev-aws-region>\",\n};\n\nconst prodEnvironment = {\n  account: \"<prod-aws-account>\",\n  region: \"<prod-aws-region>\",\n};\n\nconst pipeline = new Pipeline(app, \"Pipeline\", {\n  branch: \"main\", // connects your pipeline to the main branch\n  env: devEnvironment, // a different environment can be used\n});\n\n// Your development skill\nconst devSkill = new SkillStage(devPipeline, \"Development\", {\n  skillName: \"<your-dev-skill-name>\",\n  env: devEnvironment,\n});\nconst devStage = devPipeline.pipeline.addStage(mySkill);\n\n// Your production skill\nconst prodSkill = new SkillStage(prodPipeline, \"Production\", {\n  skillName: \"<prod-skill-name>\",\n  env: prodEnvironment,\n});\nconst prodStage = prodPipeline.pipeline.addStage(prodSkill);\nprodStage.addPre(new pipelines.ManualApprovalStep(\"ManualApproval\"));\n```\n\nTo handle certifications and publications [#695](https://github.com/alexa/ask-ac/issues/695), you currently need to use the [Alexa Developer Console](https://developer.amazon.com/alexa/console/ask).\n\n### Deploying a Pipeline\n\nWhen deploying your pipeline(s) for the first time, we must ensure our repo has the latest updates before running `cdk deploy`.\n\n```shell\ngit add .\ngit commit -m \"creating our first skill pipeline\"\ngit push\nnpx cdk deploy\n```\n\nOur pipeline will be created and configure itself to listen for GitHub changes. We now can rely on the GitHub `push` command to make changes and the pipeline will self mutate and deploy changes.\n\n### Local development\n\nOften when working in teams, developers want to use their own lambda for testing. The CDK makes this easier than ever. Simply add the following code into your source code.\n\n```ts\nnew SkillStage(app, \"Dev\", {\n  env: {\n    account: process.env.CDK_DEFAULT_ACCOUNT,\n    region: process.env.CDK_DEFAULT_REGION,\n  },\n  skillName: \"<skill-name>\",\n});\n```\n\nNow developers can simply run `npx cdk deploy Dev/*` and this will deploy a skill and lambda to the account currently configured with the CDK CLI. Developers will have an entire skill replica/lambda created in their personal AWS account.\n\n```shell\nnpx cdk deploy Dev/*\n```\n\nNote: If you don't want your vendor account to be overcrowded with skills, you can make the `vendor-id` prop in `MySkillStack` dynamic and allow developers to pass in their personal `vendor-id` so the skill gets deployed to their vendor profile.\n\n## Pipeline simulation testing\n\nTo ensure your skill is working properly, you can add a Validation Step that provides End-to-End testing by using the [SMAPI Simulation API](https://developer.amazon.com/en-US/docs/alexa/smapi/skill-simulation-api.html). To use this action, simply create an array of type `ask.Simulation` and provide it to `ask.SimulationStep`.\n\n```ts\n// some file where you want to generate simulations\nimport { Simulation, Locale } as ask from \"@alexa/ask-cdk\";\n\n// Generate your simulations however you like. These may be dynamic values.\nexport const simulations: Simulation[] = [\n    {\n      utterance: \"<your-utterance>\",\n      expect: [\"possible-alexa-response\", \"possible-en-US-alexa-response-two\"],\n      newSession: true, // default value: false\n      locale: Locale.EN_US // default value: Locale.en_US\n    },\n    ...\n]\n```\n\nNow you can add add a new `SimulationStep` to your pipeline.\n\n```ts\n//src/app.ts\nimport { simulations } from \"../simulations.ts\"\n\n...\nmyStage.addPost(new ask.SimulationStep(\"StageSimulations\", {\n  skillId: \"<your-skill-id>\"\n  secret: mySecret, // you may also pass a vendorId or secretName instead.\n  simulations: simulations,\n}));\n```\n\nThis Step creates a temporary JSON file and uploads it to s3. After the pipeline deploys your CFN stacks, it will then invoke a custom lambda function that calls the SMAPI simulation API with your test cases. Detailed logs of the tests are provided in CloudWatch, and any failure of your required tests will block the pipeline from continuing.\n\nNote: When creating skills with the CDK, we often have multiple skills within a vendor account. For example, you may have a `development` skill, `beta` skill, and a `production` skill. Due to a limitation with [Alexa NLU](https://developer.amazon.com/en-US/alexa/alexa-skills-kit/nlu), having multiple skills with the same invocation name may cause the simulations to not recognize your skill intents. To fix this, you can provide a skillInvocationPrefix to the `Skill` construct which prepends the prefix to each interaction model invocation name at deployment time. This allows you to test the different stages of your skill by each stage using it's own invocation prefix.\n\nFor example, if we had a `beta` skill within our pipeline we could do this:\n\n```ts\nnew ask.Skill(this, \"MySkill\", {\n  ...\n  // prepends \"beta \" to each interaction model's invocation name\n  skillInvocationPrefix: \"beta \",  // \"hello world\" invocation name is now \"beta hello world\"\n});\n\n// when generating your simulations, be sure that invocation simulations use the prefix\nconst betaSimulations: Simulation[] = [\n    {\n      // The en-US interaction model invocation name that is originally \"hello world\"\n      utterance: \"open beta hello world\",\n      expect: [\"possible-en-US-alexa-response\", \"possible-en-US-alexa-response-two\"],\n      locale: Locale.en_US,\n      newSession: true\n    },\n    {\n      // The fr-FR interaction model invocation name that is originally \"bonjur world\"\n      utterance: \"ouvert beta bonjur monde\", // test a different locale\n      expect: [\"possible-fr-FR-locale-response\"],\n      locale: Locale.fr_FR,\n      newSession: true\n    }\n]\n```\n\n## Testing with Jest\n\nWhen developing with CDK you may want to test your local skill, or the team's development skill. `ask-cdk` provides a `SimulationClient`([#703](https://github.com/alexa/ask-ac/issues/703)) that allows you to easily run simulations against your skill. This client can easily be integrated with [jest](https://jestjs.io) or any testing library you prefer.\n\nWith jest, we can test components of our skill by using `describe` blocks. This allows us to break our test suite into multiple components that will run sequentially. In the snippet below, once the `en-US locale` tests are complete, it will begin running the `en-CA locale` tests. Note that the [SMAPI Simulation API](https://developer.amazon.com/en-US/docs/alexa/smapi/skill-simulation-api.html) doesn't allow for concurrent requests per user, so you MUST run these tests sequentially.\n\nBy default, jest tests your files in parallel, which is not supported for alexa simulations. In order to fix this, you can add the `--runInBand` option to your `package.json` testing command which will run the tests serially. Please visit [jestjs.io/docs](https://jestjs.io/docs/cli#options) for viewing other options you can set. You may also set the `maxWorkers` property in your `jest.config.js` setup which has the same effect.\n\n```ts\nmodule.exports = {\n  preset: \"ts-jest\",\n  testEnvironment: \"node\",\n  maxWorkers: 1,\n};\n```\n\nRunning jest simulation tests in the pipeline is not currently supported [#696](https://github.com/alexa/ask-ac/issues/696). See [Epic Testing](https://github.com/alexa/ask-ac/issues/51) for future testing options.\n\n```ts\nimport { getTestingClient } from \"@alexa/ask-cdk\";\n\n// set a custom jest timeout duration\njest.setTimeout(10000);\n\nconst client = getTestingClient(\"<skill-id-to-test>\", \"<cli-profile>\");\n// Any stage specific invocation prefix.\nconst prefix = \"<my-prefix>\";\n\n// testing the en-US\ndescribe(\"en-US locale\", () => {\n  test(\"the skill is invoked\", async () => {\n    const alexaResponse = await client.simulate(\n      `open ${prefix} <my en-US invocation>`,\n      \"FORCE_NEW_SESSION\"\n    );\n    expect(alexaResponse).toContain(\n      \"Welcome, you can say Hello or Help. Which would you like to try?\"\n    );\n  });\n\n  test(\"hello\", async () => {\n    const alexaResponse = await client.simulate(\"help\");\n    expect(alexaResponse).toContain(\"Hello World!\");\n  });\n});\n\ndescribe(\"en-CA locale\", () => {\n  test(\"the en-CA locale is invoked\", async () => {\n    const alexaResponse = await client.simulate(\n      `open ${prefix} <my en-CA invocation>`,\n      \"FORCE_NEW_SESSION\",\n      \"en-CA\"\n    );\n    expect(alexaResponse).toContain(\n      \"Welcome, you can say Hello or Help. Which would you like to try?\"\n    );\n  });\n});\n```\n","readmeFilename":"README.md"}