@cdktn/provider-generator 0.24.0-pre.5 → 0.24.0-pre.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.spec.swcrc +22 -0
  2. package/LICENSE +355 -0
  3. package/README.md +1 -1
  4. package/build/__tests__/edge-provider-schema/cli.js +8 -3
  5. package/build/get/__tests__/generator/import-style.test.d.ts +2 -0
  6. package/build/get/__tests__/generator/import-style.test.js +101 -0
  7. package/build/get/__tests__/generator/module-generator.test.js +12 -12
  8. package/build/get/constructs-maker.d.ts +5 -1
  9. package/build/get/constructs-maker.js +14 -6
  10. package/build/get/generator/emitter/struct-emitter.d.ts +2 -1
  11. package/build/get/generator/emitter/struct-emitter.js +14 -11
  12. package/build/get/generator/module-generator.js +3 -3
  13. package/build/get/generator/provider-generator.d.ts +9 -1
  14. package/build/get/generator/provider-generator.js +9 -6
  15. package/jest.config.js +16 -9
  16. package/package.json +24 -23
  17. package/package.sh +1 -1
  18. package/src/__tests__/__snapshots__/edge-provider-schema.test.ts.snap +8 -8
  19. package/src/__tests__/__snapshots__/provider.test.ts.snap +2951 -2951
  20. package/src/__tests__/edge-provider-schema/builder.ts +185 -0
  21. package/src/__tests__/edge-provider-schema/cli.ts +51 -0
  22. package/src/__tests__/edge-provider-schema/index.ts +161 -0
  23. package/src/__tests__/edge-provider-schema.test.ts +24 -0
  24. package/src/__tests__/provider.test.ts +180 -0
  25. package/src/get/__tests__/constructs-maker.test.ts +118 -0
  26. package/src/get/__tests__/generator/__snapshots__/complex-computed-types.test.ts.snap +5 -5
  27. package/src/get/__tests__/generator/__snapshots__/export-sharding.test.ts.snap +3310 -3310
  28. package/src/get/__tests__/generator/__snapshots__/module-generator.test.ts.snap +355 -355
  29. package/src/get/__tests__/generator/__snapshots__/nested-types.test.ts.snap +8 -8
  30. package/src/get/__tests__/generator/__snapshots__/provider.test.ts.snap +8 -8
  31. package/src/get/__tests__/generator/__snapshots__/resource-types.test.ts.snap +126 -126
  32. package/src/get/__tests__/generator/__snapshots__/skipped-attributes.test.ts.snap +17 -17
  33. package/src/get/__tests__/generator/__snapshots__/types.test.ts.snap +65 -65
  34. package/src/get/__tests__/generator/complex-computed-types.test.ts +28 -0
  35. package/src/get/__tests__/generator/deep-nested-attributes.test.ts +56 -0
  36. package/src/get/__tests__/generator/description-escaping.test.ts +84 -0
  37. package/src/get/__tests__/generator/empty-provider-resources.test.ts +26 -0
  38. package/src/get/__tests__/generator/export-sharding.test.ts +169 -0
  39. package/src/get/__tests__/generator/import-style.test.ts +129 -0
  40. package/src/get/__tests__/generator/module-generator.test.ts +528 -0
  41. package/src/get/__tests__/generator/nested-types.test.ts +54 -0
  42. package/src/get/__tests__/generator/provider.test.ts +51 -0
  43. package/src/get/__tests__/generator/resource-types.test.ts +118 -0
  44. package/src/get/__tests__/generator/skipped-attributes.test.ts +72 -0
  45. package/src/get/__tests__/generator/types.test.ts +611 -0
  46. package/src/get/__tests__/generator/versions-file.test.ts +72 -0
  47. package/src/get/__tests__/util.ts +75 -0
  48. package/src/get/constructs-maker.ts +822 -0
  49. package/src/get/generator/custom-defaults.ts +493 -0
  50. package/src/get/generator/emitter/attributes-emitter.ts +225 -0
  51. package/src/get/generator/emitter/index.ts +5 -0
  52. package/src/get/generator/emitter/resource-emitter.ts +226 -0
  53. package/src/get/generator/emitter/struct-emitter.ts +683 -0
  54. package/src/get/generator/loop-detection.ts +81 -0
  55. package/src/get/generator/models/attribute-model.ts +216 -0
  56. package/src/get/generator/models/attribute-type-model.ts +448 -0
  57. package/src/get/generator/models/index.ts +7 -0
  58. package/src/get/generator/models/resource-model.ts +161 -0
  59. package/src/get/generator/models/scope.ts +54 -0
  60. package/src/get/generator/models/struct.ts +116 -0
  61. package/src/get/generator/module-generator.ts +234 -0
  62. package/src/get/generator/provider-generator.ts +355 -0
  63. package/src/get/generator/resource-parser.ts +762 -0
  64. package/src/get/generator/sanitized-comments.ts +49 -0
  65. package/src/get/generator/skipped-attributes.ts +27 -0
  66. package/src/index.ts +40 -0
  67. package/src/util.ts +26 -0
