From c6f014983449a92c396156c8d1ef10570f413c57 Mon Sep 17 00:00:00 2001 From: Kendra Neil <53584728+TheRealAmazonKendra@users.noreply.github.com> Date: Fri, 3 Mar 2023 10:33:57 -0800 Subject: [PATCH] chore(ssm): update integ tests to use IntegTest construct (#24405) clean up clean up clean up ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ssm/README.md | 21 +- .../index.js | 530 +++++++- .../index.js | 669 --------- .../base.assets.json | 2 +- .../integ.list-parameter.js.snapshot/cdk.out | 2 +- .../integ.json | 2 +- .../list-param.assets.json | 2 +- .../manifest.json | 4 +- ...efaultTestDeployAssert9C612E37.assets.json | 12 +- ...aultTestDeployAssert9C612E37.template.json | 4 +- .../integ.parameter-arns.js.snapshot/cdk.out | 2 +- ...efaultTestDeployAssertE4B86A49.assets.json | 19 + ...aultTestDeployAssertE4B86A49.template.json | 36 + .../integ-parameter-arns.assets.json | 2 +- .../integ.json | 12 +- .../manifest.json | 61 +- .../tree.json | 128 +- .../aws-ssm/test/integ.parameter-arns.ts | 6 +- .../index.js | 1204 +++++++++++++++++ .../cdk.out | 2 +- .../integ.json | 2 +- .../manifest.json | 18 +- .../sspms-cleanup.assets.json | 12 +- .../sspms-cleanup.template.json | 4 +- .../sspms-creating.assets.json | 12 +- .../sspms-creating.template.json | 4 +- .../sspms-using.assets.json | 2 +- .../tree.json | 74 +- .../test/integ.parameter-store-string.ts | 2 - .../SSM-Parameter.assets.json | 2 +- .../SSM-Parameter.template.json | 0 .../test/integ.parameter.js.snapshot/cdk.out | 1 + ...efaultTestDeployAssert8D247A87.assets.json | 19 + ...aultTestDeployAssert8D247A87.template.json | 36 + .../integ.parameter.js.snapshot/integ.json | 12 + .../manifest.json | 61 +- .../tree.json | 108 +- .../integ.parameter.lit.js.snapshot/cdk.out | 1 - .../integ.json | 14 - ...eg.parameter.lit.ts => integ.parameter.ts} | 16 +- .../@aws-cdk/cli-lib/THIRD_PARTY_LICENSES | 10 +- 41 files changed, 2254 insertions(+), 876 deletions(-) rename packages/@aws-cdk/aws-ssm/test/{integ.parameter-store-string.js.snapshot/asset.b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.bundle => integ.list-parameter.js.snapshot/asset.1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.bundle}/index.js (59%) delete mode 100644 packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/asset.d47f7e6772bfdf47ecbc070ffe204baf53bacbfbf7814eb407bd8ea108c1c1bb.bundle/index.js create mode 100644 packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.assets.json create mode 100644 packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.template.json create mode 100644 packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/asset.1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.bundle/index.js rename packages/@aws-cdk/aws-ssm/test/{integ.parameter.lit.js.snapshot => integ.parameter.js.snapshot}/SSM-Parameter.assets.json (96%) rename packages/@aws-cdk/aws-ssm/test/{integ.parameter.lit.js.snapshot => integ.parameter.js.snapshot}/SSM-Parameter.template.json (100%) create mode 100644 packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdkintegssmparameterDefaultTestDeployAssert8D247A87.assets.json create mode 100644 packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdkintegssmparameterDefaultTestDeployAssert8D247A87.template.json create mode 100644 packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/integ.json rename packages/@aws-cdk/aws-ssm/test/{integ.parameter.lit.js.snapshot => integ.parameter.js.snapshot}/manifest.json (57%) rename packages/@aws-cdk/aws-ssm/test/{integ.parameter.lit.js.snapshot => integ.parameter.js.snapshot}/tree.json (69%) delete mode 100644 packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/cdk.out delete mode 100644 packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/integ.json rename packages/@aws-cdk/aws-ssm/test/{integ.parameter.lit.ts => integ.parameter.ts} (67%) diff --git a/packages/@aws-cdk/aws-ssm/README.md b/packages/@aws-cdk/aws-ssm/README.md index 7c8fdbb9bd003..477403d78224a 100644 --- a/packages/@aws-cdk/aws-ssm/README.md +++ b/packages/@aws-cdk/aws-ssm/README.md @@ -125,7 +125,26 @@ new ssm.StringParameter(this, 'Parameter', { }); ``` -[creating SSM parameters](test/integ.parameter.lit.ts) +```ts +// Create a new SSM Parameter holding a String +const param = new ssm.StringParameter(stack, 'StringParameter', { + // description: 'Some user-friendly description', + // name: 'ParameterName', + stringValue: 'Initial parameter value', + // allowedPattern: '.*', +}); + +// Grant read access to some Role +param.grantRead(role); + +// Create a new SSM Parameter holding a StringList +const listParameter = new ssm.StringListParameter(stack, 'StringListParameter', { + // description: 'Some user-friendly description', + // name: 'ParameterName', + stringListValue: ['Initial parameter value A', 'Initial parameter value B'], + // allowedPattern: '.*', +}); +``` When specifying an `allowedPattern`, the values provided as string literals are validated against the pattern and an exception is raised if a value diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/asset.b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.bundle/index.js b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/asset.1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.bundle/index.js similarity index 59% rename from packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/asset.b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.bundle/index.js rename to packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/asset.1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.bundle/index.js index 2d6c2f0e85497..4264087b9aab2 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/asset.b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.bundle/index.js +++ b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/asset.1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.bundle/index.js @@ -1,4 +1,3 @@ -"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; @@ -18,6 +17,10 @@ var __copyProps = (to, from, except, desc) => { return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); @@ -34,40 +37,83 @@ module.exports = __toCommonJS(lambda_handler_exports); // ../assertions/lib/matcher.ts var Matcher = class { + /** + * Check whether the provided object is a subtype of the `IMatcher`. + */ static isMatcher(x) { return x && x instanceof Matcher; } }; var MatchResult = class { constructor(target) { - this.failures = []; + this.failuresHere = /* @__PURE__ */ new Map(); this.captures = /* @__PURE__ */ new Map(); this.finalized = false; + this.innerMatchFailures = /* @__PURE__ */ new Map(); + this._hasFailed = false; + this._failCount = 0; + this._cost = 0; this.target = target; } + /** + * DEPRECATED + * @deprecated use recordFailure() + */ push(matcher, path, message) { return this.recordFailure({ matcher, path, message }); } + /** + * Record a new failure into this result at a specific path. + */ recordFailure(failure) { - this.failures.push(failure); + const failKey = failure.path.join("."); + let list = this.failuresHere.get(failKey); + if (!list) { + list = []; + this.failuresHere.set(failKey, list); + } + this._failCount += 1; + this._cost += failure.cost ?? 1; + list.push(failure); + this._hasFailed = true; return this; } + /** Whether the match is a success */ + get isSuccess() { + return !this._hasFailed; + } + /** Does the result contain any failures. If not, the result is a success */ hasFailed() { - return this.failures.length !== 0; + return this._hasFailed; } + /** The number of failures */ get failCount() { - return this.failures.length; + return this._failCount; } + /** The cost of the failures so far */ + get failCost() { + return this._cost; + } + /** + * Compose the results of a previous match as a subtree. + * @param id the id of the parent tree. + */ compose(id, inner) { - const innerF = inner.failures; - this.failures.push(...innerF.map((f) => { - return { path: [id, ...f.path], message: f.message, matcher: f.matcher }; - })); + if (inner.hasFailed()) { + this._hasFailed = true; + this._failCount += inner.failCount; + this._cost += inner._cost; + this.innerMatchFailures.set(id, inner); + } inner.captures.forEach((vals, capture) => { vals.forEach((value) => this.recordCapture({ capture, value })); }); return this; } + /** + * Prepare the result to be analyzed. + * This API *must* be called prior to analyzing these results. + */ finished() { if (this.finalized) { return this; @@ -78,12 +124,169 @@ var MatchResult = class { this.finalized = true; return this; } + /** + * Render the failed match in a presentable way + * + * Prefer using `renderMismatch` over this method. It is left for backwards + * compatibility for test suites that expect it, but `renderMismatch()` will + * produce better output. + */ toHumanStrings() { - return this.failures.map((r) => { - const loc = r.path.length === 0 ? "" : ` at ${r.path.join("")}`; + const failures = new Array(); + debugger; + recurse(this, []); + return failures.map((r) => { + const loc = r.path.length === 0 ? "" : ` at /${r.path.join("/")}`; return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; }); + function recurse(x, prefix) { + for (const fail of Array.from(x.failuresHere.values()).flat()) { + failures.push({ + matcher: fail.matcher, + message: fail.message, + path: [...prefix, ...fail.path] + }); + } + for (const [key, inner] of x.innerMatchFailures.entries()) { + recurse(inner, [...prefix, key]); + } + } } + /** + * Do a deep render of the match result, showing the structure mismatches in context + */ + renderMismatch() { + if (!this.hasFailed()) { + return ""; + } + const parts = new Array(); + const indents = new Array(); + emitFailures(this, ""); + recurse(this); + return moveMarkersToFront(parts.join("").trimEnd()); + function emit(x) { + if (x === void 0) { + debugger; + } + parts.push(x.replace(/\n/g, ` +${indents.join("")}`)); + } + function emitFailures(r, path, scrapSet) { + for (const fail of r.failuresHere.get(path) ?? []) { + emit(`!! ${fail.message} +`); + } + scrapSet == null ? void 0 : scrapSet.delete(path); + } + function recurse(r) { + const remainingFailures = new Set(Array.from(r.failuresHere.keys()).filter((x) => x !== "")); + if (Array.isArray(r.target)) { + indents.push(" "); + emit("[\n"); + for (const [first, i] of enumFirst(range(r.target.length))) { + if (!first) { + emit(",\n"); + } + emitFailures(r, `${i}`, remainingFailures); + const innerMatcher = r.innerMatchFailures.get(`${i}`); + if (innerMatcher) { + emitFailures(innerMatcher, ""); + recurseComparingValues(innerMatcher, r.target[i]); + } else { + emit(renderAbridged(r.target[i])); + } + } + emitRemaining(); + indents.pop(); + emit("\n]"); + return; + } + if (r.target && typeof r.target === "object") { + indents.push(" "); + emit("{\n"); + const keys = Array.from(/* @__PURE__ */ new Set([ + ...Object.keys(r.target), + ...Array.from(remainingFailures) + ])).sort(); + for (const [first, key] of enumFirst(keys)) { + if (!first) { + emit(",\n"); + } + emitFailures(r, key, remainingFailures); + const innerMatcher = r.innerMatchFailures.get(key); + if (innerMatcher) { + emitFailures(innerMatcher, ""); + emit(`${jsonify(key)}: `); + recurseComparingValues(innerMatcher, r.target[key]); + } else { + emit(`${jsonify(key)}: `); + emit(renderAbridged(r.target[key])); + } + } + emitRemaining(); + indents.pop(); + emit("\n}"); + return; + } + emitRemaining(); + emit(jsonify(r.target)); + function emitRemaining() { + if (remainingFailures.size > 0) { + emit("\n"); + } + for (const key of remainingFailures) { + emitFailures(r, key); + } + } + } + function recurseComparingValues(inner, actualValue) { + if (inner.target === actualValue) { + return recurse(inner); + } + emit(renderAbridged(actualValue)); + emit(" <*> "); + recurse(inner); + } + function renderAbridged(x) { + if (Array.isArray(x)) { + switch (x.length) { + case 0: + return "[]"; + case 1: + return `[ ${renderAbridged(x[0])} ]`; + case 2: + if (x.every((e) => ["number", "boolean", "string"].includes(typeof e))) { + return `[ ${x.map(renderAbridged).join(", ")} ]`; + } + return "[ ... ]"; + default: + return "[ ... ]"; + } + } + if (x && typeof x === "object") { + const keys = Object.keys(x); + switch (keys.length) { + case 0: + return "{}"; + case 1: + return `{ ${JSON.stringify(keys[0])}: ${renderAbridged(x[keys[0]])} }`; + default: + return "{ ... }"; + } + } + return jsonify(x); + } + function jsonify(x) { + return JSON.stringify(x) ?? "undefined"; + } + function moveMarkersToFront(x) { + const re = /^(\s+)!!/gm; + return x.replace(re, (_, spaces) => `!!${spaces.substring(0, spaces.length - 2)}`); + } + } + /** + * Record a capture against in this match result. + */ recordCapture(options) { let values = this.captures.get(options.capture); if (values === void 0) { @@ -93,6 +296,18 @@ var MatchResult = class { this.captures.set(options.capture, values); } }; +function* range(n) { + for (let i = 0; i < n; i++) { + yield i; + } +} +function* enumFirst(xs) { + let first = true; + for (const x of xs) { + yield [first, x]; + first = false; + } +} // ../assertions/lib/private/matchers/absent.ts var AbsentMatch = class extends Matcher { @@ -113,6 +328,51 @@ var AbsentMatch = class extends Matcher { } }; +// ../assertions/lib/private/sorting.ts +function sortKeyComparator(keyFn) { + return (a, b) => { + const ak = keyFn(a); + const bk = keyFn(b); + for (let i = 0; i < ak.length && i < bk.length; i++) { + const av = ak[i]; + const bv = bk[i]; + let diff = 0; + if (typeof av === "number" && typeof bv === "number") { + diff = av - bv; + } else if (typeof av === "string" && typeof bv === "string") { + diff = av.localeCompare(bv); + } + if (diff !== 0) { + return diff; + } + } + return bk.length - ak.length; + }; +} + +// ../assertions/lib/private/sparse-matrix.ts +var SparseMatrix = class { + constructor() { + this.matrix = /* @__PURE__ */ new Map(); + } + get(row, col) { + var _a; + return (_a = this.matrix.get(row)) == null ? void 0 : _a.get(col); + } + row(row) { + var _a; + return Array.from(((_a = this.matrix.get(row)) == null ? void 0 : _a.entries()) ?? []); + } + set(row, col, value) { + let r = this.matrix.get(row); + if (!r) { + r = /* @__PURE__ */ new Map(); + this.matrix.set(row, r); + } + r.set(col, value); + } +}; + // ../assertions/lib/private/type.ts function getType(obj) { return Array.isArray(obj) ? "array" : typeof obj; @@ -120,33 +380,74 @@ function getType(obj) { // ../assertions/lib/match.ts var Match = class { + /** + * Use this matcher in the place of a field's value, if the field must not be present. + */ static absent() { return new AbsentMatch("absent"); } + /** + * Matches the specified pattern with the array found in the same relative path of the target. + * The set of elements (or matchers) must be in the same order as would be found. + * @param pattern the pattern to match + */ static arrayWith(pattern) { return new ArrayMatch("arrayWith", pattern); } + /** + * Matches the specified pattern with the array found in the same relative path of the target. + * The set of elements (or matchers) must match exactly and in order. + * @param pattern the pattern to match + */ static arrayEquals(pattern) { return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); } + /** + * Deep exact matching of the specified pattern to the target. + * @param pattern the pattern to match + */ static exact(pattern) { return new LiteralMatch("exact", pattern, { partialObjects: false }); } + /** + * Matches the specified pattern to an object found in the same relative path of the target. + * The keys and their values (or matchers) must be present in the target but the target can be a superset. + * @param pattern the pattern to match + */ static objectLike(pattern) { return new ObjectMatch("objectLike", pattern); } + /** + * Matches the specified pattern to an object found in the same relative path of the target. + * The keys and their values (or matchers) must match exactly with the target. + * @param pattern the pattern to match + */ static objectEquals(pattern) { return new ObjectMatch("objectEquals", pattern, { partial: false }); } + /** + * Matches any target which does NOT follow the specified pattern. + * @param pattern the pattern to NOT match + */ static not(pattern) { return new NotMatch("not", pattern); } + /** + * Matches any string-encoded JSON and applies the specified pattern after parsing it. + * @param pattern the pattern to match after parsing the encoded JSON. + */ static serializedJson(pattern) { return new SerializedJson("serializedJson", pattern); } + /** + * Matches any non-null value at the target. + */ static anyValue() { return new AnyMatch("anyValue"); } + /** + * Matches targets according to a regular expression + */ static stringLikeRegexp(pattern) { return new StringLikeRegexpMatch("stringLikeRegexp", pattern); } @@ -203,40 +504,87 @@ var ArrayMatch = class extends Matcher { message: `Expected type array but received ${getType(actual)}` }); } - if (!this.subsequence && this.pattern.length !== actual.length) { - return new MatchResult(actual).recordFailure({ + return this.subsequence ? this.testSubsequence(actual) : this.testFullArray(actual); + } + testFullArray(actual) { + const result = new MatchResult(actual); + let i = 0; + for (; i < this.pattern.length && i < actual.length; i++) { + const patternElement = this.pattern[i]; + const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const innerResult = matcher.test(actual[i]); + result.compose(`${i}`, innerResult); + } + if (i < this.pattern.length) { + result.recordFailure({ matcher: this, - path: [], - message: `Expected array of length ${this.pattern.length} but received ${actual.length}` + message: `Not enough elements in array (expecting ${this.pattern.length}, got ${actual.length})`, + path: [`${i}`] + }); + } + if (i < actual.length) { + result.recordFailure({ + matcher: this, + message: `Too many elements in array (expecting ${this.pattern.length}, got ${actual.length})`, + path: [`${i}`] }); } + return result; + } + testSubsequence(actual) { + const result = new MatchResult(actual); let patternIdx = 0; let actualIdx = 0; - const result = new MatchResult(actual); + const matches = new SparseMatrix(); while (patternIdx < this.pattern.length && actualIdx < actual.length) { const patternElement = this.pattern[patternIdx]; const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); const matcherName = matcher.name; - if (this.subsequence && (matcherName == "absent" || matcherName == "anyValue")) { + if (matcherName == "absent" || matcherName == "anyValue") { throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); } const innerResult = matcher.test(actual[actualIdx]); - if (!this.subsequence || !innerResult.hasFailed()) { - result.compose(`[${actualIdx}]`, innerResult); + matches.set(patternIdx, actualIdx, innerResult); + actualIdx++; + if (innerResult.isSuccess) { + result.compose(`${actualIdx}`, innerResult); patternIdx++; - actualIdx++; - } else { - actualIdx++; } } - for (; patternIdx < this.pattern.length; patternIdx++) { - const pattern = this.pattern[patternIdx]; - const element = Matcher.isMatcher(pattern) || typeof pattern === "object" ? " " : ` [${pattern}] `; - result.recordFailure({ - matcher: this, - path: [], - message: `Missing element${element}at pattern index ${patternIdx}` - }); + if (patternIdx < this.pattern.length) { + for (let spi = 0; spi < patternIdx; spi++) { + const foundMatch = matches.row(spi).find(([, r]) => r.isSuccess); + if (!foundMatch) { + continue; + } + const [index] = foundMatch; + result.compose(`${index}`, new MatchResult(actual[index]).recordFailure({ + matcher: this, + message: `arrayWith pattern ${spi} matched here`, + path: [], + cost: 0 + // This is an informational message so it would be unfair to assign it cost + })); + } + const failedMatches = matches.row(patternIdx); + failedMatches.sort(sortKeyComparator(([i, r]) => [r.failCost, i])); + if (failedMatches.length > 0) { + const [index, innerResult] = failedMatches[0]; + result.recordFailure({ + matcher: this, + message: `Could not match arrayWith pattern ${patternIdx}. This is the closest match`, + path: [`${index}`], + cost: 0 + // Informational message + }); + result.compose(`${index}`, innerResult); + } else { + result.recordFailure({ + matcher: this, + message: `Could not match arrayWith pattern ${patternIdx}. No more elements to try`, + path: [`${actual.length}`] + }); + } } return result; } @@ -262,8 +610,8 @@ var ObjectMatch = class extends Matcher { if (!(a in this.pattern)) { result.recordFailure({ matcher: this, - path: [`/${a}`], - message: "Unexpected key" + path: [a], + message: `Unexpected key ${a}` }); } } @@ -272,14 +620,14 @@ var ObjectMatch = class extends Matcher { if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { result.recordFailure({ matcher: this, - path: [`/${patternKey}`], - message: `Missing key '${patternKey}' among {${Object.keys(actual).join(",")}}` + path: [patternKey], + message: `Missing key '${patternKey}'` }); continue; } const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); const inner = matcher.test(actual[patternKey]); - result.compose(`/${patternKey}`, inner); + result.compose(patternKey, inner); } return result; } @@ -291,34 +639,37 @@ var SerializedJson = class extends Matcher { this.pattern = pattern; } test(actual) { - const result = new MatchResult(actual); if (getType(actual) !== "string") { - result.recordFailure({ + return new MatchResult(actual).recordFailure({ matcher: this, path: [], message: `Expected JSON as a string but found ${getType(actual)}` }); - return result; } let parsed; try { parsed = JSON.parse(actual); } catch (err) { if (err instanceof SyntaxError) { - result.recordFailure({ + return new MatchResult(actual).recordFailure({ matcher: this, path: [], message: `Invalid JSON string: ${actual}` }); - return result; } else { throw err; } } const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); const innerResult = matcher.test(parsed); - result.compose(`(${this.name})`, innerResult); - return result; + if (innerResult.hasFailed()) { + innerResult.recordFailure({ + matcher: this, + path: [], + message: "Encoded JSON value does not match" + }); + } + return innerResult; } }; var NotMatch = class extends Matcher { @@ -405,6 +756,10 @@ var CustomResourceHandler = class { this.event = event; this.physicalResourceId = extractPhysicalResourceId(event); } + /** + * Handles executing the custom resource event. If `stateMachineArn` is present + * in the props then trigger the waiter statemachine + */ async handle() { try { if ("stateMachineArn" in this.event.ResourceProperties) { @@ -426,6 +781,9 @@ var CustomResourceHandler = class { clearTimeout(this.timeout); } } + /** + * Handle async requests from the waiter state machine + */ async handleIsComplete() { try { const result = await this.processEvent(this.event.ResourceProperties); @@ -437,6 +795,10 @@ var CustomResourceHandler = class { clearTimeout(this.timeout); } } + /** + * Start a step function state machine which will wait for the request + * to be successful. + */ async startExecution(req) { try { const sfn = new AWS.StepFunctions(); @@ -507,10 +869,7 @@ var AssertionHandler = class extends CustomResourceHandler { failed: true, assertion: JSON.stringify({ status: "fail", - message: [ - ...matchResult.toHumanStrings(), - JSON.stringify(matchResult.target, void 0, 2) - ].join("\n") + message: matchResult.renderMismatch() }) }; if (request2.failDeployment) { @@ -532,6 +891,65 @@ var MatchCreator = class { matcher: obj }; } + /** + * Return a Matcher that can be tested against the actual results. + * This will convert the encoded matchers into their corresponding + * assertions matcher. + * + * For example: + * + * ExpectedResult.objectLike({ + * Messages: [{ + * Body: Match.objectLike({ + * Elements: Match.arrayWith([{ Asdf: 3 }]), + * Payload: Match.serializedJson({ key: 'value' }), + * }), + * }], + * }); + * + * Will be encoded as: + * { + * $ObjectLike: { + * Messages: [{ + * Body: { + * $ObjectLike: { + * Elements: { + * $ArrayWith: [{ Asdf: 3 }], + * }, + * Payload: { + * $SerializedJson: { key: 'value' } + * } + * }, + * }, + * }], + * }, + * } + * + * Which can then be parsed by this function. For each key (recursively) + * the parser will check if the value has one of the encoded matchers as a key + * and if so, it will set the value as the Matcher. So, + * + * { + * Body: { + * $ObjectLike: { + * Elements: { + * $ArrayWith: [{ Asdf: 3 }], + * }, + * Payload: { + * $SerializedJson: { key: 'value' } + * } + * }, + * }, + * } + * + * Will be converted to + * { + * Body: Match.objectLike({ + * Elements: Match.arrayWith([{ Asdf: 3 }]), + * Payload: Match.serializedJson({ key: 'value' }), + * }), + * } + */ getMatcher() { try { const final = JSON.parse(JSON.stringify(this.parsedObj), function(_k, v) { @@ -543,6 +961,8 @@ var MatchCreator = class { return Match.objectLike(v[nested]); case "$StringLike": return Match.stringLikeRegexp(v[nested]); + case "$SerializedJson": + return Match.serializedJson(v[nested]); default: return v; } @@ -614,11 +1034,26 @@ var AwsApiCallHandler = class extends CustomResourceHandler { const flatData = { ...flatten(respond) }; - const resp = request2.flattenResponse === "true" ? flatData : respond; + let resp = respond; + if (request2.outputPaths) { + resp = filterKeys(flatData, request2.outputPaths); + } else if (request2.flattenResponse === "true") { + resp = flatData; + } console.log(`Returning result ${JSON.stringify(resp)}`); return resp; } }; +function filterKeys(object, searchStrings) { + return Object.entries(object).reduce((filteredObject, [key, value]) => { + for (const searchString of searchStrings) { + if (key.startsWith(`apiCallResponse.${searchString}`)) { + filteredObject[key] = value; + } + } + return filteredObject; + }, {}); +} function isJsonString(value) { try { return JSON.parse(value); @@ -664,6 +1099,7 @@ async function handler(event, context) { await provider.respond({ status: "SUCCESS", reason: "OK", + // return both the result of the API call _and_ the assertion results data: { ...assertionResult, ...result diff --git a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/asset.d47f7e6772bfdf47ecbc070ffe204baf53bacbfbf7814eb407bd8ea108c1c1bb.bundle/index.js b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/asset.d47f7e6772bfdf47ecbc070ffe204baf53bacbfbf7814eb407bd8ea108c1c1bb.bundle/index.js deleted file mode 100644 index a9e7e7241efc7..0000000000000 --- a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/asset.d47f7e6772bfdf47ecbc070ffe204baf53bacbfbf7814eb407bd8ea108c1c1bb.bundle/index.js +++ /dev/null @@ -1,669 +0,0 @@ -"use strict"; -var __create = Object.create; -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __getProtoOf = Object.getPrototypeOf; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( - isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, - mod -)); -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// lib/assertions/providers/lambda-handler/index.ts -var lambda_handler_exports = {}; -__export(lambda_handler_exports, { - handler: () => handler -}); -module.exports = __toCommonJS(lambda_handler_exports); - -// ../assertions/lib/matcher.ts -var Matcher = class { - static isMatcher(x) { - return x && x instanceof Matcher; - } -}; -var MatchResult = class { - constructor(target) { - this.failures = []; - this.captures = /* @__PURE__ */ new Map(); - this.finalized = false; - this.target = target; - } - push(matcher, path, message) { - return this.recordFailure({ matcher, path, message }); - } - recordFailure(failure) { - this.failures.push(failure); - return this; - } - hasFailed() { - return this.failures.length !== 0; - } - get failCount() { - return this.failures.length; - } - compose(id, inner) { - const innerF = inner.failures; - this.failures.push(...innerF.map((f) => { - return { path: [id, ...f.path], message: f.message, matcher: f.matcher }; - })); - inner.captures.forEach((vals, capture) => { - vals.forEach((value) => this.recordCapture({ capture, value })); - }); - return this; - } - finished() { - if (this.finalized) { - return this; - } - if (this.failCount === 0) { - this.captures.forEach((vals, cap) => cap._captured.push(...vals)); - } - this.finalized = true; - return this; - } - toHumanStrings() { - return this.failures.map((r) => { - const loc = r.path.length === 0 ? "" : ` at ${r.path.join("")}`; - return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; - }); - } - recordCapture(options) { - let values = this.captures.get(options.capture); - if (values === void 0) { - values = []; - } - values.push(options.value); - this.captures.set(options.capture, values); - } -}; - -// ../assertions/lib/private/matchers/absent.ts -var AbsentMatch = class extends Matcher { - constructor(name) { - super(); - this.name = name; - } - test(actual) { - const result = new MatchResult(actual); - if (actual !== void 0) { - result.recordFailure({ - matcher: this, - path: [], - message: `Received ${actual}, but key should be absent` - }); - } - return result; - } -}; - -// ../assertions/lib/private/type.ts -function getType(obj) { - return Array.isArray(obj) ? "array" : typeof obj; -} - -// ../assertions/lib/match.ts -var Match = class { - static absent() { - return new AbsentMatch("absent"); - } - static arrayWith(pattern) { - return new ArrayMatch("arrayWith", pattern); - } - static arrayEquals(pattern) { - return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); - } - static exact(pattern) { - return new LiteralMatch("exact", pattern, { partialObjects: false }); - } - static objectLike(pattern) { - return new ObjectMatch("objectLike", pattern); - } - static objectEquals(pattern) { - return new ObjectMatch("objectEquals", pattern, { partial: false }); - } - static not(pattern) { - return new NotMatch("not", pattern); - } - static serializedJson(pattern) { - return new SerializedJson("serializedJson", pattern); - } - static anyValue() { - return new AnyMatch("anyValue"); - } - static stringLikeRegexp(pattern) { - return new StringLikeRegexpMatch("stringLikeRegexp", pattern); - } -}; -var LiteralMatch = class extends Matcher { - constructor(name, pattern, options = {}) { - super(); - this.name = name; - this.pattern = pattern; - this.partialObjects = options.partialObjects ?? false; - if (Matcher.isMatcher(this.pattern)) { - throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply."); - } - } - test(actual) { - if (Array.isArray(this.pattern)) { - return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); - } - if (typeof this.pattern === "object") { - return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual); - } - const result = new MatchResult(actual); - if (typeof this.pattern !== typeof actual) { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected type ${typeof this.pattern} but received ${getType(actual)}` - }); - return result; - } - if (actual !== this.pattern) { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected ${this.pattern} but received ${actual}` - }); - } - return result; - } -}; -var ArrayMatch = class extends Matcher { - constructor(name, pattern, options = {}) { - super(); - this.name = name; - this.pattern = pattern; - this.subsequence = options.subsequence ?? true; - this.partialObjects = options.partialObjects ?? false; - } - test(actual) { - if (!Array.isArray(actual)) { - return new MatchResult(actual).recordFailure({ - matcher: this, - path: [], - message: `Expected type array but received ${getType(actual)}` - }); - } - if (!this.subsequence && this.pattern.length !== actual.length) { - return new MatchResult(actual).recordFailure({ - matcher: this, - path: [], - message: `Expected array of length ${this.pattern.length} but received ${actual.length}` - }); - } - let patternIdx = 0; - let actualIdx = 0; - const result = new MatchResult(actual); - while (patternIdx < this.pattern.length && actualIdx < actual.length) { - const patternElement = this.pattern[patternIdx]; - const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); - const matcherName = matcher.name; - if (this.subsequence && (matcherName == "absent" || matcherName == "anyValue")) { - throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); - } - const innerResult = matcher.test(actual[actualIdx]); - if (!this.subsequence || !innerResult.hasFailed()) { - result.compose(`[${actualIdx}]`, innerResult); - patternIdx++; - actualIdx++; - } else { - actualIdx++; - } - } - for (; patternIdx < this.pattern.length; patternIdx++) { - const pattern = this.pattern[patternIdx]; - const element = Matcher.isMatcher(pattern) || typeof pattern === "object" ? " " : ` [${pattern}] `; - result.recordFailure({ - matcher: this, - path: [], - message: `Missing element${element}at pattern index ${patternIdx}` - }); - } - return result; - } -}; -var ObjectMatch = class extends Matcher { - constructor(name, pattern, options = {}) { - super(); - this.name = name; - this.pattern = pattern; - this.partial = options.partial ?? true; - } - test(actual) { - if (typeof actual !== "object" || Array.isArray(actual)) { - return new MatchResult(actual).recordFailure({ - matcher: this, - path: [], - message: `Expected type object but received ${getType(actual)}` - }); - } - const result = new MatchResult(actual); - if (!this.partial) { - for (const a of Object.keys(actual)) { - if (!(a in this.pattern)) { - result.recordFailure({ - matcher: this, - path: [`/${a}`], - message: "Unexpected key" - }); - } - } - } - for (const [patternKey, patternVal] of Object.entries(this.pattern)) { - if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { - result.recordFailure({ - matcher: this, - path: [`/${patternKey}`], - message: `Missing key '${patternKey}' among {${Object.keys(actual).join(",")}}` - }); - continue; - } - const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); - const inner = matcher.test(actual[patternKey]); - result.compose(`/${patternKey}`, inner); - } - return result; - } -}; -var SerializedJson = class extends Matcher { - constructor(name, pattern) { - super(); - this.name = name; - this.pattern = pattern; - } - test(actual) { - const result = new MatchResult(actual); - if (getType(actual) !== "string") { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected JSON as a string but found ${getType(actual)}` - }); - return result; - } - let parsed; - try { - parsed = JSON.parse(actual); - } catch (err) { - if (err instanceof SyntaxError) { - result.recordFailure({ - matcher: this, - path: [], - message: `Invalid JSON string: ${actual}` - }); - return result; - } else { - throw err; - } - } - const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); - const innerResult = matcher.test(parsed); - result.compose(`(${this.name})`, innerResult); - return result; - } -}; -var NotMatch = class extends Matcher { - constructor(name, pattern) { - super(); - this.name = name; - this.pattern = pattern; - } - test(actual) { - const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); - const innerResult = matcher.test(actual); - const result = new MatchResult(actual); - if (innerResult.failCount === 0) { - result.recordFailure({ - matcher: this, - path: [], - message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}` - }); - } - return result; - } -}; -var AnyMatch = class extends Matcher { - constructor(name) { - super(); - this.name = name; - } - test(actual) { - const result = new MatchResult(actual); - if (actual == null) { - result.recordFailure({ - matcher: this, - path: [], - message: "Expected a value but found none" - }); - } - return result; - } -}; -var StringLikeRegexpMatch = class extends Matcher { - constructor(name, pattern) { - super(); - this.name = name; - this.pattern = pattern; - } - test(actual) { - const result = new MatchResult(actual); - const regex = new RegExp(this.pattern, "gm"); - if (typeof actual !== "string") { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected a string, but got '${typeof actual}'` - }); - } - if (!regex.test(actual)) { - result.recordFailure({ - matcher: this, - path: [], - message: `String '${actual}' did not match pattern '${this.pattern}'` - }); - } - return result; - } -}; - -// lib/assertions/providers/lambda-handler/base.ts -var https = __toESM(require("https")); -var url = __toESM(require("url")); -var CustomResourceHandler = class { - constructor(event, context) { - this.event = event; - this.context = context; - this.timedOut = false; - this.timeout = setTimeout(async () => { - await this.respond({ - status: "FAILED", - reason: "Lambda Function Timeout", - data: this.context.logStreamName - }); - this.timedOut = true; - }, context.getRemainingTimeInMillis() - 1200); - this.event = event; - this.physicalResourceId = extractPhysicalResourceId(event); - } - async handle() { - try { - const response = await this.processEvent(this.event.ResourceProperties); - return response; - } catch (e) { - console.log(e); - throw e; - } finally { - clearTimeout(this.timeout); - } - } - respond(response) { - if (this.timedOut) { - return; - } - const cfResponse = { - Status: response.status, - Reason: response.reason, - PhysicalResourceId: this.physicalResourceId, - StackId: this.event.StackId, - RequestId: this.event.RequestId, - LogicalResourceId: this.event.LogicalResourceId, - NoEcho: false, - Data: response.data - }; - const responseBody = JSON.stringify(cfResponse); - console.log("Responding to CloudFormation", responseBody); - const parsedUrl = url.parse(this.event.ResponseURL); - const requestOptions = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: "PUT", - headers: { "content-type": "", "content-length": responseBody.length } - }; - return new Promise((resolve, reject) => { - try { - const request2 = https.request(requestOptions, resolve); - request2.on("error", reject); - request2.write(responseBody); - request2.end(); - } catch (e) { - reject(e); - } - }); - } -}; -function extractPhysicalResourceId(event) { - switch (event.RequestType) { - case "Create": - return event.LogicalResourceId; - case "Update": - case "Delete": - return event.PhysicalResourceId; - } -} - -// lib/assertions/providers/lambda-handler/assertion.ts -var AssertionHandler = class extends CustomResourceHandler { - async processEvent(request2) { - let actual = decodeCall(request2.actual); - const expected = decodeCall(request2.expected); - let result; - const matcher = new MatchCreator(expected).getMatcher(); - console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`); - const matchResult = matcher.test(actual); - matchResult.finished(); - if (matchResult.hasFailed()) { - result = { - failed: true, - assertion: JSON.stringify({ - status: "fail", - message: [ - ...matchResult.toHumanStrings(), - JSON.stringify(matchResult.target, void 0, 2) - ].join("\n") - }) - }; - if (request2.failDeployment) { - throw new Error(result.assertion); - } - } else { - result = { - assertion: JSON.stringify({ - status: "success" - }) - }; - } - return result; - } -}; -var MatchCreator = class { - constructor(obj) { - this.parsedObj = { - matcher: obj - }; - } - getMatcher() { - try { - const final = JSON.parse(JSON.stringify(this.parsedObj), function(_k, v) { - const nested = Object.keys(v)[0]; - switch (nested) { - case "$ArrayWith": - return Match.arrayWith(v[nested]); - case "$ObjectLike": - return Match.objectLike(v[nested]); - case "$StringLike": - return Match.stringLikeRegexp(v[nested]); - default: - return v; - } - }); - if (Matcher.isMatcher(final.matcher)) { - return final.matcher; - } - return Match.exact(final.matcher); - } catch { - return Match.exact(this.parsedObj.matcher); - } - } -}; -function decodeCall(call) { - if (!call) { - return void 0; - } - try { - const parsed = JSON.parse(call); - return parsed; - } catch (e) { - return call; - } -} - -// lib/assertions/providers/lambda-handler/utils.ts -function decode(object) { - return JSON.parse(JSON.stringify(object), (_k, v) => { - switch (v) { - case "TRUE:BOOLEAN": - return true; - case "FALSE:BOOLEAN": - return false; - default: - return v; - } - }); -} - -// lib/assertions/providers/lambda-handler/sdk.ts -function flatten(object) { - return Object.assign( - {}, - ...function _flatten(child, path = []) { - return [].concat(...Object.keys(child).map((key) => { - let childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key]; - if (typeof childKey === "string") { - childKey = isJsonString(childKey); - } - return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey }; - })); - }(object) - ); -} -var AwsApiCallHandler = class extends CustomResourceHandler { - async processEvent(request2) { - const AWS = require("aws-sdk"); - console.log(`AWS SDK VERSION: ${AWS.VERSION}`); - if (!Object.prototype.hasOwnProperty.call(AWS, request2.service)) { - throw Error(`Service ${request2.service} does not exist in AWS SDK version ${AWS.VERSION}.`); - } - const service = new AWS[request2.service](); - const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise(); - console.log(`SDK response received ${JSON.stringify(response)}`); - delete response.ResponseMetadata; - const respond = { - apiCallResponse: response - }; - const flatData = { - ...flatten(respond) - }; - const resp = request2.flattenResponse === "true" ? flatData : respond; - console.log(`Returning result ${JSON.stringify(resp)}`); - return resp; - } -}; -function isJsonString(value) { - try { - return JSON.parse(value); - } catch { - return value; - } -} - -// lib/assertions/providers/lambda-handler/types.ts -var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals"; -var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall"; - -// lib/assertions/providers/lambda-handler/index.ts -async function handler(event, context) { - console.log(`Event: ${JSON.stringify({ ...event, ResponseURL: "..." })}`); - const provider = createResourceHandler(event, context); - try { - if (event.RequestType === "Delete") { - await provider.respond({ - status: "SUCCESS", - reason: "OK" - }); - return; - } - const result = await provider.handle(); - const actualPath = event.ResourceProperties.actualPath; - const actual = actualPath ? result[`apiCallResponse.${actualPath}`] : result.apiCallResponse; - if ("expected" in event.ResourceProperties) { - const assertion = new AssertionHandler({ - ...event, - ResourceProperties: { - ServiceToken: event.ServiceToken, - actual, - expected: event.ResourceProperties.expected - } - }, context); - try { - const assertionResult = await assertion.handle(); - await provider.respond({ - status: "SUCCESS", - reason: "OK", - data: { - ...assertionResult, - ...result - } - }); - return; - } catch (e) { - await provider.respond({ - status: "FAILED", - reason: e.message ?? "Internal Error" - }); - return; - } - } - await provider.respond({ - status: "SUCCESS", - reason: "OK", - data: result - }); - } catch (e) { - await provider.respond({ - status: "FAILED", - reason: e.message ?? "Internal Error" - }); - return; - } - return; -} -function createResourceHandler(event, context) { - if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) { - return new AwsApiCallHandler(event, context); - } else if (event.ResourceType.startsWith(ASSERT_RESOURCE_TYPE)) { - return new AssertionHandler(event, context); - } else { - throw new Error(`Unsupported resource type "${event.ResourceType}`); - } -} -// Annotate the CommonJS export names for ESM import in node: -0 && (module.exports = { - handler -}); diff --git a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/base.assets.json b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/base.assets.json index 9f0c6525d7f91..2b0406276e727 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/base.assets.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/base.assets.json @@ -1,5 +1,5 @@ { - "version": "21.0.0", + "version": "30.1.0", "files": { "1caf5ea1b3cc1aedc4ec46feb2680836eae5804fa1ae1d8a572944636e88531b": { "source": { diff --git a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/cdk.out b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/cdk.out index 8ecc185e9dbee..b72fef144f05c 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"21.0.0"} \ No newline at end of file +{"version":"30.1.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/integ.json b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/integ.json index 1e9ee364aca3e..aebe2612de860 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "21.0.0", + "version": "30.1.0", "testCases": { "ssm-string-param/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/list-param.assets.json b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/list-param.assets.json index 47303559bc7be..511467ae0901c 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/list-param.assets.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/list-param.assets.json @@ -1,5 +1,5 @@ { - "version": "21.0.0", + "version": "30.1.0", "files": { "f21f04e61fc048db023578e8c9bdab9b7f45992bd3d533bcf7fb9eb87991bc95": { "source": { diff --git a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/manifest.json b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/manifest.json index d8e0de9e54959..719162cd30161 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "21.0.0", + "version": "30.1.0", "artifacts": { "base.assets": { "type": "cdk:asset-manifest", @@ -178,7 +178,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}/3fdd4225944766d24a4a35fa88db2febd8e7f28bd8ea992c8972583ca24d0b9a.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/b707a28540181bd09e120c895fa139b9bf644835095f79f29045b5281a61ff9b.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/ssmstringparamDefaultTestDeployAssert9C612E37.assets.json b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/ssmstringparamDefaultTestDeployAssert9C612E37.assets.json index edc04ad32c05b..03bae5afdafd5 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/ssmstringparamDefaultTestDeployAssert9C612E37.assets.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/ssmstringparamDefaultTestDeployAssert9C612E37.assets.json @@ -1,20 +1,20 @@ { - "version": "21.0.0", + "version": "30.1.0", "files": { - "d47f7e6772bfdf47ecbc070ffe204baf53bacbfbf7814eb407bd8ea108c1c1bb": { + "1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28": { "source": { - "path": "asset.d47f7e6772bfdf47ecbc070ffe204baf53bacbfbf7814eb407bd8ea108c1c1bb.bundle", + "path": "asset.1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.bundle", "packaging": "zip" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "d47f7e6772bfdf47ecbc070ffe204baf53bacbfbf7814eb407bd8ea108c1c1bb.zip", + "objectKey": "1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } }, - "3fdd4225944766d24a4a35fa88db2febd8e7f28bd8ea992c8972583ca24d0b9a": { + "b707a28540181bd09e120c895fa139b9bf644835095f79f29045b5281a61ff9b": { "source": { "path": "ssmstringparamDefaultTestDeployAssert9C612E37.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "3fdd4225944766d24a4a35fa88db2febd8e7f28bd8ea992c8972583ca24d0b9a.json", + "objectKey": "b707a28540181bd09e120c895fa139b9bf644835095f79f29045b5281a61ff9b.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-ssm/test/integ.list-parameter.js.snapshot/ssmstringparamDefaultTestDeployAssert9C612E37.template.json b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/ssmstringparamDefaultTestDeployAssert9C612E37.template.json index f0328cc7a02d6..c724787b3ce7a 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/ssmstringparamDefaultTestDeployAssert9C612E37.template.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.list-parameter.js.snapshot/ssmstringparamDefaultTestDeployAssert9C612E37.template.json @@ -26,7 +26,7 @@ ] }, "flattenResponse": "false", - "salt": "1663960575818" + "salt": "1677705112870" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -80,7 +80,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "d47f7e6772bfdf47ecbc070ffe204baf53bacbfbf7814eb407bd8ea108c1c1bb.zip" + "S3Key": "1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.zip" }, "Timeout": 120, "Handler": "index.handler", diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdk.out b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdk.out index 588d7b269d34f..b72fef144f05c 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"30.1.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.assets.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.assets.json new file mode 100644 index 0000000000000..764a6d46160e1 --- /dev/null +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.assets.json @@ -0,0 +1,19 @@ +{ + "version": "30.1.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.template.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/integ-parameter-arns.assets.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/integ-parameter-arns.assets.json index d74374938ea2e..48bdfe13582a6 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/integ-parameter-arns.assets.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/integ-parameter-arns.assets.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "30.1.0", "files": { "ab8a821f2d4347b885e1e7bdf551140408b2750fbafbac4513181d12253510be": { "source": { diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/integ.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/integ.json index 19eada241a4ca..7ce3e8ca1c0c5 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/integ.json @@ -1,14 +1,12 @@ { - "version": "20.0.0", + "version": "30.1.0", "testCases": { - "integ.parameter-arns": { + "cdk-integ-ssm-parameter-arns/DefaultTest": { "stacks": [ "integ-parameter-arns" ], - "diffAssets": false, - "stackUpdateWorkflow": true + "assertionStack": "cdk-integ-ssm-parameter-arns/DefaultTest/DeployAssert", + "assertionStackName": "cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49" } - }, - "synthContext": {}, - "enableLookups": false + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/manifest.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/manifest.json index 5c02e6d204a32..8256affd97ded 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/manifest.json @@ -1,12 +1,6 @@ { - "version": "20.0.0", + "version": "30.1.0", "artifacts": { - "Tree": { - "type": "cdk:tree", - "properties": { - "file": "tree.json" - } - }, "integ-parameter-arns.assets": { "type": "cdk:asset-manifest", "properties": { @@ -155,6 +149,59 @@ ] }, "displayName": "integ-parameter-arns" + }, + "cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "cdkintegssmparameterarnsDefaultTestDeployAssertE4B86A49.assets" + ], + "metadata": { + "/cdk-integ-ssm-parameter-arns/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/cdk-integ-ssm-parameter-arns/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "cdk-integ-ssm-parameter-arns/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/tree.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/tree.json index 2b744d57fcbe2..4d63b893e3801 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.js.snapshot/tree.json @@ -4,14 +4,6 @@ "id": "App", "path": "", "children": { - "Tree": { - "id": "Tree", - "path": "Tree", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" - } - }, "integ-parameter-arns": { "id": "integ-parameter-arns", "path": "integ-parameter-arns", @@ -20,8 +12,8 @@ "id": "ParameterNameParameter", "path": "integ-parameter-arns/ParameterNameParameter", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" } }, "StringAutogen": { @@ -247,76 +239,154 @@ "id": "StringAutogenArn", "path": "integ-parameter-arns/StringAutogenArn", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" } }, "StringSimpleArn": { "id": "StringSimpleArn", "path": "integ-parameter-arns/StringSimpleArn", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" } }, "StringPathArn": { "id": "StringPathArn", "path": "integ-parameter-arns/StringPathArn", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" } }, "ListAutogenArn": { "id": "ListAutogenArn", "path": "integ-parameter-arns/ListAutogenArn", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" } }, "ListSimpleArn": { "id": "ListSimpleArn", "path": "integ-parameter-arns/ListSimpleArn", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" } }, "ListPathArn": { "id": "ListPathArn", "path": "integ-parameter-arns/ListPathArn", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" } }, "ParameterizedSimpleArn": { "id": "ParameterizedSimpleArn", "path": "integ-parameter-arns/ParameterizedSimpleArn", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" } }, "ParameterizedNonSimpleArn": { "id": "ParameterizedNonSimpleArn", "path": "integ-parameter-arns/ParameterizedNonSimpleArn", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-parameter-arns/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-parameter-arns/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" } } }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "cdk-integ-ssm-parameter-arns": { + "id": "cdk-integ-ssm-parameter-arns", + "path": "cdk-integ-ssm-parameter-arns", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "cdk-integ-ssm-parameter-arns/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "cdk-integ-ssm-parameter-arns/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.264" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "cdk-integ-ssm-parameter-arns/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "cdk-integ-ssm-parameter-arns/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "cdk-integ-ssm-parameter-arns/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.264" } } }, "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-ssm/test/integ.parameter-arns.ts b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.ts index 277e06557ba6d..6d14c352f1350 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.ts +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-arns.ts @@ -1,5 +1,5 @@ -/* eslint-disable max-len */ import { App, CfnOutput, CfnParameter, Stack } from '@aws-cdk/core'; +import { IntegTest } from '@aws-cdk/integ-tests'; import * as ssm from '../lib'; const app = new App(); @@ -22,4 +22,6 @@ for (const p of params) { new CfnOutput(stack, `${p.node.id}Arn`, { value: p.parameterArn }); } -app.synth(); +new IntegTest(app, 'cdk-integ-ssm-parameter-arns', { + testCases: [stack], +}); diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/asset.1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.bundle/index.js b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/asset.1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.bundle/index.js new file mode 100644 index 0000000000000..4264087b9aab2 --- /dev/null +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/asset.1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.bundle/index.js @@ -0,0 +1,1204 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// lib/assertions/providers/lambda-handler/index.ts +var lambda_handler_exports = {}; +__export(lambda_handler_exports, { + handler: () => handler, + isComplete: () => isComplete, + onTimeout: () => onTimeout +}); +module.exports = __toCommonJS(lambda_handler_exports); + +// ../assertions/lib/matcher.ts +var Matcher = class { + /** + * Check whether the provided object is a subtype of the `IMatcher`. + */ + static isMatcher(x) { + return x && x instanceof Matcher; + } +}; +var MatchResult = class { + constructor(target) { + this.failuresHere = /* @__PURE__ */ new Map(); + this.captures = /* @__PURE__ */ new Map(); + this.finalized = false; + this.innerMatchFailures = /* @__PURE__ */ new Map(); + this._hasFailed = false; + this._failCount = 0; + this._cost = 0; + this.target = target; + } + /** + * DEPRECATED + * @deprecated use recordFailure() + */ + push(matcher, path, message) { + return this.recordFailure({ matcher, path, message }); + } + /** + * Record a new failure into this result at a specific path. + */ + recordFailure(failure) { + const failKey = failure.path.join("."); + let list = this.failuresHere.get(failKey); + if (!list) { + list = []; + this.failuresHere.set(failKey, list); + } + this._failCount += 1; + this._cost += failure.cost ?? 1; + list.push(failure); + this._hasFailed = true; + return this; + } + /** Whether the match is a success */ + get isSuccess() { + return !this._hasFailed; + } + /** Does the result contain any failures. If not, the result is a success */ + hasFailed() { + return this._hasFailed; + } + /** The number of failures */ + get failCount() { + return this._failCount; + } + /** The cost of the failures so far */ + get failCost() { + return this._cost; + } + /** + * Compose the results of a previous match as a subtree. + * @param id the id of the parent tree. + */ + compose(id, inner) { + if (inner.hasFailed()) { + this._hasFailed = true; + this._failCount += inner.failCount; + this._cost += inner._cost; + this.innerMatchFailures.set(id, inner); + } + inner.captures.forEach((vals, capture) => { + vals.forEach((value) => this.recordCapture({ capture, value })); + }); + return this; + } + /** + * Prepare the result to be analyzed. + * This API *must* be called prior to analyzing these results. + */ + finished() { + if (this.finalized) { + return this; + } + if (this.failCount === 0) { + this.captures.forEach((vals, cap) => cap._captured.push(...vals)); + } + this.finalized = true; + return this; + } + /** + * Render the failed match in a presentable way + * + * Prefer using `renderMismatch` over this method. It is left for backwards + * compatibility for test suites that expect it, but `renderMismatch()` will + * produce better output. + */ + toHumanStrings() { + const failures = new Array(); + debugger; + recurse(this, []); + return failures.map((r) => { + const loc = r.path.length === 0 ? "" : ` at /${r.path.join("/")}`; + return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; + }); + function recurse(x, prefix) { + for (const fail of Array.from(x.failuresHere.values()).flat()) { + failures.push({ + matcher: fail.matcher, + message: fail.message, + path: [...prefix, ...fail.path] + }); + } + for (const [key, inner] of x.innerMatchFailures.entries()) { + recurse(inner, [...prefix, key]); + } + } + } + /** + * Do a deep render of the match result, showing the structure mismatches in context + */ + renderMismatch() { + if (!this.hasFailed()) { + return ""; + } + const parts = new Array(); + const indents = new Array(); + emitFailures(this, ""); + recurse(this); + return moveMarkersToFront(parts.join("").trimEnd()); + function emit(x) { + if (x === void 0) { + debugger; + } + parts.push(x.replace(/\n/g, ` +${indents.join("")}`)); + } + function emitFailures(r, path, scrapSet) { + for (const fail of r.failuresHere.get(path) ?? []) { + emit(`!! ${fail.message} +`); + } + scrapSet == null ? void 0 : scrapSet.delete(path); + } + function recurse(r) { + const remainingFailures = new Set(Array.from(r.failuresHere.keys()).filter((x) => x !== "")); + if (Array.isArray(r.target)) { + indents.push(" "); + emit("[\n"); + for (const [first, i] of enumFirst(range(r.target.length))) { + if (!first) { + emit(",\n"); + } + emitFailures(r, `${i}`, remainingFailures); + const innerMatcher = r.innerMatchFailures.get(`${i}`); + if (innerMatcher) { + emitFailures(innerMatcher, ""); + recurseComparingValues(innerMatcher, r.target[i]); + } else { + emit(renderAbridged(r.target[i])); + } + } + emitRemaining(); + indents.pop(); + emit("\n]"); + return; + } + if (r.target && typeof r.target === "object") { + indents.push(" "); + emit("{\n"); + const keys = Array.from(/* @__PURE__ */ new Set([ + ...Object.keys(r.target), + ...Array.from(remainingFailures) + ])).sort(); + for (const [first, key] of enumFirst(keys)) { + if (!first) { + emit(",\n"); + } + emitFailures(r, key, remainingFailures); + const innerMatcher = r.innerMatchFailures.get(key); + if (innerMatcher) { + emitFailures(innerMatcher, ""); + emit(`${jsonify(key)}: `); + recurseComparingValues(innerMatcher, r.target[key]); + } else { + emit(`${jsonify(key)}: `); + emit(renderAbridged(r.target[key])); + } + } + emitRemaining(); + indents.pop(); + emit("\n}"); + return; + } + emitRemaining(); + emit(jsonify(r.target)); + function emitRemaining() { + if (remainingFailures.size > 0) { + emit("\n"); + } + for (const key of remainingFailures) { + emitFailures(r, key); + } + } + } + function recurseComparingValues(inner, actualValue) { + if (inner.target === actualValue) { + return recurse(inner); + } + emit(renderAbridged(actualValue)); + emit(" <*> "); + recurse(inner); + } + function renderAbridged(x) { + if (Array.isArray(x)) { + switch (x.length) { + case 0: + return "[]"; + case 1: + return `[ ${renderAbridged(x[0])} ]`; + case 2: + if (x.every((e) => ["number", "boolean", "string"].includes(typeof e))) { + return `[ ${x.map(renderAbridged).join(", ")} ]`; + } + return "[ ... ]"; + default: + return "[ ... ]"; + } + } + if (x && typeof x === "object") { + const keys = Object.keys(x); + switch (keys.length) { + case 0: + return "{}"; + case 1: + return `{ ${JSON.stringify(keys[0])}: ${renderAbridged(x[keys[0]])} }`; + default: + return "{ ... }"; + } + } + return jsonify(x); + } + function jsonify(x) { + return JSON.stringify(x) ?? "undefined"; + } + function moveMarkersToFront(x) { + const re = /^(\s+)!!/gm; + return x.replace(re, (_, spaces) => `!!${spaces.substring(0, spaces.length - 2)}`); + } + } + /** + * Record a capture against in this match result. + */ + recordCapture(options) { + let values = this.captures.get(options.capture); + if (values === void 0) { + values = []; + } + values.push(options.value); + this.captures.set(options.capture, values); + } +}; +function* range(n) { + for (let i = 0; i < n; i++) { + yield i; + } +} +function* enumFirst(xs) { + let first = true; + for (const x of xs) { + yield [first, x]; + first = false; + } +} + +// ../assertions/lib/private/matchers/absent.ts +var AbsentMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual !== void 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Received ${actual}, but key should be absent` + }); + } + return result; + } +}; + +// ../assertions/lib/private/sorting.ts +function sortKeyComparator(keyFn) { + return (a, b) => { + const ak = keyFn(a); + const bk = keyFn(b); + for (let i = 0; i < ak.length && i < bk.length; i++) { + const av = ak[i]; + const bv = bk[i]; + let diff = 0; + if (typeof av === "number" && typeof bv === "number") { + diff = av - bv; + } else if (typeof av === "string" && typeof bv === "string") { + diff = av.localeCompare(bv); + } + if (diff !== 0) { + return diff; + } + } + return bk.length - ak.length; + }; +} + +// ../assertions/lib/private/sparse-matrix.ts +var SparseMatrix = class { + constructor() { + this.matrix = /* @__PURE__ */ new Map(); + } + get(row, col) { + var _a; + return (_a = this.matrix.get(row)) == null ? void 0 : _a.get(col); + } + row(row) { + var _a; + return Array.from(((_a = this.matrix.get(row)) == null ? void 0 : _a.entries()) ?? []); + } + set(row, col, value) { + let r = this.matrix.get(row); + if (!r) { + r = /* @__PURE__ */ new Map(); + this.matrix.set(row, r); + } + r.set(col, value); + } +}; + +// ../assertions/lib/private/type.ts +function getType(obj) { + return Array.isArray(obj) ? "array" : typeof obj; +} + +// ../assertions/lib/match.ts +var Match = class { + /** + * Use this matcher in the place of a field's value, if the field must not be present. + */ + static absent() { + return new AbsentMatch("absent"); + } + /** + * Matches the specified pattern with the array found in the same relative path of the target. + * The set of elements (or matchers) must be in the same order as would be found. + * @param pattern the pattern to match + */ + static arrayWith(pattern) { + return new ArrayMatch("arrayWith", pattern); + } + /** + * Matches the specified pattern with the array found in the same relative path of the target. + * The set of elements (or matchers) must match exactly and in order. + * @param pattern the pattern to match + */ + static arrayEquals(pattern) { + return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); + } + /** + * Deep exact matching of the specified pattern to the target. + * @param pattern the pattern to match + */ + static exact(pattern) { + return new LiteralMatch("exact", pattern, { partialObjects: false }); + } + /** + * Matches the specified pattern to an object found in the same relative path of the target. + * The keys and their values (or matchers) must be present in the target but the target can be a superset. + * @param pattern the pattern to match + */ + static objectLike(pattern) { + return new ObjectMatch("objectLike", pattern); + } + /** + * Matches the specified pattern to an object found in the same relative path of the target. + * The keys and their values (or matchers) must match exactly with the target. + * @param pattern the pattern to match + */ + static objectEquals(pattern) { + return new ObjectMatch("objectEquals", pattern, { partial: false }); + } + /** + * Matches any target which does NOT follow the specified pattern. + * @param pattern the pattern to NOT match + */ + static not(pattern) { + return new NotMatch("not", pattern); + } + /** + * Matches any string-encoded JSON and applies the specified pattern after parsing it. + * @param pattern the pattern to match after parsing the encoded JSON. + */ + static serializedJson(pattern) { + return new SerializedJson("serializedJson", pattern); + } + /** + * Matches any non-null value at the target. + */ + static anyValue() { + return new AnyMatch("anyValue"); + } + /** + * Matches targets according to a regular expression + */ + static stringLikeRegexp(pattern) { + return new StringLikeRegexpMatch("stringLikeRegexp", pattern); + } +}; +var LiteralMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partialObjects = options.partialObjects ?? false; + if (Matcher.isMatcher(this.pattern)) { + throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply."); + } + } + test(actual) { + if (Array.isArray(this.pattern)) { + return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); + } + if (typeof this.pattern === "object") { + return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual); + } + const result = new MatchResult(actual); + if (typeof this.pattern !== typeof actual) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected type ${typeof this.pattern} but received ${getType(actual)}` + }); + return result; + } + if (actual !== this.pattern) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected ${this.pattern} but received ${actual}` + }); + } + return result; + } +}; +var ArrayMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.subsequence = options.subsequence ?? true; + this.partialObjects = options.partialObjects ?? false; + } + test(actual) { + if (!Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type array but received ${getType(actual)}` + }); + } + return this.subsequence ? this.testSubsequence(actual) : this.testFullArray(actual); + } + testFullArray(actual) { + const result = new MatchResult(actual); + let i = 0; + for (; i < this.pattern.length && i < actual.length; i++) { + const patternElement = this.pattern[i]; + const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const innerResult = matcher.test(actual[i]); + result.compose(`${i}`, innerResult); + } + if (i < this.pattern.length) { + result.recordFailure({ + matcher: this, + message: `Not enough elements in array (expecting ${this.pattern.length}, got ${actual.length})`, + path: [`${i}`] + }); + } + if (i < actual.length) { + result.recordFailure({ + matcher: this, + message: `Too many elements in array (expecting ${this.pattern.length}, got ${actual.length})`, + path: [`${i}`] + }); + } + return result; + } + testSubsequence(actual) { + const result = new MatchResult(actual); + let patternIdx = 0; + let actualIdx = 0; + const matches = new SparseMatrix(); + while (patternIdx < this.pattern.length && actualIdx < actual.length) { + const patternElement = this.pattern[patternIdx]; + const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const matcherName = matcher.name; + if (matcherName == "absent" || matcherName == "anyValue") { + throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); + } + const innerResult = matcher.test(actual[actualIdx]); + matches.set(patternIdx, actualIdx, innerResult); + actualIdx++; + if (innerResult.isSuccess) { + result.compose(`${actualIdx}`, innerResult); + patternIdx++; + } + } + if (patternIdx < this.pattern.length) { + for (let spi = 0; spi < patternIdx; spi++) { + const foundMatch = matches.row(spi).find(([, r]) => r.isSuccess); + if (!foundMatch) { + continue; + } + const [index] = foundMatch; + result.compose(`${index}`, new MatchResult(actual[index]).recordFailure({ + matcher: this, + message: `arrayWith pattern ${spi} matched here`, + path: [], + cost: 0 + // This is an informational message so it would be unfair to assign it cost + })); + } + const failedMatches = matches.row(patternIdx); + failedMatches.sort(sortKeyComparator(([i, r]) => [r.failCost, i])); + if (failedMatches.length > 0) { + const [index, innerResult] = failedMatches[0]; + result.recordFailure({ + matcher: this, + message: `Could not match arrayWith pattern ${patternIdx}. This is the closest match`, + path: [`${index}`], + cost: 0 + // Informational message + }); + result.compose(`${index}`, innerResult); + } else { + result.recordFailure({ + matcher: this, + message: `Could not match arrayWith pattern ${patternIdx}. No more elements to try`, + path: [`${actual.length}`] + }); + } + } + return result; + } +}; +var ObjectMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partial = options.partial ?? true; + } + test(actual) { + if (typeof actual !== "object" || Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type object but received ${getType(actual)}` + }); + } + const result = new MatchResult(actual); + if (!this.partial) { + for (const a of Object.keys(actual)) { + if (!(a in this.pattern)) { + result.recordFailure({ + matcher: this, + path: [a], + message: `Unexpected key ${a}` + }); + } + } + } + for (const [patternKey, patternVal] of Object.entries(this.pattern)) { + if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { + result.recordFailure({ + matcher: this, + path: [patternKey], + message: `Missing key '${patternKey}'` + }); + continue; + } + const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); + const inner = matcher.test(actual[patternKey]); + result.compose(patternKey, inner); + } + return result; + } +}; +var SerializedJson = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + if (getType(actual) !== "string") { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected JSON as a string but found ${getType(actual)}` + }); + } + let parsed; + try { + parsed = JSON.parse(actual); + } catch (err) { + if (err instanceof SyntaxError) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Invalid JSON string: ${actual}` + }); + } else { + throw err; + } + } + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(parsed); + if (innerResult.hasFailed()) { + innerResult.recordFailure({ + matcher: this, + path: [], + message: "Encoded JSON value does not match" + }); + } + return innerResult; + } +}; +var NotMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(actual); + const result = new MatchResult(actual); + if (innerResult.failCount === 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}` + }); + } + return result; + } +}; +var AnyMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual == null) { + result.recordFailure({ + matcher: this, + path: [], + message: "Expected a value but found none" + }); + } + return result; + } +}; +var StringLikeRegexpMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + const regex = new RegExp(this.pattern, "gm"); + if (typeof actual !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected a string, but got '${typeof actual}'` + }); + } + if (!regex.test(actual)) { + result.recordFailure({ + matcher: this, + path: [], + message: `String '${actual}' did not match pattern '${this.pattern}'` + }); + } + return result; + } +}; + +// lib/assertions/providers/lambda-handler/base.ts +var https = __toESM(require("https")); +var url = __toESM(require("url")); +var AWS = __toESM(require("aws-sdk")); +var CustomResourceHandler = class { + constructor(event, context) { + this.event = event; + this.context = context; + this.timedOut = false; + this.timeout = setTimeout(async () => { + await this.respond({ + status: "FAILED", + reason: "Lambda Function Timeout", + data: this.context.logStreamName + }); + this.timedOut = true; + }, context.getRemainingTimeInMillis() - 1200); + this.event = event; + this.physicalResourceId = extractPhysicalResourceId(event); + } + /** + * Handles executing the custom resource event. If `stateMachineArn` is present + * in the props then trigger the waiter statemachine + */ + async handle() { + try { + if ("stateMachineArn" in this.event.ResourceProperties) { + const req = { + stateMachineArn: this.event.ResourceProperties.stateMachineArn, + name: this.event.RequestId, + input: JSON.stringify(this.event) + }; + await this.startExecution(req); + return; + } else { + const response = await this.processEvent(this.event.ResourceProperties); + return response; + } + } catch (e) { + console.log(e); + throw e; + } finally { + clearTimeout(this.timeout); + } + } + /** + * Handle async requests from the waiter state machine + */ + async handleIsComplete() { + try { + const result = await this.processEvent(this.event.ResourceProperties); + return result; + } catch (e) { + console.log(e); + return; + } finally { + clearTimeout(this.timeout); + } + } + /** + * Start a step function state machine which will wait for the request + * to be successful. + */ + async startExecution(req) { + try { + const sfn = new AWS.StepFunctions(); + await sfn.startExecution(req).promise(); + } finally { + clearTimeout(this.timeout); + } + } + respond(response) { + if (this.timedOut) { + return; + } + const cfResponse = { + Status: response.status, + Reason: response.reason, + PhysicalResourceId: this.physicalResourceId, + StackId: this.event.StackId, + RequestId: this.event.RequestId, + LogicalResourceId: this.event.LogicalResourceId, + NoEcho: false, + Data: response.data + }; + const responseBody = JSON.stringify(cfResponse); + console.log("Responding to CloudFormation", responseBody); + const parsedUrl = url.parse(this.event.ResponseURL); + const requestOptions = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: "PUT", + headers: { "content-type": "", "content-length": responseBody.length } + }; + return new Promise((resolve, reject) => { + try { + const request2 = https.request(requestOptions, resolve); + request2.on("error", reject); + request2.write(responseBody); + request2.end(); + } catch (e) { + reject(e); + } finally { + clearTimeout(this.timeout); + } + }); + } +}; +function extractPhysicalResourceId(event) { + switch (event.RequestType) { + case "Create": + return event.LogicalResourceId; + case "Update": + case "Delete": + return event.PhysicalResourceId; + } +} + +// lib/assertions/providers/lambda-handler/assertion.ts +var AssertionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + let actual = decodeCall(request2.actual); + const expected = decodeCall(request2.expected); + let result; + const matcher = new MatchCreator(expected).getMatcher(); + console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`); + const matchResult = matcher.test(actual); + matchResult.finished(); + if (matchResult.hasFailed()) { + result = { + failed: true, + assertion: JSON.stringify({ + status: "fail", + message: matchResult.renderMismatch() + }) + }; + if (request2.failDeployment) { + throw new Error(result.assertion); + } + } else { + result = { + assertion: JSON.stringify({ + status: "success" + }) + }; + } + return result; + } +}; +var MatchCreator = class { + constructor(obj) { + this.parsedObj = { + matcher: obj + }; + } + /** + * Return a Matcher that can be tested against the actual results. + * This will convert the encoded matchers into their corresponding + * assertions matcher. + * + * For example: + * + * ExpectedResult.objectLike({ + * Messages: [{ + * Body: Match.objectLike({ + * Elements: Match.arrayWith([{ Asdf: 3 }]), + * Payload: Match.serializedJson({ key: 'value' }), + * }), + * }], + * }); + * + * Will be encoded as: + * { + * $ObjectLike: { + * Messages: [{ + * Body: { + * $ObjectLike: { + * Elements: { + * $ArrayWith: [{ Asdf: 3 }], + * }, + * Payload: { + * $SerializedJson: { key: 'value' } + * } + * }, + * }, + * }], + * }, + * } + * + * Which can then be parsed by this function. For each key (recursively) + * the parser will check if the value has one of the encoded matchers as a key + * and if so, it will set the value as the Matcher. So, + * + * { + * Body: { + * $ObjectLike: { + * Elements: { + * $ArrayWith: [{ Asdf: 3 }], + * }, + * Payload: { + * $SerializedJson: { key: 'value' } + * } + * }, + * }, + * } + * + * Will be converted to + * { + * Body: Match.objectLike({ + * Elements: Match.arrayWith([{ Asdf: 3 }]), + * Payload: Match.serializedJson({ key: 'value' }), + * }), + * } + */ + getMatcher() { + try { + const final = JSON.parse(JSON.stringify(this.parsedObj), function(_k, v) { + const nested = Object.keys(v)[0]; + switch (nested) { + case "$ArrayWith": + return Match.arrayWith(v[nested]); + case "$ObjectLike": + return Match.objectLike(v[nested]); + case "$StringLike": + return Match.stringLikeRegexp(v[nested]); + case "$SerializedJson": + return Match.serializedJson(v[nested]); + default: + return v; + } + }); + if (Matcher.isMatcher(final.matcher)) { + return final.matcher; + } + return Match.exact(final.matcher); + } catch { + return Match.exact(this.parsedObj.matcher); + } + } +}; +function decodeCall(call) { + if (!call) { + return void 0; + } + try { + const parsed = JSON.parse(call); + return parsed; + } catch (e) { + return call; + } +} + +// lib/assertions/providers/lambda-handler/utils.ts +function decode(object) { + return JSON.parse(JSON.stringify(object), (_k, v) => { + switch (v) { + case "TRUE:BOOLEAN": + return true; + case "FALSE:BOOLEAN": + return false; + default: + return v; + } + }); +} + +// lib/assertions/providers/lambda-handler/sdk.ts +function flatten(object) { + return Object.assign( + {}, + ...function _flatten(child, path = []) { + return [].concat(...Object.keys(child).map((key) => { + let childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key]; + if (typeof childKey === "string") { + childKey = isJsonString(childKey); + } + return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey }; + })); + }(object) + ); +} +var AwsApiCallHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const AWS2 = require("aws-sdk"); + console.log(`AWS SDK VERSION: ${AWS2.VERSION}`); + if (!Object.prototype.hasOwnProperty.call(AWS2, request2.service)) { + throw Error(`Service ${request2.service} does not exist in AWS SDK version ${AWS2.VERSION}.`); + } + const service = new AWS2[request2.service](); + const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise(); + console.log(`SDK response received ${JSON.stringify(response)}`); + delete response.ResponseMetadata; + const respond = { + apiCallResponse: response + }; + const flatData = { + ...flatten(respond) + }; + let resp = respond; + if (request2.outputPaths) { + resp = filterKeys(flatData, request2.outputPaths); + } else if (request2.flattenResponse === "true") { + resp = flatData; + } + console.log(`Returning result ${JSON.stringify(resp)}`); + return resp; + } +}; +function filterKeys(object, searchStrings) { + return Object.entries(object).reduce((filteredObject, [key, value]) => { + for (const searchString of searchStrings) { + if (key.startsWith(`apiCallResponse.${searchString}`)) { + filteredObject[key] = value; + } + } + return filteredObject; + }, {}); +} +function isJsonString(value) { + try { + return JSON.parse(value); + } catch { + return value; + } +} + +// lib/assertions/providers/lambda-handler/types.ts +var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals"; +var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall"; + +// lib/assertions/providers/lambda-handler/index.ts +async function handler(event, context) { + console.log(`Event: ${JSON.stringify({ ...event, ResponseURL: "..." })}`); + const provider = createResourceHandler(event, context); + try { + if (event.RequestType === "Delete") { + await provider.respond({ + status: "SUCCESS", + reason: "OK" + }); + return; + } + const result = await provider.handle(); + if ("stateMachineArn" in event.ResourceProperties) { + console.info('Found "stateMachineArn", waiter statemachine started'); + return; + } else if ("expected" in event.ResourceProperties) { + console.info('Found "expected", testing assertions'); + const actualPath = event.ResourceProperties.actualPath; + const actual = actualPath ? result[`apiCallResponse.${actualPath}`] : result.apiCallResponse; + const assertion = new AssertionHandler({ + ...event, + ResourceProperties: { + ServiceToken: event.ServiceToken, + actual, + expected: event.ResourceProperties.expected + } + }, context); + try { + const assertionResult = await assertion.handle(); + await provider.respond({ + status: "SUCCESS", + reason: "OK", + // return both the result of the API call _and_ the assertion results + data: { + ...assertionResult, + ...result + } + }); + return; + } catch (e) { + await provider.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + return; + } + } + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: result + }); + } catch (e) { + await provider.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + return; + } + return; +} +async function onTimeout(timeoutEvent) { + const isCompleteRequest = JSON.parse(JSON.parse(timeoutEvent.Cause).errorMessage); + const provider = createResourceHandler(isCompleteRequest, standardContext); + await provider.respond({ + status: "FAILED", + reason: "Operation timed out: " + JSON.stringify(isCompleteRequest) + }); +} +async function isComplete(event, context) { + console.log(`Event: ${JSON.stringify({ ...event, ResponseURL: "..." })}`); + const provider = createResourceHandler(event, context); + try { + const result = await provider.handleIsComplete(); + const actualPath = event.ResourceProperties.actualPath; + if (result) { + const actual = actualPath ? result[`apiCallResponse.${actualPath}`] : result.apiCallResponse; + if ("expected" in event.ResourceProperties) { + const assertion = new AssertionHandler({ + ...event, + ResourceProperties: { + ServiceToken: event.ServiceToken, + actual, + expected: event.ResourceProperties.expected + } + }, context); + const assertionResult = await assertion.handleIsComplete(); + if (!(assertionResult == null ? void 0 : assertionResult.failed)) { + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: { + ...assertionResult, + ...result + } + }); + return; + } else { + console.log(`Assertion Failed: ${JSON.stringify(assertionResult)}`); + throw new Error(JSON.stringify(event)); + } + } + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: result + }); + } else { + console.log("No result"); + throw new Error(JSON.stringify(event)); + } + return; + } catch (e) { + console.log(e); + throw new Error(JSON.stringify(event)); + } +} +function createResourceHandler(event, context) { + if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) { + return new AwsApiCallHandler(event, context); + } else if (event.ResourceType.startsWith(ASSERT_RESOURCE_TYPE)) { + return new AssertionHandler(event, context); + } else { + throw new Error(`Unsupported resource type "${event.ResourceType}`); + } +} +var standardContext = { + getRemainingTimeInMillis: () => 9e4 +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler, + isComplete, + onTimeout +}); diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/cdk.out b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/cdk.out index 8ecc185e9dbee..b72fef144f05c 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"21.0.0"} \ No newline at end of file +{"version":"30.1.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/integ.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/integ.json index 37dd1517a09fc..4516309b36851 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "21.0.0", + "version": "30.1.0", "testCases": { "SSMParameterStoreTest/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/manifest.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/manifest.json index b3dea0254e5ea..25ba4d8cba2d5 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/manifest.json @@ -1,12 +1,6 @@ { - "version": "21.0.0", + "version": "30.1.0", "artifacts": { - "Tree": { - "type": "cdk:tree", - "properties": { - "file": "tree.json" - } - }, "sspms-creating.assets": { "type": "cdk:asset-manifest", "properties": { @@ -23,7 +17,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}/774681e523ca39cdb798d74ea486ac7874030c04a36debc6a623f24afe196859.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/e66d9c468deb11a6edb4850fee05e856d6fef6c50e5ba763bdfbc3bb6d21cd72.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -160,7 +154,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}/ed82f3ce48345002448971161c8bd7594b2f548f56641b70afb2f4688e4c0aef.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/c1201c40c53533a2b464021ee0d0d5d7a5bbfa71e6a9498a3e90fe8e46745a10.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -209,6 +203,12 @@ ] }, "displayName": "sspms-cleanup" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-cleanup.assets.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-cleanup.assets.json index dd2d2b82e2cc2..948760a544bd0 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-cleanup.assets.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-cleanup.assets.json @@ -1,20 +1,20 @@ { - "version": "21.0.0", + "version": "30.1.0", "files": { - "b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b": { + "1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28": { "source": { - "path": "asset.b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.bundle", + "path": "asset.1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.bundle", "packaging": "zip" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.zip", + "objectKey": "1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } }, - "ed82f3ce48345002448971161c8bd7594b2f548f56641b70afb2f4688e4c0aef": { + "c1201c40c53533a2b464021ee0d0d5d7a5bbfa71e6a9498a3e90fe8e46745a10": { "source": { "path": "sspms-cleanup.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "ed82f3ce48345002448971161c8bd7594b2f548f56641b70afb2f4688e4c0aef.json", + "objectKey": "c1201c40c53533a2b464021ee0d0d5d7a5bbfa71e6a9498a3e90fe8e46745a10.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-ssm/test/integ.parameter-store-string.js.snapshot/sspms-cleanup.template.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-cleanup.template.json index a2af3eeea9e3d..73cdbb183a469 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-cleanup.template.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-cleanup.template.json @@ -15,7 +15,7 @@ "Name": "/My/Secret/Parameter" }, "flattenResponse": "false", - "salt": "1666616854742" + "salt": "1677705111898" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -69,7 +69,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.zip" + "S3Key": "1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.zip" }, "Timeout": 120, "Handler": "index.handler", diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-creating.assets.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-creating.assets.json index 58c023eab8e3b..31639a1b4f194 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-creating.assets.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-creating.assets.json @@ -1,20 +1,20 @@ { - "version": "21.0.0", + "version": "30.1.0", "files": { - "b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b": { + "1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28": { "source": { - "path": "asset.b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.bundle", + "path": "asset.1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.bundle", "packaging": "zip" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.zip", + "objectKey": "1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } }, - "774681e523ca39cdb798d74ea486ac7874030c04a36debc6a623f24afe196859": { + "e66d9c468deb11a6edb4850fee05e856d6fef6c50e5ba763bdfbc3bb6d21cd72": { "source": { "path": "sspms-creating.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "774681e523ca39cdb798d74ea486ac7874030c04a36debc6a623f24afe196859.json", + "objectKey": "e66d9c468deb11a6edb4850fee05e856d6fef6c50e5ba763bdfbc3bb6d21cd72.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-ssm/test/integ.parameter-store-string.js.snapshot/sspms-creating.template.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-creating.template.json index 5300480f9984b..0529d02721822 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-creating.template.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-creating.template.json @@ -25,7 +25,7 @@ "Value": "Abc123" }, "flattenResponse": "false", - "salt": "1666616854737" + "salt": "1677705111893" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -79,7 +79,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.zip" + "S3Key": "1f3c2cfb18e102edc713fe4c4b4d87572f4297ee4a5e80a5960adf526ee9ea28.zip" }, "Timeout": 120, "Handler": "index.handler", diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-using.assets.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-using.assets.json index 5b3b30e94ddf0..b4b1027289319 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-using.assets.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/sspms-using.assets.json @@ -1,5 +1,5 @@ { - "version": "21.0.0", + "version": "30.1.0", "files": { "f6e392d82be8514b35d4085f6ff4e3df808815b1cf4cc7a119cb67ace46e03b9": { "source": { diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/tree.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/tree.json index 89b9dbbcb8d05..f6ad39fb45a21 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.js.snapshot/tree.json @@ -4,14 +4,6 @@ "id": "App", "path": "", "children": { - "Tree": { - "id": "Tree", - "path": "Tree", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.133" - } - }, "sspms-creating": { "id": "sspms-creating", "path": "sspms-creating", @@ -55,7 +47,7 @@ "path": "sspms-creating/SecureParam/SdkProvider/AssertionsProvider", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.133" + "version": "10.1.264" } } }, @@ -119,7 +111,23 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.133" + "version": "10.1.264" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "sspms-creating/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "sspms-creating/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" } } }, @@ -195,6 +203,22 @@ "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "sspms-using/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "sspms-using/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } } }, "constructInfo": { @@ -219,7 +243,7 @@ "path": "sspms-cleanup/AwsApiCallSSMdeleteParameter/SdkProvider/AssertionsProvider", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.133" + "version": "10.1.264" } } }, @@ -283,7 +307,23 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.133" + "version": "10.1.264" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "sspms-cleanup/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "sspms-cleanup/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" } } }, @@ -305,7 +345,7 @@ "path": "SSMParameterStoreTest/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.133" + "version": "10.1.264" } } }, @@ -319,6 +359,14 @@ "fqn": "@aws-cdk/integ-tests.IntegTest", "version": "0.0.0" } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.264" + } } }, "constructInfo": { diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.ts b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.ts index 7386e9ff03fef..a345c3c761b01 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.ts +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.ts @@ -98,5 +98,3 @@ const integTest = new integ.IntegTest(app, 'SSMParameterStoreTest', { integTest.assertions.awsApiCall('SSM', 'deleteParameter', { Name: SECURE_PARAM_NAME, }); - -app.synth(); diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/SSM-Parameter.assets.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/SSM-Parameter.assets.json similarity index 96% rename from packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/SSM-Parameter.assets.json rename to packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/SSM-Parameter.assets.json index 5e8efc26c5d8e..5084653c99fbd 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/SSM-Parameter.assets.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/SSM-Parameter.assets.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "30.1.0", "files": { "22116adb80d8a58634b45b5916a99c2ef23e56479f377bb32f8b4f18dbae3aad": { "source": { diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/SSM-Parameter.template.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/SSM-Parameter.template.json similarity index 100% rename from packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/SSM-Parameter.template.json rename to packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/SSM-Parameter.template.json diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdk.out b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdk.out new file mode 100644 index 0000000000000..b72fef144f05c --- /dev/null +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"30.1.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdkintegssmparameterDefaultTestDeployAssert8D247A87.assets.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdkintegssmparameterDefaultTestDeployAssert8D247A87.assets.json new file mode 100644 index 0000000000000..f35a9d3747dc7 --- /dev/null +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdkintegssmparameterDefaultTestDeployAssert8D247A87.assets.json @@ -0,0 +1,19 @@ +{ + "version": "30.1.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "cdkintegssmparameterDefaultTestDeployAssert8D247A87.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdkintegssmparameterDefaultTestDeployAssert8D247A87.template.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdkintegssmparameterDefaultTestDeployAssert8D247A87.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/cdkintegssmparameterDefaultTestDeployAssert8D247A87.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/integ.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/integ.json new file mode 100644 index 0000000000000..f9dbe6b59729b --- /dev/null +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "30.1.0", + "testCases": { + "cdk-integ-ssm-parameter/DefaultTest": { + "stacks": [ + "SSM-Parameter" + ], + "assertionStack": "cdk-integ-ssm-parameter/DefaultTest/DeployAssert", + "assertionStackName": "cdkintegssmparameterDefaultTestDeployAssert8D247A87" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/manifest.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/manifest.json similarity index 57% rename from packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/manifest.json rename to packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/manifest.json index 0f8354281923f..31a6b259ead0c 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/manifest.json @@ -1,12 +1,6 @@ { - "version": "20.0.0", + "version": "30.1.0", "artifacts": { - "Tree": { - "type": "cdk:tree", - "properties": { - "file": "tree.json" - } - }, "SSM-Parameter.assets": { "type": "cdk:asset-manifest", "properties": { @@ -89,6 +83,59 @@ ] }, "displayName": "SSM-Parameter" + }, + "cdkintegssmparameterDefaultTestDeployAssert8D247A87.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "cdkintegssmparameterDefaultTestDeployAssert8D247A87.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "cdkintegssmparameterDefaultTestDeployAssert8D247A87": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "cdkintegssmparameterDefaultTestDeployAssert8D247A87.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "cdkintegssmparameterDefaultTestDeployAssert8D247A87.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "cdkintegssmparameterDefaultTestDeployAssert8D247A87.assets" + ], + "metadata": { + "/cdk-integ-ssm-parameter/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/cdk-integ-ssm-parameter/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "cdk-integ-ssm-parameter/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/tree.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/tree.json similarity index 69% rename from packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/tree.json rename to packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/tree.json index 4ccbfdde520d6..2b95bc53da505 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter.js.snapshot/tree.json @@ -4,14 +4,6 @@ "id": "App", "path": "", "children": { - "Tree": { - "id": "Tree", - "path": "Tree", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" - } - }, "SSM-Parameter": { "id": "SSM-Parameter", "path": "SSM-Parameter", @@ -20,6 +12,14 @@ "id": "UserRole", "path": "SSM-Parameter/UserRole", "children": { + "ImportUserRole": { + "id": "ImportUserRole", + "path": "SSM-Parameter/UserRole/ImportUserRole", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, "Resource": { "id": "Resource", "path": "SSM-Parameter/UserRole/Resource", @@ -186,28 +186,106 @@ "id": "StringListOutput", "path": "SSM-Parameter/StringListOutput", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" } }, "ParamArn": { "id": "ParamArn", "path": "SSM-Parameter/ParamArn", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "SSM-Parameter/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "SSM-Parameter/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "cdk-integ-ssm-parameter": { + "id": "cdk-integ-ssm-parameter", + "path": "cdk-integ-ssm-parameter", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "cdk-integ-ssm-parameter/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "cdk-integ-ssm-parameter/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.264" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "cdk-integ-ssm-parameter/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "cdk-integ-ssm-parameter/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "cdk-integ-ssm-parameter/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" } } }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.264" } } }, "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-ssm/test/integ.parameter.lit.js.snapshot/cdk.out b/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/cdk.out deleted file mode 100644 index 588d7b269d34f..0000000000000 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/cdk.out +++ /dev/null @@ -1 +0,0 @@ -{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/integ.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/integ.json deleted file mode 100644 index 8a8bfa258d831..0000000000000 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.js.snapshot/integ.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": "20.0.0", - "testCases": { - "integ.parameter.lit": { - "stacks": [ - "SSM-Parameter" - ], - "diffAssets": false, - "stackUpdateWorkflow": true - } - }, - "synthContext": {}, - "enableLookups": false -} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.ts b/packages/@aws-cdk/aws-ssm/test/integ.parameter.ts similarity index 67% rename from packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.ts rename to packages/@aws-cdk/aws-ssm/test/integ.parameter.ts index 211d7b8e77daf..db8315792696b 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.ts +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter.ts @@ -1,5 +1,6 @@ import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; +import { IntegTest } from '@aws-cdk/integ-tests'; import * as ssm from '../lib'; const app = new cdk.App(); @@ -9,26 +10,15 @@ const role = new iam.Role(stack, 'UserRole', { assumedBy: new iam.AccountRootPrincipal(), }); -/// !show -// Create a new SSM Parameter holding a String const param = new ssm.StringParameter(stack, 'StringParameter', { - // description: 'Some user-friendly description', - // name: 'ParameterName', stringValue: 'Initial parameter value', - // allowedPattern: '.*', }); -// Grant read access to some Role param.grantRead(role); -// Create a new SSM Parameter holding a StringList const listParameter = new ssm.StringListParameter(stack, 'StringListParameter', { - // description: 'Some user-friendly description', - // name: 'ParameterName', stringListValue: ['Initial parameter value A', 'Initial parameter value B'], - // allowedPattern: '.*', }); -/// !hide new cdk.CfnOutput(stack, 'StringListOutput', { value: cdk.Fn.join('+', listParameter.stringListValue), @@ -38,4 +28,6 @@ new cdk.CfnOutput(stack, 'ParamArn', { value: param.parameterArn, }); -app.synth(); +new IntegTest(app, 'cdk-integ-ssm-parameter', { + testCases: [stack], +}); diff --git a/packages/@aws-cdk/cli-lib/THIRD_PARTY_LICENSES b/packages/@aws-cdk/cli-lib/THIRD_PARTY_LICENSES index 1f2953d2f4c71..985d70ffb6f69 100644 --- a/packages/@aws-cdk/cli-lib/THIRD_PARTY_LICENSES +++ b/packages/@aws-cdk/cli-lib/THIRD_PARTY_LICENSES @@ -1,6 +1,6 @@ The @aws-cdk/cli-lib package includes the following third-party software/licensing: -** @jsii/check-node@1.75.0 - https://www.npmjs.com/package/@jsii/check-node/v/1.75.0 | Apache-2.0 +** @jsii/check-node@1.76.0 - https://www.npmjs.com/package/@jsii/check-node/v/1.76.0 | Apache-2.0 jsii Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -268,7 +268,7 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH RE ---------------- -** aws-sdk@2.1317.0 - https://www.npmjs.com/package/aws-sdk/v/2.1317.0 | Apache-2.0 +** aws-sdk@2.1325.0 - https://www.npmjs.com/package/aws-sdk/v/2.1325.0 | Apache-2.0 AWS SDK for JavaScript Copyright 2012-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -2475,7 +2475,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------- -** raw-body@2.5.1 - https://www.npmjs.com/package/raw-body/v/2.5.1 | MIT +** raw-body@2.5.2 - https://www.npmjs.com/package/raw-body/v/2.5.2 | MIT The MIT License (MIT) Copyright (c) 2013-2014 Jonathan Ong @@ -2545,7 +2545,7 @@ IN THE SOFTWARE. ---------------- -** readable-stream@2.3.7 - https://www.npmjs.com/package/readable-stream/v/2.3.7 | MIT +** readable-stream@2.3.8 - https://www.npmjs.com/package/readable-stream/v/2.3.8 | MIT Node.js is licensed for use as follows: """ @@ -2597,7 +2597,7 @@ IN THE SOFTWARE. ---------------- -** readable-stream@3.6.0 - https://www.npmjs.com/package/readable-stream/v/3.6.0 | MIT +** readable-stream@3.6.1 - https://www.npmjs.com/package/readable-stream/v/3.6.1 | MIT Node.js is licensed for use as follows: """