Skip to content

Commit

Permalink
feat(lib): allow chaining of TerraformIterator created resources
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielMSchmidt committed Nov 20, 2023
1 parent 47c8ec5 commit 0374555
Show file tree
Hide file tree
Showing 13 changed files with 5,036 additions and 286 deletions.
39 changes: 37 additions & 2 deletions examples/typescript/documentation/iterators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import { Construct } from "constructs";
import { AwsProvider } from "@cdktf/provider-aws/lib/aws-provider";
import { S3Bucket } from "@cdktf/provider-aws/lib/s3-bucket";

// DOCS_BLOCK_END:iterators,iterators-complex-types
import { TerraformAsset } from "cdktf";
import { S3BucketObject } from "@cdktf/provider-aws/lib/s3-bucket-object";
// DOCS_BLOCK_START:iterators,iterators-complex-types
export class IteratorsStack extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);
Expand Down Expand Up @@ -78,7 +82,38 @@ export class IteratorsStack extends TerraformStack {
});
// DOCS_BLOCK_END:iterators-complex-types

// DOCS_BLOCK_START:iterators,iterators-complex-types
// DOCS_BLOCK_START:iterators-chain
const s3BucketConfigurationIterator = TerraformIterator.fromMap({
website: {
name: "website-static-files",
tags: { app: "website" },
},
images: {
name: "images",
tags: { app: "image-converter" },
},
});

const s3Buckets = new S3Bucket(this, "complex-iterator-buckets", {
forEach: s3BucketConfigurationIterator,
bucket: s3BucketConfigurationIterator.getString("name"),
tags: s3BucketConfigurationIterator.getStringMap("tags"),
});

// This would be TerraformIterator.fromDataSources for data_sources
const s3BucketsIterator = TerraformIterator.fromResources(s3Buckets);
const helpFile = new TerraformAsset(this, "help", {
path: "./help",
});
new S3BucketObject(this, "object", {
forEach: s3BucketsIterator,
bucket: s3BucketsIterator.getString("id"),
key: "help",
source: helpFile.path,
});
// DOCS_BLOCK_END:iterators-chain

// DOCS_BLOCK_START:iterators,iterators-complex-types,iterators-chain
}
}
// DOCS_BLOCK_END:iterators,iterators-complex-types
// DOCS_BLOCK_END:iterators,iterators-complex-types,iterators-chain
26 changes: 13 additions & 13 deletions examples/typescript/documentation/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,18 @@ new VariablesStack(app, "variables");

app.synth();

// Full synth examples for Backends
localToCloudBackend();
cloudBackendToCustomBackend(localToCloudBackend());
// // Full synth examples for Backends
// localToCloudBackend();
// cloudBackendToCustomBackend(localToCloudBackend());

// Full synth examples for Stacks
singleStackRunner();
multiStackRunner();
crossStackReferencesRunner();
stackDependenciesRunner();
escapeHatchesRunner();
// // Full synth examples for Stacks
// singleStackRunner();
// multiStackRunner();
// crossStackReferencesRunner();
// stackDependenciesRunner();
// escapeHatchesRunner();

// Full synth variable examples
outputVariablesRunner();
outputsUsageRunner();
remoteStateRunner();
// // Full synth variable examples
// outputVariablesRunner();
// outputsUsageRunner();
// remoteStateRunner();
64 changes: 64 additions & 0 deletions packages/cdktf/lib/terraform-iterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "./complex-computed-list";
import { TerraformDynamicExpression } from "./terraform-dynamic-expression";
import { Fn } from "./terraform-functions";
import { ITerraformResource } from "./terraform-resource";
import {
FOR_EXPRESSION_KEY,
FOR_EXPRESSION_VALUE,
Expand Down Expand Up @@ -80,6 +81,26 @@ export abstract class TerraformIterator implements ITerraformIterator {
return new MapTerraformIterator(map);
}

/**
* Creates a new iterator from a resource that
* has been created with the `for_each` argument.
*/
public static fromResources(
resource: ITerraformResource
): ResourceTerraformIterator {
return new ResourceTerraformIterator(resource);
}

/**
* Creates a new iterator from a data source that
* has been created with the `for_each` argument.
*/
public static fromDataSources(
resource: ITerraformResource
): ResourceTerraformIterator {
return new ResourceTerraformIterator(resource);
}

/**
* @param attribute name of the property to retrieve
* @returns the given attribute of the current item iterated over as a string
Expand Down Expand Up @@ -284,3 +305,46 @@ export class MapTerraformIterator extends TerraformIterator {
return this._getValue();
}
}

// eslint-disable-next-line jsdoc/require-jsdoc
export class ResourceTerraformIterator extends TerraformIterator {
constructor(private readonly element: ITerraformResource) {
super();

if (element.count) {
throw new Error(
"Cannot create iterator from resource with count argument. Please use the same TerraformCount used in the resource passed here instead."
);
}

if (!element.forEach) {
throw new Error(
"Cannot create iterator from resource without for_each argument"
);
}
}

/**
* Returns the currenty entry in the list or set that is being iterated over.
* For lists this is the same as `iterator.value`. If you need the index,
* use count using the escape hatch:
* https://developer.hashicorp.com/terraform/cdktf/concepts/resources#escape-hatch
*/
public get key(): any {
return this._getKey();
}

/**
* Returns the value of the current item iterated over.
*/
public get value(): any {
return this._getValue();
}

/**
* @internal used by TerraformResource to set the for_each expression
*/
public _getForEachExpression(): any {
return this.element.fqn; // no wrapping necessary for resources
}
}
26 changes: 26 additions & 0 deletions packages/cdktf/test/dynamic-block.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,29 @@ test("dynamic blocks are properly rendered for providers", () => {
},
]);
});