@@ -0,0 +1,185 @@
1
+ // Copyright (c) HashiCorp, Inc
2
+ // SPDX-License-Identifier: MPL-2.0
3
+ import { ProviderSchema, Schema, AttributeType, Block } from "@cdktn/commons";
4
+
5
+ type ResourceSchema = { [type: string]: Schema };
6
+ export function schema({
7
+ name,
8
+ provider,
9
+ resources = {},
10
+ dataSources = {},
11
+ }: {
12
+ name: string;
13
+ provider: Schema;
14
+ resources: ResourceSchema;
15
+ dataSources: ResourceSchema;
16
+ }): ProviderSchema {
17
+ return {
18
+ format_version: "0.2",
19
+ provider_schemas: {
20
+ [`registry.terraform.io/cdktf/${name}`]: {
21
+ provider: provider,
22
+ resource_schemas: resources,
23
+ data_source_schemas: dataSources,
24
+ },
25
+ },
26
+ };
27
+ }
28
+
29
+ export class SchemaBuilder {
30
+ private schema: Schema;
31
+
32
+ constructor() {
33
+ this.schema = {
34
+ version: 0,
35
+ block: {
36
+ attributes: {},
37
+ block_types: {},
38
+ },
39
+ };
40
+ }
41
+
42
+ public attribute({
43
+ name,
44
+ type,
45
+ required = false,
46
+ computed = false,
47
+ optional = !required,
48
+ }: {
49
+ name: string;
50
+ type: AttributeType;
51
+ required?: boolean;
52
+ computed?: boolean;
53
+ optional?: boolean;
54
+ }): SchemaBuilder {
55
+ this.schema.block.attributes[name] = {
56
+ type,
57
+ optional,
58
+ computed,
59
+ required,
60
+ };
61
+ return this;
62
+ }
63
+
64
+ public listBlock({
65
+ name,
66
+ block,
67
+ minItems,
68
+ maxItems,
69
+ }: {
70
+ name: string;
71
+ block: Block;
72
+ minItems: number;
73
+ maxItems: number;
74
+ }): SchemaBuilder {
75
+ this.schema.block.block_types[name] = {
76
+ nesting_mode: "list",
77
+ block,
78
+ min_items: minItems,
79
+ max_items: maxItems,
80
+ };
81
+ return this;
82
+ }
83
+
84
+ public mapBlock({
85
+ name,
86
+ block,
87
+ }: {
88
+ name: string;
89
+ block: Block;
90
+ }): SchemaBuilder {
91
+ this.schema.block.block_types[name] = { nesting_mode: "map", block };
92
+ return this;
93
+ }
94
+
95
+ public setBlock({
96
+ name,
97
+ block,
98
+ }: {
99
+ name: string;
100
+ block: Block;
101
+ }): SchemaBuilder {
102
+ this.schema.block.block_types[name] = { nesting_mode: "set", block };
103
+ return this;
104
+ }
105
+ public singleBlock({
106
+ name,
107
+ block,
108
+ }: {
109
+ name: string;
110
+ block: Block;
111
+ }): SchemaBuilder {
112
+ this.schema.block.block_types[name] = { nesting_mode: "single", block };
113
+ return this;
114
+ }
115
+
116
+ public build(): Schema {
117
+ return this.schema;
118
+ }
119
+
120
+ public asBlock(): Block {
121
+ return this.schema.block;
122
+ }
123
+
124
+ public addAllPrimitiveTypes({
125
+ required,
126
+ computed,
127
+ prefix = "",
128
+ }: {
129
+ required: boolean;
130
+ computed: boolean;
131
+ prefix?: string;
132
+ }): SchemaBuilder {
133
+ this.attribute({ name: prefix + "str", type: "string", required, computed })
134
+ .attribute({ name: prefix + "num", type: "number", required, computed })
135
+ .attribute({ name: prefix + "bool", type: "bool", required, computed });
136
+
137
+ return this;
138
+ }
139
+
140
+ public addAllPrimitivePermutations(): SchemaBuilder {
141
+ this.addAllPrimitiveTypes({
142
+ required: false,
143
+ computed: false,
144
+ prefix: "opt",
145
+ })
146
+ .addAllPrimitiveTypes({ required: true, computed: false, prefix: "req" })
147
+ .addAllPrimitiveTypes({
148
+ required: false,
149
+ computed: true,
150
+ prefix: "computed",
151
+ });
152
+ return this;
153
+ }
154
+
155
+ public addAllPrimitiveListTypes({
156
+ required,
157
+ computed,
158
+ prefix = "",
159
+ }: {
160
+ required: boolean;
161
+ computed: boolean;
162
+ prefix?: string;
163
+ }): SchemaBuilder {
164
+ this.attribute({
165
+ name: prefix + "strList",
166
+ type: ["list", "string"],
167
+ required,
168
+ computed,
169
+ })
170
+ .attribute({
171
+ name: prefix + "numList",
172
+ type: ["list", "number"],
173
+ required,
174
+ computed,
175
+ })
176
+ .attribute({
177
+ name: prefix + "boolList",
178
+ type: ["list", "bool"],
179
+ required,
180
+ computed,
181
+ });
182
+
183
+ return this;
184
+ }
185
+ }
@@ -0,0 +1,51 @@
1
+ // Copyright (c) HashiCorp, Inc
2
+ // SPDX-License-Identifier: MPL-2.0
3
+ import * as path from "path";
4
+ import * as fs from "fs-extra";
5
+ import { edgeSchema } from ".";
6
+ import { generateProviderBindingsFromSchema } from "../..";
7
+
8
+ // This script builds the schema for the edge provider into the given directory for all languages.
9
+ const args = process.argv.slice(2);
10
+ const targetPath = args[0];
11
+
12
+ if (!targetPath) {
13
+ console.error("No target path specified");
14
+ process.exit(1);
15
+ }
16
+
17
+ // Clear / Create path
18
+ fs.mkdirpSync(targetPath);
19
+ fs.emptyDirSync(targetPath);
20
+
21
+ // Static require.resolve calls so knip / pnpm strict isolation can see the deps.
22
+ const deps = [
23
+ path.dirname(require.resolve("@types/node/package.json")),
24
+ path.dirname(require.resolve("constructs/package.json")),
25
+ path.dirname(require.resolve("cdktn/package.json")),
26
+ ];
27
+
28
+ (async () => {
29
+ await generateProviderBindingsFromSchema(targetPath, edgeSchema, {
30
+ entrypoint: path.join("providers", "edge", "index.ts"),
31
+ deps,
32
+ moduleKey: "edge",
33
+ python: {
34
+ outdir: path.resolve(targetPath, "python"),
35
+ moduleName: "edge",
36
+ },
37
+ java: {
38
+ outdir: path.resolve(targetPath, "java"),
39
+ package: `imports.edgeprovider`,
40
+ },
41
+ csharp: {
42
+ outdir: path.resolve(targetPath, "csharp"),
43
+ namespace: "Providers.Edge",
44
+ },
45
+ golang: {
46
+ outdir: path.resolve(targetPath, "go"),
47
+ moduleName: "cdk.tf/go/stack/generated/hashicorp",
48
+ packageName: "edge",
49
+ },
50
+ });
51
+ })();
@@ -0,0 +1,161 @@
1
+ // Copyright (c) HashiCorp, Inc
2
+ // SPDX-License-Identifier: MPL-2.0
3
+ import { ProviderSchema } from "@cdktn/commons";
4
+ import { schema, SchemaBuilder as S } from "./builder";
5
+
6
+ const required_attribute_resource = new S()
7
+ .addAllPrimitiveTypes({ required: true, computed: false })
8
+ .addAllPrimitiveListTypes({ required: true, computed: false })
9
+ .build();
10
+
11
+ const optional_attribute_resource = new S()
12
+ .addAllPrimitiveTypes({ required: false, computed: false })
13
+ .addAllPrimitiveListTypes({ required: false, computed: false })
14
+ .build();
15
+
16
+ const optional_computed_attribute_resource = new S()
17
+ .addAllPrimitiveTypes({ required: false, computed: true })
18
+ .addAllPrimitiveListTypes({ required: false, computed: true })
19
+ .build();
20
+
21
+ const list_block_resource = new S()
22
+ .listBlock({
23
+ name: "opt",
24
+ block: new S().addAllPrimitivePermutations().asBlock(),
25
+ minItems: 0,
26
+ maxItems: 42,
27
+ })
28
+ .listBlock({
29
+ name: "req",
30
+ block: new S().addAllPrimitivePermutations().asBlock(),
31
+ minItems: 1,
32
+ maxItems: 42,
33
+ })
34
+ .listBlock({
35
+ name: "singleopt",
36
+ block: new S().addAllPrimitivePermutations().asBlock(),
37
+ minItems: 0,
38
+ maxItems: 1,
39
+ })
40
+ .listBlock({
41
+ name: "singlereq",
42
+ block: new S().addAllPrimitivePermutations().asBlock(),
43
+ minItems: 1,
44
+ maxItems: 1,
45
+ })
46
+ .listBlock({
47
+ name: "singleComputedBlock",
48
+ block: new S()
49
+ .attribute({
50
+ computed: true,
51
+ name: "computed",
52
+ type: "string",
53
+ required: false,
54
+ optional: false,
55
+ })
56
+ .attribute({
57
+ computed: false,
58
+ name: "configured",
59
+ type: "string",
60
+ required: false,
61
+ })
62
+ .asBlock(),
63
+ minItems: 0,
64
+ maxItems: 1,
65
+ })
66
+ .attribute({
67
+ name: "computedListOfObject",
68
+ type: [
69
+ "list",
70
+ [
71
+ "object",
72
+ {
73
+ str: "string",
74
+ },
75
+ ],
76
+ ],
77
+ computed: true,
78
+ optional: false,
79
+ })
80
+ .attribute({
81
+ name: "computedListOfMapOfObject",
82
+ type: [
83
+ "list",
84
+ [
85
+ "map",
86
+ [
87
+ "object",
88
+ {
89
+ str: "string",
90
+ other: "string",
91
+ },
92
+ ],
93
+ ],
94
+ ],
95
+ computed: true,
96
+ optional: false,
97
+ })
98
+ .build();
99
+
100
+ const map_list_resource = new S()
101
+ .attribute({
102
+ name: "mapListOfObject",
103
+ type: [
104
+ "map",
105
+ [
106
+ "list",
107
+ [
108
+ "object",
109
+ {
110
+ hello: "string",
111
+ },
112
+ ],
113
+ ],
114
+ ],
115
+ computed: false,
116
+ optional: false,
117
+ })
118
+ .build();
119
+
120
+ const map_resource = new S()
121
+ .attribute({
122
+ name: "optMap",
123
+ type: ["map", "string"],
124
+ required: false,
125
+ computed: false,
126
+ })
127
+ .attribute({
128
+ name: "reqMap",
129
+ type: ["map", "bool"],
130
+ required: true,
131
+ computed: false,
132
+ })
133
+ .attribute({
134
+ name: "computedMap",
135
+ type: ["map", "number"],
136
+ required: false,
137
+ computed: true,
138
+ })
139
+ .build();
140
+
141
+ const set_block_resource = new S()
142
+ .setBlock({
143
+ name: "set",
144
+ block: new S().addAllPrimitivePermutations().asBlock(),
145
+ })
146
+ .build();
147
+
148
+ export const edgeSchema: ProviderSchema = schema({
149
+ name: "edge",
150
+ provider: new S().addAllPrimitivePermutations().build(),
151
+ resources: {
152
+ required_attribute_resource,
153
+ optional_attribute_resource,
154
+ optional_computed_attribute_resource,
155
+ list_block_resource,
156
+ map_resource,
157
+ set_block_resource,
158
+ map_list_resource,
159
+ },
160
+ dataSources: {},
161
+ });
@@ -0,0 +1,24 @@
1
+ // Copyright (c) HashiCorp, Inc
2
+ // SPDX-License-Identifier: MPL-2.0
3
+ import * as fs from "fs-extra";
4
+ import * as path from "path";
5
+ import { generateProviderBindingsFromSchema } from "..";
6
+ import { mkdtemp } from "@cdktn/commons";
7
+ import { edgeSchema } from "./edge-provider-schema";
8
+
9
+ describe("Edge Provider Schema", () => {
10
+ it("compiles to Typescript", async () => {
11
+ return mkdtemp(async (dir) => {
12
+ await generateProviderBindingsFromSchema(dir, edgeSchema);
13
+
14
+ fs.readdirSync(path.join(dir, "providers", "edge")).forEach((file) => {
15
+ if (!file.endsWith(".ts")) {
16
+ return;
17
+ }
18
+ expect(
19
+ fs.readFileSync(path.join(dir, "providers", "edge", file), "utf8"),
20
+ ).toMatchSnapshot(file);
21
+ });
22
+ });
23
+ });
24
+ });
@@ -0,0 +1,180 @@
1
+ // Copyright (c) HashiCorp, Inc
2
+ // SPDX-License-Identifier: MPL-2.0
3
+ import * as path from "path";
4
+ import * as fs from "fs-extra";
5
+ import { glob } from "glob";
6
+ import { mkdtemp } from "@cdktn/commons";
7
+ import { ConstructsMaker } from "../get/constructs-maker";
8
+ import { Language, TerraformProviderConstraint } from "@cdktn/commons";
9
+
10
+ interface SynthOutput {
11
+ [filePath: string]: any;
12
+ }
13
+
14
+ function directorySnapshot(root: string) {
15
+ const output: SynthOutput = {};
16
+
17
+ const files = glob.sync("**", {
18
+ ignore: [".git/**", ".jsii"],
19
+ cwd: root,
20
+ nodir: true,
21
+ dot: true,
22
+ });
23
+
24
+ for (const file of files) {
25
+ const filePath = path.join(root, file);
26
+
27
+ let content;
28
+
29
+ if (path.extname(filePath) === ".json") {
30
+ content = fs.readJsonSync(filePath);
31
+
32
+ if (path.basename(filePath) === "constraints.json") {
33
+ delete content.cdktf;
34
+ }
35
+ } else {
36
+ content = fs.readFileSync(filePath, "utf-8");
37
+ }
38
+
39
+ output[file] = content;
40
+ }
41
+
42
+ return output;
43
+ }
44
+
45
+ function resourceTypesPresentInSnapshot(
46
+ snapshot: SynthOutput,
47
+ providerNameInPath: string,
48
+ ) {
49
+ const resources: string[] = [];
50
+ const files = Object.keys(snapshot);
51
+ for (const file of files) {
52
+ const match = file.match(
53
+ `/providers/${providerNameInPath}/(.*?)/index.ts/`,
54
+ );
55
+ // avoids any not resources from being pushed
56
+ if (
57
+ match &&
58
+ !match[1].includes("/") &&
59
+ !match[1].includes("data-") &&
60
+ !match[1].includes("provider")
61
+ ) {
62
+ resources.push(match[1]);
63
+ }
64
+ }
65
+ return resources;
66
+ }
67
+
68
+ describe("Provider", () => {
69
+ it("generates a provider", async () => {
70
+ const constraint = new TerraformProviderConstraint(
71
+ "DataDog/datadog@= 3.12.0",
72
+ );
73
+ return await mkdtemp(async (workdir) => {
74
+ const jsiiPath = path.join(workdir, ".jsii");
75
+ const maker = new ConstructsMaker(
76
+ {
77
+ codeMakerOutput: workdir,
78
+ outputJsii: jsiiPath,
79
+ targetLanguage: Language.TYPESCRIPT,
80
+ },
81
+ process.env.CDKTF_EXPERIMENTAL_PROVIDER_SCHEMA_CACHE_PATH,
82
+ );
83
+ await maker.generate([constraint]);
84
+ const snapshot = directorySnapshot(workdir);
85
+ expect(snapshot).toMatchSnapshot();
86
+ });
87
+ }, 600_000);
88
+
89
+ it("has generated provider that includes static import functions", async () => {
90
+ const constraint = new TerraformProviderConstraint(
91
+ "DataDog/datadog@= 3.12.0",
92
+ );
93
+ return await mkdtemp(async (workdir) => {
94
+ const jsiiPath = path.join(workdir, ".jsii");
95
+ const maker = new ConstructsMaker({
96
+ codeMakerOutput: workdir,
97
+ outputJsii: jsiiPath,
98
+ targetLanguage: Language.TYPESCRIPT,
99
+ });
100
+ await maker.generate([constraint]);
101
+ const snapshot = directorySnapshot(workdir);
102
+
103
+ const terraformResourceTypesPresent: string[] =
104
+ resourceTypesPresentInSnapshot(snapshot, "datadog");
105
+
106
+ terraformResourceTypesPresent.forEach((resource) => {
107
+ let terraformResourceType = resource.replace(/-/g, "_");
108
+ if (!terraformResourceType.includes("datadog")) {
109
+ terraformResourceType = `datadog_${terraformResourceType}`;
110
+ }
111
+ expect(snapshot[`providers/datadog/${resource}/index.ts`]).toContain(
112
+ `public static generateConfigForImport(scope: Construct, importToId: string, importFromId: string, provider?: cdktn.TerraformProvider) {`,
113
+ );
114
+ expect(snapshot[`providers/datadog/${resource}/index.ts`]).toContain(
115
+ `return new cdktn.ImportableResource(scope, importToId, { terraformResourceType: "${terraformResourceType}", importId: importFromId, provider });`,
116
+ );
117
+ });
118
+ });
119
+ }, 600_000);
120
+
121
+ it("has name in constraint that does not match resolved name in fqpn", async () => {
122
+ const constraint = new TerraformProviderConstraint({
123
+ name: "dockerr",
124
+ source: "registry.terraform.io/kreuzwerker/docker",
125
+ version: "3.0.2",
126
+ });
127
+ return await mkdtemp(async (workdir) => {
128
+ const jsiiPath = path.join(workdir, ".jsii");
129
+ const maker = new ConstructsMaker(
130
+ {
131
+ codeMakerOutput: workdir,
132
+ outputJsii: jsiiPath,
133
+ targetLanguage: Language.TYPESCRIPT,
134
+ },
135
+ process.env.CDKTF_EXPERIMENTAL_PROVIDER_SCHEMA_CACHE_PATH,
136
+ );
137
+ await maker.generate([constraint]);
138
+ const snapshot = directorySnapshot(workdir);
139
+
140
+ const terraformResourceTypesPresent: string[] =
141
+ resourceTypesPresentInSnapshot(snapshot, "dockerr");
142
+
143
+ terraformResourceTypesPresent.forEach((resource) => {
144
+ expect(
145
+ snapshot[`providers/dockerr/${resource}/index.ts`],
146
+ ).toBeDefined();
147
+ });
148
+ });
149
+ }, 600_000);
150
+
151
+ it("generates constructs for two providers with same name", async () => {
152
+ const constraint = new TerraformProviderConstraint({
153
+ name: "bitbucket",
154
+ namespace: "DrFaust92",
155
+ version: "2.51.0",
156
+ source: "DrFaust92/bitbucket",
157
+ });
158
+ const constraint2 = new TerraformProviderConstraint({
159
+ name: "abitbucket",
160
+ namespace: "andsafe-AG",
161
+ version: "2.5.0",
162
+ source: "andsafe-AG/bitbucket",
163
+ });
164
+ return await mkdtemp(async (workdir) => {
165
+ const jsiiPath = path.join(workdir, ".jsii");
166
+ const maker = new ConstructsMaker(
167
+ {
168
+ codeMakerOutput: workdir,
169
+ outputJsii: jsiiPath,
170
+ targetLanguage: Language.TYPESCRIPT,
171
+ },
172
+ process.env.CDKTF_EXPERIMENTAL_PROVIDER_SCHEMA_CACHE_PATH,
173
+ );
174
+ await maker.generate([constraint, constraint2]);
175
+ console.log("workdir", workdir);
176
+ const snapshot = directorySnapshot(workdir);
177
+ expect(snapshot).toMatchSnapshot();
178
+ });
179
+ }, 600_000);
180
+ });