test("chained iterators used in dynamic blocks", () => {
const app = Testing.app();
const stack = new TerraformStack(app, "test");
const it = TerraformIterator.fromList(["a", "b", "c"]);
const source = new TestResource(stack, "test", {
forEach: it,
name: "foo",
});

const chainedIt = TerraformIterator.fromResources(source);
new TestResource(stack, "chained", {
name: "foo",
listBlock: chainedIt.dynamic({ name: chainedIt.getString("string_value") }),
});

const synth = JSON.parse(Testing.synth(stack));
expect(synth).toHaveProperty(
"resource.test_resource.chained.dynamic.list_block.for_each",
"${test_resource.test}"
);
expect(synth).toHaveProperty(
"resource.test_resource.chained.dynamic.list_block.content",
{ name: "${each.value.string_value}" }
);
});
59 changes: 59 additions & 0 deletions packages/cdktf/test/iterator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,3 +393,62 @@ test("count can count references", () => {
"data${count.index}"
);
});

test("chained iterators used in for_each", () => {
const app = Testing.app();
const stack = new TerraformStack(app, "test");
const it = TerraformIterator.fromList(["a", "b", "c"]);
const source = new TestResource(stack, "test", {
forEach: it,
name: "foo",
});

const chainedIt = TerraformIterator.fromResources(source);
new TestResource(stack, "chained", {
forEach: chainedIt,
name: chainedIt.getString("string_value"),
});

const synth = JSON.parse(Testing.synth(stack));
expect(synth).toHaveProperty(
"resource.test_resource.chained.for_each",
"${test_resource.test}"
);
expect(synth).toHaveProperty(
"resource.test_resource.chained.name",
"${each.value.string_value}"
);
});

test("chained iterators from singular resources", () => {
const app = Testing.app();
const stack = new TerraformStack(app, "test");
const source = new TestResource(stack, "test", {
name: "foo",
});

expect(() => {
TerraformIterator.fromResources(source);
}).toThrowErrorMatchingInlineSnapshot(
`"Cannot create iterator from resource without for_each argument"`
);
});

test("chained iterators used with count", () => {
const app = Testing.app();
const stack = new TerraformStack(app, "test");

const resource = new TestResource(stack, "test", { name: "foo" });
const it = TerraformCount.of(resource.numericValue);

const datasFromCount = new TestDataSource(stack, "test_data", {
count: it,
name: `data${it.index}`,
});

expect(() => {
TerraformIterator.fromDataSources(datasFromCount);
}).toThrowErrorMatchingInlineSnapshot(
`"Cannot create iterator from resource with count argument. Please use the same TerraformCount used in the resource passed here instead."`
);
});
39 changes: 16 additions & 23 deletions tools/documentation-generation/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,23 @@ archiver-utils@^2.1.0:
normalize-path "^3.0.0"
readable-stream "^2.0.0"

[email protected].1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.1.tgz#21e92811d6f09ecfce649fbefefe8c79e57cbbb6"
integrity sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==
[email protected].2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.2.tgz#99991d5957e53bd0303a392979276ac4ddccf3b0"
integrity sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==
dependencies:
archiver-utils "^2.1.0"
async "^3.2.3"
async "^3.2.4"
buffer-crc32 "^0.2.1"
readable-stream "^3.6.0"
readdir-glob "^1.0.0"
readdir-glob "^1.1.2"
tar-stream "^2.2.0"
zip-stream "^4.1.0"

async@^3.2.3:
version "3.2.4"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
async@^3.2.4:
version "3.2.5"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66"
integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==

balanced-match@^1.0.0:
version "1.0.2"
Expand Down Expand Up @@ -146,9 +146,9 @@ case@^1.6.3:
"cdktf@file:../../packages/cdktf":
version "0.0.0"
dependencies:
archiver "5.3.1"
archiver "5.3.2"
json-stable-stringify "^1.0.2"
semver "^7.5.3"
semver "^7.5.4"

chalk@^4, chalk@^4.1.2:
version "4.1.2"
Expand Down Expand Up @@ -497,10 +497,10 @@ readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"

readdir-glob@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.2.tgz#b185789b8e6a43491635b6953295c5c5e3fd224c"
integrity sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==
readdir-glob@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584"
integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==
dependencies:
minimatch "^5.1.0"

Expand All @@ -524,13 +524,6 @@ safe-buffer@~5.2.0:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==

semver@^7.5.3:
version "7.5.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e"
integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==
dependencies:
lru-cache "^6.0.0"

semver@^7.5.4:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
Expand Down
Loading

0 comments on commit 0374555

Please sign in to comment.