@aws/nx-plugin 1.0.7 → 1.0.9
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.
- package/migrations.json +21 -1
- package/package.json +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +10 -0
- package/src/infra/app/files/app/README.md.template +12 -0
- package/src/infra/app/generator.js +8 -0
- package/src/infra/app/generator.js.map +1 -1
- package/src/migrations/latest/cache-inputs-outputs/metadata.json +3 -0
- package/src/migrations/latest/cache-inputs-outputs/migration.d.ts +6 -0
- package/src/migrations/latest/cache-inputs-outputs/migration.js +176 -0
- package/src/migrations/latest/cache-inputs-outputs/migration.js.map +1 -0
- package/src/migrations/latest/infra-test-loop/metadata.json +3 -0
- package/src/migrations/latest/infra-test-loop/migration.d.ts +6 -0
- package/src/migrations/latest/infra-test-loop/migration.js +110 -0
- package/src/migrations/latest/infra-test-loop/migration.js.map +1 -0
- package/src/migrations/latest/py-dynamodb-bind-base-model-meta/metadata.json +3 -0
- package/src/migrations/latest/py-dynamodb-bind-base-model-meta/migration.d.ts +6 -0
- package/src/migrations/latest/py-dynamodb-bind-base-model-meta/migration.js +56 -0
- package/src/migrations/latest/py-dynamodb-bind-base-model-meta/migration.js.map +1 -0
- package/src/migrations/latest/py-dynamodb-type-base-model-meta/metadata.json +3 -0
- package/src/migrations/latest/py-dynamodb-type-base-model-meta/migration.d.ts +6 -0
- package/src/migrations/latest/py-dynamodb-type-base-model-meta/migration.js +54 -0
- package/src/migrations/latest/py-dynamodb-type-base-model-meta/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.90/0004-python-test-input-scoping/migration.js +8 -2
- package/src/migrations/v1.0.0-rc.90/0004-python-test-input-scoping/migration.js.map +1 -1
- package/src/open-api/py-client/files/client/__clientModuleName__.py.template +17 -6
- package/src/open-api/py-client/files/shared/errors.py.template +3 -5
- package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +8 -4
- package/src/py/dynamodb/base-model-meta.d.ts +39 -0
- package/src/py/dynamodb/base-model-meta.js +54 -0
- package/src/py/dynamodb/base-model-meta.js.map +1 -0
- package/src/py/dynamodb/files/__name__/entities/base.py.template +4 -2
- package/src/smithy/project/__snapshots__/generator.spec.ts.snap +9 -0
- package/src/smithy/project/generator.d.ts +9 -0
- package/src/smithy/project/generator.js +15 -0
- package/src/smithy/project/generator.js.map +1 -1
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +2 -0
- package/src/terraform/project/generator.js +2 -4
- package/src/terraform/project/generator.js.map +1 -1
- package/src/ts/lib/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/lib/generator.d.ts +14 -3
- package/src/ts/lib/generator.js +18 -7
- package/src/ts/lib/generator.js.map +1 -1
- package/src/ts/rdb/generator.js +4 -0
- package/src/ts/rdb/generator.js.map +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +102 -2
- package/src/utils/__snapshots__/cdk-test-setup.spec.ts.snap +25 -0
- package/src/utils/cdk-test-setup.d.ts +22 -0
- package/src/utils/cdk-test-setup.js +38 -0
- package/src/utils/cdk-test-setup.js.map +1 -0
- package/src/utils/files/cdk-test-setup/tests/global-setup.ts.template +45 -0
- package/src/utils/shared-constructs.js +2 -0
- package/src/utils/shared-constructs.js.map +1 -1
- package/src/utils/test/python-dependencies.d.ts +6 -0
- package/src/utils/test/python-dependencies.js +9 -0
- package/src/utils/test/python-dependencies.js.map +1 -1
- package/src/utils/test/warm-python-cache.js +40 -21
- package/src/utils/test/warm-python-cache.js.map +1 -1
- package/src/utils/version-lockstep/hold.d.ts +26 -0
- package/src/utils/version-lockstep/hold.js +33 -0
- package/src/utils/version-lockstep/hold.js.map +1 -0
- package/src/utils/versions.d.ts +1 -1
- package/src/utils/versions.js +1 -1
- package/src/utils/versions.js.map +1 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/ import { bindMetaBlock, divergedMetaNextStep, findMetaBlockShape, META_BINDING, NESTED_PROTOCOL_META_HEADER, pyDynamoDbBasePyPaths, TYPED_IMPORT, UNTYPED_IMPORT, UNTYPED_META_HEADER } from "../../../py/dynamodb/base-model-meta.js";
|
|
5
|
+
import { formatFilesInSubtree } from "../../../utils/format.js";
|
|
6
|
+
/**
|
|
7
|
+
* Bind the `Meta` of the `BaseModel` that py#dynamodb vends in
|
|
8
|
+
* `<module>/entities/base.py` with pynamodb's declared `MetaProtocol` type.
|
|
9
|
+
*
|
|
10
|
+
* Release 1.0.8 vends, and its `py-dynamodb-type-base-model-meta` migration
|
|
11
|
+
* writes, a nested `class Meta(MetaProtocol):`. pynamodb declares
|
|
12
|
+
* `Model.Meta: MetaProtocol`, a mutable attribute, and the nested class
|
|
13
|
+
* narrows it to the class's own type, which ty 0.0.87 reports as
|
|
14
|
+
* `invalid-mutable-override`. This declares the block as
|
|
15
|
+
* `class _Meta(MetaProtocol):` and binds it with `Meta: MetaProtocol = _Meta`.
|
|
16
|
+
* `Meta` is the same class object, so pynamodb's runtime defaults apply as
|
|
17
|
+
* before.
|
|
18
|
+
*
|
|
19
|
+
* A base.py is edited only where its import line and `Meta` block still have
|
|
20
|
+
* the exact shape the generator wrote. One already bound is left alone, as is
|
|
21
|
+
* an untyped `class Meta:` in the generated shape, which
|
|
22
|
+
* `py-dynamodb-type-base-model-meta` converges on the bound shape before this
|
|
23
|
+
* runs. Any other base.py is left untouched and reported via `nextSteps`.
|
|
24
|
+
*/ /**
|
|
25
|
+
* Rewrite a base.py still in the 1.0.8 shape. Already bound, untyped and
|
|
26
|
+
* missing files are left alone; a diverged one is reported via nextSteps.
|
|
27
|
+
*/ const migrateBasePy = (tree, filePath, nextSteps)=>{
|
|
28
|
+
if (!tree.exists(filePath)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const content = tree.read(filePath, 'utf-8') ?? '';
|
|
32
|
+
if (content.includes(META_BINDING)) {
|
|
33
|
+
return; // Already migrated (or user-added).
|
|
34
|
+
}
|
|
35
|
+
if (content.includes(UNTYPED_IMPORT) && findMetaBlockShape(content, UNTYPED_META_HEADER)) {
|
|
36
|
+
return; // Left to py-dynamodb-type-base-model-meta.
|
|
37
|
+
}
|
|
38
|
+
const shape = findMetaBlockShape(content, NESTED_PROTOCOL_META_HEADER);
|
|
39
|
+
if (!shape || !content.includes(TYPED_IMPORT)) {
|
|
40
|
+
nextSteps.push(divergedMetaNextStep(filePath));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
tree.write(filePath, bindMetaBlock(content, NESTED_PROTOCOL_META_HEADER, shape));
|
|
44
|
+
};
|
|
45
|
+
export default async function migration(tree) {
|
|
46
|
+
const nextSteps = [];
|
|
47
|
+
for (const filePath of pyDynamoDbBasePyPaths(tree)){
|
|
48
|
+
migrateBasePy(tree, filePath, nextSteps);
|
|
49
|
+
}
|
|
50
|
+
await formatFilesInSubtree(tree);
|
|
51
|
+
return {
|
|
52
|
+
nextSteps
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/py-dynamodb-bind-base-model-meta/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { MigrationReturnObject, Tree } from '@nx/devkit';\nimport {\n bindMetaBlock,\n divergedMetaNextStep,\n findMetaBlockShape,\n META_BINDING,\n NESTED_PROTOCOL_META_HEADER,\n pyDynamoDbBasePyPaths,\n TYPED_IMPORT,\n UNTYPED_IMPORT,\n UNTYPED_META_HEADER,\n} from '../../../py/dynamodb/base-model-meta.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\n\n/**\n * Bind the `Meta` of the `BaseModel` that py#dynamodb vends in\n * `<module>/entities/base.py` with pynamodb's declared `MetaProtocol` type.\n *\n * Release 1.0.8 vends, and its `py-dynamodb-type-base-model-meta` migration\n * writes, a nested `class Meta(MetaProtocol):`. pynamodb declares\n * `Model.Meta: MetaProtocol`, a mutable attribute, and the nested class\n * narrows it to the class's own type, which ty 0.0.87 reports as\n * `invalid-mutable-override`. This declares the block as\n * `class _Meta(MetaProtocol):` and binds it with `Meta: MetaProtocol = _Meta`.\n * `Meta` is the same class object, so pynamodb's runtime defaults apply as\n * before.\n *\n * A base.py is edited only where its import line and `Meta` block still have\n * the exact shape the generator wrote. One already bound is left alone, as is\n * an untyped `class Meta:` in the generated shape, which\n * `py-dynamodb-type-base-model-meta` converges on the bound shape before this\n * runs. Any other base.py is left untouched and reported via `nextSteps`.\n */\n\n/**\n * Rewrite a base.py still in the 1.0.8 shape. Already bound, untyped and\n * missing files are left alone; a diverged one is reported via nextSteps.\n */\nconst migrateBasePy = (\n tree: Tree,\n filePath: string,\n nextSteps: string[],\n): void => {\n if (!tree.exists(filePath)) {\n return;\n }\n\n const content = tree.read(filePath, 'utf-8') ?? '';\n if (content.includes(META_BINDING)) {\n return; // Already migrated (or user-added).\n }\n\n if (\n content.includes(UNTYPED_IMPORT) &&\n findMetaBlockShape(content, UNTYPED_META_HEADER)\n ) {\n return; // Left to py-dynamodb-type-base-model-meta.\n }\n\n const shape = findMetaBlockShape(content, NESTED_PROTOCOL_META_HEADER);\n if (!shape || !content.includes(TYPED_IMPORT)) {\n nextSteps.push(divergedMetaNextStep(filePath));\n return;\n }\n\n tree.write(\n filePath,\n bindMetaBlock(content, NESTED_PROTOCOL_META_HEADER, shape),\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const filePath of pyDynamoDbBasePyPaths(tree)) {\n migrateBasePy(tree, filePath, nextSteps);\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["bindMetaBlock","divergedMetaNextStep","findMetaBlockShape","META_BINDING","NESTED_PROTOCOL_META_HEADER","pyDynamoDbBasePyPaths","TYPED_IMPORT","UNTYPED_IMPORT","UNTYPED_META_HEADER","formatFilesInSubtree","migrateBasePy","tree","filePath","nextSteps","exists","content","read","includes","shape","push","write","migration"],"mappings":"AAAA;;;CAGC,GAED,SACEA,aAAa,EACbC,oBAAoB,EACpBC,kBAAkB,EAClBC,YAAY,EACZC,2BAA2B,EAC3BC,qBAAqB,EACrBC,YAAY,EACZC,cAAc,EACdC,mBAAmB,QACd,0CAA0C;AACjD,SAASC,oBAAoB,QAAQ,2BAA2B;AAEhE;;;;;;;;;;;;;;;;;;CAkBC,GAED;;;CAGC,GACD,MAAMC,gBAAgB,CACpBC,MACAC,UACAC;IAEA,IAAI,CAACF,KAAKG,MAAM,CAACF,WAAW;QAC1B;IACF;IAEA,MAAMG,UAAUJ,KAAKK,IAAI,CAACJ,UAAU,YAAY;IAChD,IAAIG,QAAQE,QAAQ,CAACd,eAAe;QAClC,QAAQ,oCAAoC;IAC9C;IAEA,IACEY,QAAQE,QAAQ,CAACV,mBACjBL,mBAAmBa,SAASP,sBAC5B;QACA,QAAQ,4CAA4C;IACtD;IAEA,MAAMU,QAAQhB,mBAAmBa,SAASX;IAC1C,IAAI,CAACc,SAAS,CAACH,QAAQE,QAAQ,CAACX,eAAe;QAC7CO,UAAUM,IAAI,CAAClB,qBAAqBW;QACpC;IACF;IAEAD,KAAKS,KAAK,CACRR,UACAZ,cAAce,SAASX,6BAA6Bc;AAExD;AAEA,eAAe,eAAeG,UAC5BV,IAAU;IAEV,MAAME,YAAsB,EAAE;IAE9B,KAAK,MAAMD,YAAYP,sBAAsBM,MAAO;QAClDD,cAAcC,MAAMC,UAAUC;IAChC;IAEA,MAAMJ,qBAAqBE;IAE3B,OAAO;QAAEE;IAAU;AACrB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/ import { bindMetaBlock, divergedMetaNextStep, findMetaBlockShape, META_BINDING, pyDynamoDbBasePyPaths, TYPED_IMPORT, UNTYPED_IMPORT, UNTYPED_META_HEADER } from "../../../py/dynamodb/base-model-meta.js";
|
|
5
|
+
import { formatFilesInSubtree } from "../../../utils/format.js";
|
|
6
|
+
/**
|
|
7
|
+
* Type the `Meta` of the `BaseModel` that py#dynamodb vends in
|
|
8
|
+
* `<module>/entities/base.py` as pynamodb's `MetaProtocol`.
|
|
9
|
+
*
|
|
10
|
+
* pynamodb declares `Model.Meta: MetaProtocol`, a mutable attribute. A nested
|
|
11
|
+
* `class Meta:` defines only some of the protocol's members, which ty reports
|
|
12
|
+
* as `invalid-attribute-override`; and any nested `class Meta`, even one
|
|
13
|
+
* subclassing the protocol, declares `Meta` with the narrower type of that
|
|
14
|
+
* class, which ty 0.0.87 reports as `invalid-mutable-override`. So the block is
|
|
15
|
+
* declared as `class _Meta(MetaProtocol):` and bound with
|
|
16
|
+
* `Meta: MetaProtocol = _Meta`, keeping the inherited type. Subclassing the
|
|
17
|
+
* protocol declares every member with the protocol's type; the members it only
|
|
18
|
+
* annotates are not set on the class, so pynamodb's runtime defaults apply as
|
|
19
|
+
* before, and `Meta` is the same class object it was.
|
|
20
|
+
*
|
|
21
|
+
* The generator writes base.py once and keeps the user's copy, so this brings
|
|
22
|
+
* existing workspaces up to date. A base.py is edited only where its import
|
|
23
|
+
* line and `Meta` block still have the exact shape the generator wrote. One
|
|
24
|
+
* that has diverged is left untouched and reported via `nextSteps`.
|
|
25
|
+
*/ /**
|
|
26
|
+
* Rewrite a base.py still in the generated shape. Already migrated and missing
|
|
27
|
+
* files are left alone; a diverged one is reported via nextSteps.
|
|
28
|
+
*/ const migrateBasePy = (tree, filePath, nextSteps)=>{
|
|
29
|
+
if (!tree.exists(filePath)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const content = tree.read(filePath, 'utf-8') ?? '';
|
|
33
|
+
if (content.includes(META_BINDING)) {
|
|
34
|
+
return; // Already migrated (or user-added).
|
|
35
|
+
}
|
|
36
|
+
const shape = findMetaBlockShape(content, UNTYPED_META_HEADER);
|
|
37
|
+
if (!shape || !content.includes(UNTYPED_IMPORT)) {
|
|
38
|
+
nextSteps.push(divergedMetaNextStep(filePath));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
tree.write(filePath, bindMetaBlock(content.replace(UNTYPED_IMPORT, TYPED_IMPORT), UNTYPED_META_HEADER, shape));
|
|
42
|
+
};
|
|
43
|
+
export default async function migration(tree) {
|
|
44
|
+
const nextSteps = [];
|
|
45
|
+
for (const filePath of pyDynamoDbBasePyPaths(tree)){
|
|
46
|
+
migrateBasePy(tree, filePath, nextSteps);
|
|
47
|
+
}
|
|
48
|
+
await formatFilesInSubtree(tree);
|
|
49
|
+
return {
|
|
50
|
+
nextSteps
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/py-dynamodb-type-base-model-meta/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { MigrationReturnObject, Tree } from '@nx/devkit';\nimport {\n bindMetaBlock,\n divergedMetaNextStep,\n findMetaBlockShape,\n META_BINDING,\n pyDynamoDbBasePyPaths,\n TYPED_IMPORT,\n UNTYPED_IMPORT,\n UNTYPED_META_HEADER,\n} from '../../../py/dynamodb/base-model-meta.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\n\n/**\n * Type the `Meta` of the `BaseModel` that py#dynamodb vends in\n * `<module>/entities/base.py` as pynamodb's `MetaProtocol`.\n *\n * pynamodb declares `Model.Meta: MetaProtocol`, a mutable attribute. A nested\n * `class Meta:` defines only some of the protocol's members, which ty reports\n * as `invalid-attribute-override`; and any nested `class Meta`, even one\n * subclassing the protocol, declares `Meta` with the narrower type of that\n * class, which ty 0.0.87 reports as `invalid-mutable-override`. So the block is\n * declared as `class _Meta(MetaProtocol):` and bound with\n * `Meta: MetaProtocol = _Meta`, keeping the inherited type. Subclassing the\n * protocol declares every member with the protocol's type; the members it only\n * annotates are not set on the class, so pynamodb's runtime defaults apply as\n * before, and `Meta` is the same class object it was.\n *\n * The generator writes base.py once and keeps the user's copy, so this brings\n * existing workspaces up to date. A base.py is edited only where its import\n * line and `Meta` block still have the exact shape the generator wrote. One\n * that has diverged is left untouched and reported via `nextSteps`.\n */\n\n/**\n * Rewrite a base.py still in the generated shape. Already migrated and missing\n * files are left alone; a diverged one is reported via nextSteps.\n */\nconst migrateBasePy = (\n tree: Tree,\n filePath: string,\n nextSteps: string[],\n): void => {\n if (!tree.exists(filePath)) {\n return;\n }\n\n const content = tree.read(filePath, 'utf-8') ?? '';\n if (content.includes(META_BINDING)) {\n return; // Already migrated (or user-added).\n }\n\n const shape = findMetaBlockShape(content, UNTYPED_META_HEADER);\n if (!shape || !content.includes(UNTYPED_IMPORT)) {\n nextSteps.push(divergedMetaNextStep(filePath));\n return;\n }\n\n tree.write(\n filePath,\n bindMetaBlock(\n content.replace(UNTYPED_IMPORT, TYPED_IMPORT),\n UNTYPED_META_HEADER,\n shape,\n ),\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const filePath of pyDynamoDbBasePyPaths(tree)) {\n migrateBasePy(tree, filePath, nextSteps);\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["bindMetaBlock","divergedMetaNextStep","findMetaBlockShape","META_BINDING","pyDynamoDbBasePyPaths","TYPED_IMPORT","UNTYPED_IMPORT","UNTYPED_META_HEADER","formatFilesInSubtree","migrateBasePy","tree","filePath","nextSteps","exists","content","read","includes","shape","push","write","replace","migration"],"mappings":"AAAA;;;CAGC,GAED,SACEA,aAAa,EACbC,oBAAoB,EACpBC,kBAAkB,EAClBC,YAAY,EACZC,qBAAqB,EACrBC,YAAY,EACZC,cAAc,EACdC,mBAAmB,QACd,0CAA0C;AACjD,SAASC,oBAAoB,QAAQ,2BAA2B;AAEhE;;;;;;;;;;;;;;;;;;;CAmBC,GAED;;;CAGC,GACD,MAAMC,gBAAgB,CACpBC,MACAC,UACAC;IAEA,IAAI,CAACF,KAAKG,MAAM,CAACF,WAAW;QAC1B;IACF;IAEA,MAAMG,UAAUJ,KAAKK,IAAI,CAACJ,UAAU,YAAY;IAChD,IAAIG,QAAQE,QAAQ,CAACb,eAAe;QAClC,QAAQ,oCAAoC;IAC9C;IAEA,MAAMc,QAAQf,mBAAmBY,SAASP;IAC1C,IAAI,CAACU,SAAS,CAACH,QAAQE,QAAQ,CAACV,iBAAiB;QAC/CM,UAAUM,IAAI,CAACjB,qBAAqBU;QACpC;IACF;IAEAD,KAAKS,KAAK,CACRR,UACAX,cACEc,QAAQM,OAAO,CAACd,gBAAgBD,eAChCE,qBACAU;AAGN;AAEA,eAAe,eAAeI,UAC5BX,IAAU;IAEV,MAAME,YAAsB,EAAE;IAE9B,KAAK,MAAMD,YAAYP,sBAAsBM,MAAO;QAClDD,cAAcC,MAAMC,UAAUC;IAChC;IAEA,MAAMJ,qBAAqBE;IAE3B,OAAO;QAAEE;IAAU;AACrB"}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/ import { getProjects, readNxJson, updateNxJson, updateProjectConfiguration } from "@nx/devkit";
|
|
5
5
|
import { PYTHON_TEST_FILE_EXCLUSIONS } from "../../../py/project/generator.js";
|
|
6
|
-
import { DEPENDENT_TASKS_OUTPUT_FILES_INPUT } from "../../../ts/lib/generator.js";
|
|
7
6
|
import { formatFilesInSubtree } from "../../../utils/format.js";
|
|
8
7
|
/**
|
|
9
8
|
* Editing a Python test file used to invalidate the whole build chain for its
|
|
@@ -39,6 +38,13 @@ import { formatFilesInSubtree } from "../../../utils/format.js";
|
|
|
39
38
|
'**/*',
|
|
40
39
|
'dist/**'
|
|
41
40
|
];
|
|
41
|
+
/**
|
|
42
|
+
* The dependent-task-output glob this migration shipped with at release. A
|
|
43
|
+
* released migration's output must not change, so this is the literal value
|
|
44
|
+
* rather than a reference to ts/lib/generator.ts's current constant, which a
|
|
45
|
+
* later migration (cache-inputs-outputs) widens further and reports through
|
|
46
|
+
* its own stale-pass advisory.
|
|
47
|
+
*/ const RELEASED_DEPENDENT_TASKS_OUTPUT_GLOB = '!{reports,coverage}/**';
|
|
42
48
|
const isPythonProject = (project)=>Object.values(project.targets ?? {}).some((target)=>String(target?.executor ?? '').startsWith('@nxlv/python'));
|
|
43
49
|
/**
|
|
44
50
|
* An OpenAPI spec generation target as the generators vend it: named `openapi`
|
|
@@ -71,7 +77,7 @@ const arraysEqual = (a, b)=>a.length === b.length && a.every((entry, index)=>ent
|
|
|
71
77
|
const defaultInput = nxJson.namedInputs?.default;
|
|
72
78
|
let nextDefault = defaultInput;
|
|
73
79
|
if (defaultInput) {
|
|
74
|
-
const glob =
|
|
80
|
+
const glob = RELEASED_DEPENDENT_TASKS_OUTPUT_GLOB;
|
|
75
81
|
// Only an entry this plugin vended is rewritten; a different glob is the
|
|
76
82
|
// user's own scoping decision.
|
|
77
83
|
nextDefault = defaultInput.map((input)=>typeof input === 'object' && input !== null && 'dependentTasksOutputFiles' in input && PREVIOUSLY_VENDED_GLOBS.includes(input.dependentTasksOutputFiles) ? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.90/0004-python-test-input-scoping/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n getProjects,\n type MigrationReturnObject,\n type ProjectConfiguration,\n readNxJson,\n type TargetConfiguration,\n type Tree,\n updateNxJson,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { PYTHON_TEST_FILE_EXCLUSIONS } from '../../../py/project/generator.js';\nimport { DEPENDENT_TASKS_OUTPUT_FILES_INPUT } from '../../../ts/lib/generator.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\n\n/**\n * Editing a Python test file used to invalidate the whole build chain for its\n * project, and cascade into every downstream project. Two independent causes:\n *\n * 1. The `production` named input excluded TypeScript specs but nothing Python,\n * and the Python build targets read `default` (every file in the project), so\n * a test file was a build input by construction.\n * 2. `default` consumed *every* output of a dependency task. The Python `test`\n * target writes JUnit and coverage reports that pytest stamps wall-clock time\n * into, so each test run minted a new hash that propagated downstream.\n *\n * Targets whose output genuinely cannot contain a test file move to\n * `production`; `typecheck` deliberately stays on `default`, because `ty` checks\n * the tests directory and must re-run when a test changes.\n */\n\n/** The Python targets whose declared output cannot contain a test file. */\nconst PRODUCTION_SCOPED_TARGETS = ['compile', 'bundle-x86', 'bundle-arm'];\n\n/** The inputs those targets read once scoped to production sources. */\nconst PRODUCTION_INPUTS = ['production', '^production'];\n\n/** The inputs those targets previously read. */\nconst PREVIOUS_INPUTS = ['default', '^production'];\n\n/**\n * Dependent-task-output globs this plugin has vended. Any of them is rewritten\n * to the current one; anything else is the user's own scoping decision.\n */\nconst PREVIOUSLY_VENDED_GLOBS = ['**/*', 'dist/**'];\n\nconst isPythonProject = (project: ProjectConfiguration): boolean =>\n Object.values(project.targets ?? {}).some((target) =>\n String(target?.executor ?? '').startsWith('@nxlv/python'),\n );\n\n/**\n * An OpenAPI spec generation target as the generators vend it: named `openapi`\n * or `<agent>-openapi`, running a command, and writing its spec under\n * `dist/.../openapi`. The extra checks keep a user-authored target that merely\n * shares the suffix from being rescoped, since these targets are identified by\n * declaring no `inputs` at all.\n */\nconst isOpenApiTarget = (\n name: string,\n target?: TargetConfiguration,\n): boolean => {\n if (name !== 'openapi' && !name.endsWith('-openapi')) return false;\n if (!target) return true;\n return (\n target.executor === 'nx:run-commands' &&\n (target.outputs ?? []).some((output) => output.includes('/openapi'))\n );\n};\n\nconst arraysEqual = (a: readonly unknown[], b: readonly unknown[]): boolean =>\n a.length === b.length && a.every((entry, index) => entry === b[index]);\n\n/**\n * Excludes Python test files from the `production` named input, and narrows the\n * dependent-task-output input so test reports no longer propagate downstream.\n */\nconst migrateNamedInputs = (tree: Tree, nextSteps: string[]): void => {\n const nxJson = readNxJson(tree);\n if (!nxJson) return;\n\n // Seeded as `['default']` when absent, exactly as the generator does. The\n // targets below are rewritten to read `production`, and Nx fails hard on a\n // named input that is not defined, so the two must not diverge.\n const production = nxJson.namedInputs?.production ?? ['default'];\n const nextProduction = [\n ...production,\n ...PYTHON_TEST_FILE_EXCLUSIONS.filter(\n (exclusion) => !production.includes(exclusion),\n ),\n ];\n\n const defaultInput = nxJson.namedInputs?.default;\n let nextDefault = defaultInput;\n if (defaultInput) {\n const glob = DEPENDENT_TASKS_OUTPUT_FILES_INPUT.dependentTasksOutputFiles;\n // Only an entry this plugin vended is rewritten; a different glob is the\n // user's own scoping decision.\n nextDefault = defaultInput.map((input) =>\n typeof input === 'object' &&\n input !== null &&\n 'dependentTasksOutputFiles' in input &&\n PREVIOUSLY_VENDED_GLOBS.includes(\n input.dependentTasksOutputFiles as string,\n )\n ? { ...input, dependentTasksOutputFiles: glob }\n : input,\n );\n const hadVendedEntry = nextDefault.some(\n (input) =>\n typeof input === 'object' &&\n input !== null &&\n 'dependentTasksOutputFiles' in input &&\n input.dependentTasksOutputFiles === glob,\n );\n const hasCustomEntry = defaultInput.some(\n (input) =>\n typeof input === 'object' &&\n input !== null &&\n 'dependentTasksOutputFiles' in input &&\n !PREVIOUSLY_VENDED_GLOBS.includes(\n input.dependentTasksOutputFiles as string,\n ) &&\n input.dependentTasksOutputFiles !== glob,\n );\n if (!hadVendedEntry && hasCustomEntry) {\n nextSteps.push(\n `nx.json's 'default' named input scopes 'dependentTasksOutputFiles' to a custom glob — left untouched. Narrow it to '${glob}' so timestamped test reports do not invalidate downstream tasks.`,\n );\n }\n }\n\n updateNxJson(tree, {\n ...nxJson,\n namedInputs: {\n ...nxJson.namedInputs,\n production: nextProduction,\n ...(nextDefault ? { default: nextDefault } : {}),\n },\n });\n};\n\n/**\n * Scopes a Python project's build-chain targets to production sources. Only\n * targets still carrying the inputs the generator vended are rewritten, so a\n * project whose inputs the user has tuned is reported rather than clobbered.\n */\nconst migrateProjectTargets = (\n tree: Tree,\n projectName: string,\n project: ProjectConfiguration,\n nextSteps: string[],\n): void => {\n let changed = false;\n\n for (const [targetName, target] of Object.entries(project.targets ?? {})) {\n if (\n !PRODUCTION_SCOPED_TARGETS.includes(targetName) &&\n !isOpenApiTarget(targetName, target)\n ) {\n continue;\n }\n\n const inputs = target.inputs;\n\n // Already migrated, so a re-run is a no-op.\n if (inputs && arraysEqual(inputs as unknown[], PRODUCTION_INPUTS)) {\n continue;\n }\n\n // The OpenAPI targets declared no inputs at all, so they fell back to\n // `default`. Everything else carried the vended pair.\n const isVendedShape = inputs\n ? arraysEqual(inputs as unknown[], PREVIOUS_INPUTS)\n : isOpenApiTarget(targetName, target);\n\n if (!isVendedShape) {\n nextSteps.push(\n `${projectName}:${targetName}: 'inputs' has diverged from the generated shape — left untouched. Scope it to ['production', '^production'] so Python test edits do not invalidate it.`,\n );\n continue;\n }\n\n target.inputs = [...PRODUCTION_INPUTS];\n changed = true;\n }\n\n if (changed) {\n updateProjectConfiguration(tree, projectName, project);\n }\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n migrateNamedInputs(tree, nextSteps);\n\n for (const [projectName, project] of getProjects(tree)) {\n if (!isPythonProject(project)) continue;\n migrateProjectTargets(tree, projectName, project, nextSteps);\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["getProjects","readNxJson","updateNxJson","updateProjectConfiguration","PYTHON_TEST_FILE_EXCLUSIONS","DEPENDENT_TASKS_OUTPUT_FILES_INPUT","formatFilesInSubtree","PRODUCTION_SCOPED_TARGETS","PRODUCTION_INPUTS","PREVIOUS_INPUTS","PREVIOUSLY_VENDED_GLOBS","isPythonProject","project","Object","values","targets","some","target","String","executor","startsWith","isOpenApiTarget","name","endsWith","outputs","output","includes","arraysEqual","a","b","length","every","entry","index","migrateNamedInputs","tree","nextSteps","nxJson","production","namedInputs","nextProduction","filter","exclusion","defaultInput","default","nextDefault","glob","dependentTasksOutputFiles","map","input","hadVendedEntry","hasCustomEntry","push","migrateProjectTargets","projectName","changed","targetName","entries","inputs","isVendedShape","migration"],"mappings":"AAAA;;;CAGC,GACD,SACEA,WAAW,EAGXC,UAAU,EAGVC,YAAY,EACZC,0BAA0B,QACrB,aAAa;AACpB,SAASC,2BAA2B,QAAQ,mCAAmC;AAC/E,SAASC,kCAAkC,QAAQ,+BAA+B;AAClF,SAASC,oBAAoB,QAAQ,2BAA2B;AAEhE;;;;;;;;;;;;;;CAcC,GAED,yEAAyE,GACzE,MAAMC,4BAA4B;IAAC;IAAW;IAAc;CAAa;AAEzE,qEAAqE,GACrE,MAAMC,oBAAoB;IAAC;IAAc;CAAc;AAEvD,8CAA8C,GAC9C,MAAMC,kBAAkB;IAAC;IAAW;CAAc;AAElD;;;CAGC,GACD,MAAMC,0BAA0B;IAAC;IAAQ;CAAU;AAEnD,MAAMC,kBAAkB,CAACC,UACvBC,OAAOC,MAAM,CAACF,QAAQG,OAAO,IAAI,CAAC,GAAGC,IAAI,CAAC,CAACC,SACzCC,OAAOD,QAAQE,YAAY,IAAIC,UAAU,CAAC;AAG9C;;;;;;CAMC,GACD,MAAMC,kBAAkB,CACtBC,MACAL;IAEA,IAAIK,SAAS,aAAa,CAACA,KAAKC,QAAQ,CAAC,aAAa,OAAO;IAC7D,IAAI,CAACN,QAAQ,OAAO;IACpB,OACEA,OAAOE,QAAQ,KAAK,qBACpB,AAACF,CAAAA,OAAOO,OAAO,IAAI,EAAE,AAAD,EAAGR,IAAI,CAAC,CAACS,SAAWA,OAAOC,QAAQ,CAAC;AAE5D;AAEA,MAAMC,cAAc,CAACC,GAAuBC,IAC1CD,EAAEE,MAAM,KAAKD,EAAEC,MAAM,IAAIF,EAAEG,KAAK,CAAC,CAACC,OAAOC,QAAUD,UAAUH,CAAC,CAACI,MAAM;AAEvE;;;CAGC,GACD,MAAMC,qBAAqB,CAACC,MAAYC;IACtC,MAAMC,SAASpC,WAAWkC;IAC1B,IAAI,CAACE,QAAQ;IAEb,0EAA0E;IAC1E,2EAA2E;IAC3E,gEAAgE;IAChE,MAAMC,aAAaD,OAAOE,WAAW,EAAED,cAAc;QAAC;KAAU;IAChE,MAAME,iBAAiB;WAClBF;WACAlC,4BAA4BqC,MAAM,CACnC,CAACC,YAAc,CAACJ,WAAWZ,QAAQ,CAACgB;KAEvC;IAED,MAAMC,eAAeN,OAAOE,WAAW,EAAEK;IACzC,IAAIC,cAAcF;IAClB,IAAIA,cAAc;QAChB,MAAMG,OAAOzC,mCAAmC0C,yBAAyB;QACzE,yEAAyE;QACzE,+BAA+B;QAC/BF,cAAcF,aAAaK,GAAG,CAAC,CAACC,QAC9B,OAAOA,UAAU,YACjBA,UAAU,QACV,+BAA+BA,SAC/BvC,wBAAwBgB,QAAQ,CAC9BuB,MAAMF,yBAAyB,IAE7B;gBAAE,GAAGE,KAAK;gBAAEF,2BAA2BD;YAAK,IAC5CG;QAEN,MAAMC,iBAAiBL,YAAY7B,IAAI,CACrC,CAACiC,QACC,OAAOA,UAAU,YACjBA,UAAU,QACV,+BAA+BA,SAC/BA,MAAMF,yBAAyB,KAAKD;QAExC,MAAMK,iBAAiBR,aAAa3B,IAAI,CACtC,CAACiC,QACC,OAAOA,UAAU,YACjBA,UAAU,QACV,+BAA+BA,SAC/B,CAACvC,wBAAwBgB,QAAQ,CAC/BuB,MAAMF,yBAAyB,KAEjCE,MAAMF,yBAAyB,KAAKD;QAExC,IAAI,CAACI,kBAAkBC,gBAAgB;YACrCf,UAAUgB,IAAI,CACZ,CAAC,oHAAoH,EAAEN,KAAK,iEAAiE,CAAC;QAElM;IACF;IAEA5C,aAAaiC,MAAM;QACjB,GAAGE,MAAM;QACTE,aAAa;YACX,GAAGF,OAAOE,WAAW;YACrBD,YAAYE;YACZ,GAAIK,cAAc;gBAAED,SAASC;YAAY,IAAI,CAAC,CAAC;QACjD;IACF;AACF;AAEA;;;;CAIC,GACD,MAAMQ,wBAAwB,CAC5BlB,MACAmB,aACA1C,SACAwB;IAEA,IAAImB,UAAU;IAEd,KAAK,MAAM,CAACC,YAAYvC,OAAO,IAAIJ,OAAO4C,OAAO,CAAC7C,QAAQG,OAAO,IAAI,CAAC,GAAI;QACxE,IACE,CAACR,0BAA0BmB,QAAQ,CAAC8B,eACpC,CAACnC,gBAAgBmC,YAAYvC,SAC7B;YACA;QACF;QAEA,MAAMyC,SAASzC,OAAOyC,MAAM;QAE5B,4CAA4C;QAC5C,IAAIA,UAAU/B,YAAY+B,QAAqBlD,oBAAoB;YACjE;QACF;QAEA,sEAAsE;QACtE,sDAAsD;QACtD,MAAMmD,gBAAgBD,SAClB/B,YAAY+B,QAAqBjD,mBACjCY,gBAAgBmC,YAAYvC;QAEhC,IAAI,CAAC0C,eAAe;YAClBvB,UAAUgB,IAAI,CACZ,GAAGE,YAAY,CAAC,EAAEE,WAAW,uJAAuJ,CAAC;YAEvL;QACF;QAEAvC,OAAOyC,MAAM,GAAG;eAAIlD;SAAkB;QACtC+C,UAAU;IACZ;IAEA,IAAIA,SAAS;QACXpD,2BAA2BgC,MAAMmB,aAAa1C;IAChD;AACF;AAEA,eAAe,eAAegD,UAC5BzB,IAAU;IAEV,MAAMC,YAAsB,EAAE;IAE9BF,mBAAmBC,MAAMC;IAEzB,KAAK,MAAM,CAACkB,aAAa1C,QAAQ,IAAIZ,YAAYmC,MAAO;QACtD,IAAI,CAACxB,gBAAgBC,UAAU;QAC/ByC,sBAAsBlB,MAAMmB,aAAa1C,SAASwB;IACpD;IAEA,MAAM9B,qBAAqB6B;IAE3B,OAAO;QAAEC;IAAU;AACrB"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.90/0004-python-test-input-scoping/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n getProjects,\n type MigrationReturnObject,\n type ProjectConfiguration,\n readNxJson,\n type TargetConfiguration,\n type Tree,\n updateNxJson,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { PYTHON_TEST_FILE_EXCLUSIONS } from '../../../py/project/generator.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\n\n/**\n * Editing a Python test file used to invalidate the whole build chain for its\n * project, and cascade into every downstream project. Two independent causes:\n *\n * 1. The `production` named input excluded TypeScript specs but nothing Python,\n * and the Python build targets read `default` (every file in the project), so\n * a test file was a build input by construction.\n * 2. `default` consumed *every* output of a dependency task. The Python `test`\n * target writes JUnit and coverage reports that pytest stamps wall-clock time\n * into, so each test run minted a new hash that propagated downstream.\n *\n * Targets whose output genuinely cannot contain a test file move to\n * `production`; `typecheck` deliberately stays on `default`, because `ty` checks\n * the tests directory and must re-run when a test changes.\n */\n\n/** The Python targets whose declared output cannot contain a test file. */\nconst PRODUCTION_SCOPED_TARGETS = ['compile', 'bundle-x86', 'bundle-arm'];\n\n/** The inputs those targets read once scoped to production sources. */\nconst PRODUCTION_INPUTS = ['production', '^production'];\n\n/** The inputs those targets previously read. */\nconst PREVIOUS_INPUTS = ['default', '^production'];\n\n/**\n * Dependent-task-output globs this plugin has vended. Any of them is rewritten\n * to the current one; anything else is the user's own scoping decision.\n */\nconst PREVIOUSLY_VENDED_GLOBS = ['**/*', 'dist/**'];\n\n/**\n * The dependent-task-output glob this migration shipped with at release. A\n * released migration's output must not change, so this is the literal value\n * rather than a reference to ts/lib/generator.ts's current constant, which a\n * later migration (cache-inputs-outputs) widens further and reports through\n * its own stale-pass advisory.\n */\nconst RELEASED_DEPENDENT_TASKS_OUTPUT_GLOB = '!{reports,coverage}/**';\n\nconst isPythonProject = (project: ProjectConfiguration): boolean =>\n Object.values(project.targets ?? {}).some((target) =>\n String(target?.executor ?? '').startsWith('@nxlv/python'),\n );\n\n/**\n * An OpenAPI spec generation target as the generators vend it: named `openapi`\n * or `<agent>-openapi`, running a command, and writing its spec under\n * `dist/.../openapi`. The extra checks keep a user-authored target that merely\n * shares the suffix from being rescoped, since these targets are identified by\n * declaring no `inputs` at all.\n */\nconst isOpenApiTarget = (\n name: string,\n target?: TargetConfiguration,\n): boolean => {\n if (name !== 'openapi' && !name.endsWith('-openapi')) return false;\n if (!target) return true;\n return (\n target.executor === 'nx:run-commands' &&\n (target.outputs ?? []).some((output) => output.includes('/openapi'))\n );\n};\n\nconst arraysEqual = (a: readonly unknown[], b: readonly unknown[]): boolean =>\n a.length === b.length && a.every((entry, index) => entry === b[index]);\n\n/**\n * Excludes Python test files from the `production` named input, and narrows the\n * dependent-task-output input so test reports no longer propagate downstream.\n */\nconst migrateNamedInputs = (tree: Tree, nextSteps: string[]): void => {\n const nxJson = readNxJson(tree);\n if (!nxJson) return;\n\n // Seeded as `['default']` when absent, exactly as the generator does. The\n // targets below are rewritten to read `production`, and Nx fails hard on a\n // named input that is not defined, so the two must not diverge.\n const production = nxJson.namedInputs?.production ?? ['default'];\n const nextProduction = [\n ...production,\n ...PYTHON_TEST_FILE_EXCLUSIONS.filter(\n (exclusion) => !production.includes(exclusion),\n ),\n ];\n\n const defaultInput = nxJson.namedInputs?.default;\n let nextDefault = defaultInput;\n if (defaultInput) {\n const glob = RELEASED_DEPENDENT_TASKS_OUTPUT_GLOB;\n // Only an entry this plugin vended is rewritten; a different glob is the\n // user's own scoping decision.\n nextDefault = defaultInput.map((input) =>\n typeof input === 'object' &&\n input !== null &&\n 'dependentTasksOutputFiles' in input &&\n PREVIOUSLY_VENDED_GLOBS.includes(\n input.dependentTasksOutputFiles as string,\n )\n ? { ...input, dependentTasksOutputFiles: glob }\n : input,\n );\n const hadVendedEntry = nextDefault.some(\n (input) =>\n typeof input === 'object' &&\n input !== null &&\n 'dependentTasksOutputFiles' in input &&\n input.dependentTasksOutputFiles === glob,\n );\n const hasCustomEntry = defaultInput.some(\n (input) =>\n typeof input === 'object' &&\n input !== null &&\n 'dependentTasksOutputFiles' in input &&\n !PREVIOUSLY_VENDED_GLOBS.includes(\n input.dependentTasksOutputFiles as string,\n ) &&\n input.dependentTasksOutputFiles !== glob,\n );\n if (!hadVendedEntry && hasCustomEntry) {\n nextSteps.push(\n `nx.json's 'default' named input scopes 'dependentTasksOutputFiles' to a custom glob — left untouched. Narrow it to '${glob}' so timestamped test reports do not invalidate downstream tasks.`,\n );\n }\n }\n\n updateNxJson(tree, {\n ...nxJson,\n namedInputs: {\n ...nxJson.namedInputs,\n production: nextProduction,\n ...(nextDefault ? { default: nextDefault } : {}),\n },\n });\n};\n\n/**\n * Scopes a Python project's build-chain targets to production sources. Only\n * targets still carrying the inputs the generator vended are rewritten, so a\n * project whose inputs the user has tuned is reported rather than clobbered.\n */\nconst migrateProjectTargets = (\n tree: Tree,\n projectName: string,\n project: ProjectConfiguration,\n nextSteps: string[],\n): void => {\n let changed = false;\n\n for (const [targetName, target] of Object.entries(project.targets ?? {})) {\n if (\n !PRODUCTION_SCOPED_TARGETS.includes(targetName) &&\n !isOpenApiTarget(targetName, target)\n ) {\n continue;\n }\n\n const inputs = target.inputs;\n\n // Already migrated, so a re-run is a no-op.\n if (inputs && arraysEqual(inputs as unknown[], PRODUCTION_INPUTS)) {\n continue;\n }\n\n // The OpenAPI targets declared no inputs at all, so they fell back to\n // `default`. Everything else carried the vended pair.\n const isVendedShape = inputs\n ? arraysEqual(inputs as unknown[], PREVIOUS_INPUTS)\n : isOpenApiTarget(targetName, target);\n\n if (!isVendedShape) {\n nextSteps.push(\n `${projectName}:${targetName}: 'inputs' has diverged from the generated shape — left untouched. Scope it to ['production', '^production'] so Python test edits do not invalidate it.`,\n );\n continue;\n }\n\n target.inputs = [...PRODUCTION_INPUTS];\n changed = true;\n }\n\n if (changed) {\n updateProjectConfiguration(tree, projectName, project);\n }\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n migrateNamedInputs(tree, nextSteps);\n\n for (const [projectName, project] of getProjects(tree)) {\n if (!isPythonProject(project)) continue;\n migrateProjectTargets(tree, projectName, project, nextSteps);\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["getProjects","readNxJson","updateNxJson","updateProjectConfiguration","PYTHON_TEST_FILE_EXCLUSIONS","formatFilesInSubtree","PRODUCTION_SCOPED_TARGETS","PRODUCTION_INPUTS","PREVIOUS_INPUTS","PREVIOUSLY_VENDED_GLOBS","RELEASED_DEPENDENT_TASKS_OUTPUT_GLOB","isPythonProject","project","Object","values","targets","some","target","String","executor","startsWith","isOpenApiTarget","name","endsWith","outputs","output","includes","arraysEqual","a","b","length","every","entry","index","migrateNamedInputs","tree","nextSteps","nxJson","production","namedInputs","nextProduction","filter","exclusion","defaultInput","default","nextDefault","glob","map","input","dependentTasksOutputFiles","hadVendedEntry","hasCustomEntry","push","migrateProjectTargets","projectName","changed","targetName","entries","inputs","isVendedShape","migration"],"mappings":"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"}
|
|
@@ -46,12 +46,23 @@ const clientClassName = `${asyncPrefix}${className}`;
|
|
|
46
46
|
const streamType = (item) => `${isAsync ? 'AsyncGenerator' : 'Iterator'}[${item}]`;
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* The `collections.abc` names the module imports.
|
|
50
|
-
*
|
|
51
|
-
*
|
|
49
|
+
* The `collections.abc` names the module imports. `_stream_with_url` is
|
|
50
|
+
* decorated `@contextmanager`/`@asynccontextmanager`, which must be annotated
|
|
51
|
+
* `Generator`/`AsyncGenerator` rather than `Iterator`/`AsyncIterator` — a type
|
|
52
|
+
* checker (e.g. `ty`) rejects the iterator form on a context-manager function.
|
|
53
|
+
* Async spells both names `AsyncGenerator`; sync needs both `Iterator` (an
|
|
54
|
+
* operation's stream) and `Generator` (the context manager).
|
|
52
55
|
*/
|
|
53
56
|
const streamAbc = isAsync ? 'AsyncGenerator' : 'Iterator';
|
|
54
|
-
const
|
|
57
|
+
const contextAbcName = isAsync ? 'AsyncGenerator' : 'Generator';
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The declared return type of a `@contextmanager`/`@asynccontextmanager`
|
|
61
|
+
* function. `AsyncGenerator` takes a send type, `Generator` also takes a
|
|
62
|
+
* return type — both left as `None` since nothing here uses either.
|
|
63
|
+
*/
|
|
64
|
+
const contextReturnType = (item) =>
|
|
65
|
+
isAsync ? `AsyncGenerator[${item}, None]` : `Generator[${item}, None, None]`;
|
|
55
66
|
|
|
56
67
|
const resultTypeFor = (op) => {
|
|
57
68
|
if (op.isStreaming) {
|
|
@@ -329,7 +340,7 @@ const tagGroups = operationsByTag;
|
|
|
329
340
|
const hasContentJsonParams = allOperations.some(op => (op.parameters || []).some(p => p.in !== 'body' && p.mediaType === 'application/json'));
|
|
330
341
|
const hasPartContentTypes = allOperations.some(op => op.parametersBody && op.parametersBody.partContentTypes);
|
|
331
342
|
const hasAllowReservedParams = allOperations.some(op => (op.parameters || []).some(p => p.in === 'query' && p.allowReserved));
|
|
332
|
-
const abcImports = [...new Set([streamAbc, ...(hasAllowReservedParams ? [
|
|
343
|
+
const abcImports = [...new Set([streamAbc, ...(hasAllowReservedParams ? [contextAbcName] : [])])].join(', ');
|
|
333
344
|
// A response declaring several media types is read through `_json_body`.
|
|
334
345
|
const hasMultiMediaResponse = allOperations.some(op => (op.responses || []).some(r => (r.mediaTypes || []).length > 1));
|
|
335
346
|
// An untyped streamed item is decoded with `json.loads`, which needs the import.
|
|
@@ -681,7 +692,7 @@ class <%- clientClassName %>:
|
|
|
681
692
|
@<%- contextManager %>
|
|
682
693
|
<%- kw.def %> _stream_with_url(
|
|
683
694
|
self, final_url: str, **kwargs: Any
|
|
684
|
-
) -> <%-
|
|
695
|
+
) -> <%- contextReturnType('httpx.Response') %>:
|
|
685
696
|
# The streaming counterpart of `_send_with_url`: `stream()` would rebuild
|
|
686
697
|
# the query from the client's params and percent-encode a hand-built one.
|
|
687
698
|
request = self._client.build_request(**kwargs)
|
|
@@ -9,7 +9,7 @@ from typing import Any<%- hasNeverErrorType ? ', Never' : '' %>
|
|
|
9
9
|
from . import types
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class ApiError(Exception):
|
|
12
|
+
class ApiError[ErrorT](Exception):
|
|
13
13
|
"""Base class for all API errors.
|
|
14
14
|
|
|
15
15
|
Subclasses are generated per-operation so `error` can be narrowed by the
|
|
@@ -18,7 +18,7 @@ class ApiError(Exception):
|
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
status: int
|
|
21
|
-
error:
|
|
21
|
+
error: ErrorT
|
|
22
22
|
|
|
23
23
|
def __init__(self, status: int, error: Any = None) -> None:
|
|
24
24
|
super().__init__(f"API returned status {status}")
|
|
@@ -30,8 +30,6 @@ class ApiError(Exception):
|
|
|
30
30
|
_%>
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
class <%- shape.exceptionClassName %>(ApiError):
|
|
33
|
+
class <%- shape.exceptionClassName %>(ApiError[<% if (shape.entries.length === 0) { %>Never<% } else { %>types.<%- shape.unionTypeName %><% } %>]):
|
|
34
34
|
"""Raised when `<%- op.operationIdSnakeCase || op.name %>` returns a non-success status."""
|
|
35
|
-
|
|
36
|
-
error: <% if (shape.entries.length === 0) { %>Never<% } else { %>types.<%- shape.unionTypeName %><% } %>
|
|
37
35
|
<%_ }); _%>
|
|
@@ -739,7 +739,7 @@ exports[`py#dynamodb generator > should generate the dynamodb project > packages
|
|
|
739
739
|
|
|
740
740
|
from pynamodb.attributes import DiscriminatorAttribute, UnicodeAttribute
|
|
741
741
|
from pynamodb.indexes import AllProjection, GlobalSecondaryIndex
|
|
742
|
-
from pynamodb.models import Model
|
|
742
|
+
from pynamodb.models import MetaProtocol, Model
|
|
743
743
|
|
|
744
744
|
from ..client import _local_dev, get_table_name, is_local
|
|
745
745
|
|
|
@@ -775,13 +775,15 @@ class BaseModel(Model):
|
|
|
775
775
|
See https://pynamodb.readthedocs.io/en/stable/polymorphism.html
|
|
776
776
|
"""
|
|
777
777
|
|
|
778
|
-
class
|
|
778
|
+
class _Meta(MetaProtocol):
|
|
779
779
|
table_name: str
|
|
780
780
|
region = os.environ.get("AWS_REGION", "us-east-1")
|
|
781
781
|
host = f"http://localhost:{_local_dev()['port']}" if is_local() else None
|
|
782
782
|
aws_access_key_id = "UNUSED" if is_local() else None
|
|
783
783
|
aws_secret_access_key = "UNUSED" if is_local() else None
|
|
784
784
|
|
|
785
|
+
Meta: MetaProtocol = _Meta
|
|
786
|
+
|
|
785
787
|
entity_type = DiscriminatorAttribute()
|
|
786
788
|
|
|
787
789
|
pk = UnicodeAttribute(hash_key=True)
|
|
@@ -1459,7 +1461,7 @@ exports[`py#dynamodb generator > should generate with infra=none then upgrade to
|
|
|
1459
1461
|
|
|
1460
1462
|
from pynamodb.attributes import DiscriminatorAttribute, UnicodeAttribute
|
|
1461
1463
|
from pynamodb.indexes import AllProjection, GlobalSecondaryIndex
|
|
1462
|
-
from pynamodb.models import Model
|
|
1464
|
+
from pynamodb.models import MetaProtocol, Model
|
|
1463
1465
|
|
|
1464
1466
|
from ..client import _local_dev, get_table_name, is_local
|
|
1465
1467
|
|
|
@@ -1495,13 +1497,15 @@ class BaseModel(Model):
|
|
|
1495
1497
|
See https://pynamodb.readthedocs.io/en/stable/polymorphism.html
|
|
1496
1498
|
"""
|
|
1497
1499
|
|
|
1498
|
-
class
|
|
1500
|
+
class _Meta(MetaProtocol):
|
|
1499
1501
|
table_name: str
|
|
1500
1502
|
region = os.environ.get("AWS_REGION", "us-east-1")
|
|
1501
1503
|
host = f"http://localhost:{_local_dev()['port']}" if is_local() else None
|
|
1502
1504
|
aws_access_key_id = "UNUSED" if is_local() else None
|
|
1503
1505
|
aws_secret_access_key = "UNUSED" if is_local() else None
|
|
1504
1506
|
|
|
1507
|
+
Meta: MetaProtocol = _Meta
|
|
1508
|
+
|
|
1505
1509
|
entity_type = DiscriminatorAttribute()
|
|
1506
1510
|
|
|
1507
1511
|
pk = UnicodeAttribute(hash_key=True)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { type Tree } from '@nx/devkit';
|
|
6
|
+
/**
|
|
7
|
+
* The shapes of the `BaseModel` Meta block py#dynamodb has vended in
|
|
8
|
+
* `<module>/entities/base.py`, shared by the migrations that carry an existing
|
|
9
|
+
* base.py to the shape the generator writes today.
|
|
10
|
+
*/
|
|
11
|
+
/** The pynamodb.models import of a base.py whose Meta is untyped. */
|
|
12
|
+
export declare const UNTYPED_IMPORT = "\nfrom pynamodb.models import Model\n";
|
|
13
|
+
/** The pynamodb.models import of a base.py whose Meta is typed. */
|
|
14
|
+
export declare const TYPED_IMPORT = "\nfrom pynamodb.models import MetaProtocol, Model\n";
|
|
15
|
+
/** A nested Meta that types none of pynamodb's `MetaProtocol` members. */
|
|
16
|
+
export declare const UNTYPED_META_HEADER = " class Meta:\n";
|
|
17
|
+
/** A nested Meta that subclasses `MetaProtocol` but narrows the mutable `Meta`. */
|
|
18
|
+
export declare const NESTED_PROTOCOL_META_HEADER = " class Meta(MetaProtocol):\n";
|
|
19
|
+
/** The block the generator writes today, bound to `Meta` by `META_BINDING`. */
|
|
20
|
+
export declare const BOUND_META_HEADER = " class _Meta(MetaProtocol):\n";
|
|
21
|
+
/** Binds `Meta` to the block while keeping pynamodb's declared type. */
|
|
22
|
+
export declare const META_BINDING = " Meta: MetaProtocol = _Meta\n";
|
|
23
|
+
/** One way the generator has written the block's body. */
|
|
24
|
+
export interface MetaBlockShape {
|
|
25
|
+
readonly helper: string;
|
|
26
|
+
readonly quote: string;
|
|
27
|
+
}
|
|
28
|
+
/** Every shape of the block's body the generator has written. */
|
|
29
|
+
export declare const META_BLOCK_SHAPES: readonly MetaBlockShape[];
|
|
30
|
+
/** The vended `BaseModel` Meta block, up to the blank line that ends it. */
|
|
31
|
+
export declare const metaBlock: (header: string, { helper, quote }: MetaBlockShape, binding?: string) => string;
|
|
32
|
+
/** The shape of `content`'s block under `header`, if it has a vended one. */
|
|
33
|
+
export declare const findMetaBlockShape: (content: string, header: string) => MetaBlockShape | undefined;
|
|
34
|
+
/** Rewrite the vended block under `header` to the shape vended today. */
|
|
35
|
+
export declare const bindMetaBlock: (content: string, header: string, shape: MetaBlockShape) => string;
|
|
36
|
+
/** The nextSteps entry for a base.py whose Meta or import has diverged. */
|
|
37
|
+
export declare const divergedMetaNextStep: (filePath: string) => string;
|
|
38
|
+
/** The base.py path of every py#dynamodb project, whether or not it exists. */
|
|
39
|
+
export declare const pyDynamoDbBasePyPaths: (tree: Tree) => string[];
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/ import { getProjects, joinPathFragments } from "@nx/devkit";
|
|
5
|
+
import { PY_DYNAMODB_GENERATOR_INFO } from "./generator.js";
|
|
6
|
+
/**
|
|
7
|
+
* The shapes of the `BaseModel` Meta block py#dynamodb has vended in
|
|
8
|
+
* `<module>/entities/base.py`, shared by the migrations that carry an existing
|
|
9
|
+
* base.py to the shape the generator writes today.
|
|
10
|
+
*/ /** The pynamodb.models import of a base.py whose Meta is untyped. */ export const UNTYPED_IMPORT = '\nfrom pynamodb.models import Model\n';
|
|
11
|
+
/** The pynamodb.models import of a base.py whose Meta is typed. */ export const TYPED_IMPORT = '\nfrom pynamodb.models import MetaProtocol, Model\n';
|
|
12
|
+
/** A nested Meta that types none of pynamodb's `MetaProtocol` members. */ export const UNTYPED_META_HEADER = ' class Meta:\n';
|
|
13
|
+
/** A nested Meta that subclasses `MetaProtocol` but narrows the mutable `Meta`. */ export const NESTED_PROTOCOL_META_HEADER = ' class Meta(MetaProtocol):\n';
|
|
14
|
+
/** The block the generator writes today, bound to `Meta` by `META_BINDING`. */ export const BOUND_META_HEADER = ' class _Meta(MetaProtocol):\n';
|
|
15
|
+
/** Binds `Meta` to the block while keeping pynamodb's declared type. */ export const META_BINDING = ' Meta: MetaProtocol = _Meta\n';
|
|
16
|
+
/**
|
|
17
|
+
* The helper the vended `host` line reads the local port through. Workspaces
|
|
18
|
+
* generated before the serve-local -> local-dev rename use `_serve_local`.
|
|
19
|
+
*/ const LOCAL_DEV_HELPERS = [
|
|
20
|
+
'_local_dev',
|
|
21
|
+
'_serve_local'
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* The quote the block's string literals use: the template's single quotes, or
|
|
25
|
+
* the double quotes `ruff format` rewrites them to.
|
|
26
|
+
*/ const QUOTES = [
|
|
27
|
+
'"',
|
|
28
|
+
"'"
|
|
29
|
+
];
|
|
30
|
+
/** Every shape of the block's body the generator has written. */ export const META_BLOCK_SHAPES = LOCAL_DEV_HELPERS.flatMap((helper)=>QUOTES.map((quote)=>({
|
|
31
|
+
helper,
|
|
32
|
+
quote
|
|
33
|
+
})));
|
|
34
|
+
/** The vended `BaseModel` Meta block, up to the blank line that ends it. */ export const metaBlock = (header, { helper, quote }, binding = '')=>`${header} table_name: str
|
|
35
|
+
region = os.environ.get("AWS_REGION", ${quote}us-east-1${quote})
|
|
36
|
+
host = f"http://localhost:{${helper}()['port']}" if is_local() else None
|
|
37
|
+
aws_access_key_id = ${quote}UNUSED${quote} if is_local() else None
|
|
38
|
+
aws_secret_access_key = ${quote}UNUSED${quote} if is_local() else None
|
|
39
|
+
${binding ? `\n${binding}` : ''}
|
|
40
|
+
`;
|
|
41
|
+
/** The shape of `content`'s block under `header`, if it has a vended one. */ export const findMetaBlockShape = (content, header)=>META_BLOCK_SHAPES.find((shape)=>content.includes(metaBlock(header, shape)));
|
|
42
|
+
/** Rewrite the vended block under `header` to the shape vended today. */ export const bindMetaBlock = (content, header, shape)=>content.replace(metaBlock(header, shape), metaBlock(BOUND_META_HEADER, shape, META_BINDING));
|
|
43
|
+
/** The nextSteps entry for a base.py whose Meta or import has diverged. */ export const divergedMetaNextStep = (filePath)=>`${filePath}: its BaseModel Meta class or its pynamodb.models import has been customised, so it was left unchanged. If \`ty check\` reports invalid-attribute-override or invalid-mutable-override for it, declare the class as \`class _Meta(MetaProtocol):\` followed by \`Meta: MetaProtocol = _Meta\`, importing MetaProtocol from pynamodb.models.`;
|
|
44
|
+
/** The base.py path of every py#dynamodb project, whether or not it exists. */ export const pyDynamoDbBasePyPaths = (tree)=>[
|
|
45
|
+
...getProjects(tree).values()
|
|
46
|
+
].flatMap((project)=>{
|
|
47
|
+
const generator = project.metadata?.generator;
|
|
48
|
+
// py#dynamodb writes its package under the project's sourceRoot.
|
|
49
|
+
return generator === PY_DYNAMODB_GENERATOR_INFO.id && project.sourceRoot ? [
|
|
50
|
+
joinPathFragments(project.sourceRoot, 'entities', 'base.py')
|
|
51
|
+
] : [];
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=base-model-meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/py/dynamodb/base-model-meta.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { getProjects, joinPathFragments, type Tree } from '@nx/devkit';\nimport { PY_DYNAMODB_GENERATOR_INFO } from './generator.js';\n\n/**\n * The shapes of the `BaseModel` Meta block py#dynamodb has vended in\n * `<module>/entities/base.py`, shared by the migrations that carry an existing\n * base.py to the shape the generator writes today.\n */\n\n/** The pynamodb.models import of a base.py whose Meta is untyped. */\nexport const UNTYPED_IMPORT = '\\nfrom pynamodb.models import Model\\n';\n\n/** The pynamodb.models import of a base.py whose Meta is typed. */\nexport const TYPED_IMPORT =\n '\\nfrom pynamodb.models import MetaProtocol, Model\\n';\n\n/** A nested Meta that types none of pynamodb's `MetaProtocol` members. */\nexport const UNTYPED_META_HEADER = ' class Meta:\\n';\n\n/** A nested Meta that subclasses `MetaProtocol` but narrows the mutable `Meta`. */\nexport const NESTED_PROTOCOL_META_HEADER = ' class Meta(MetaProtocol):\\n';\n\n/** The block the generator writes today, bound to `Meta` by `META_BINDING`. */\nexport const BOUND_META_HEADER = ' class _Meta(MetaProtocol):\\n';\n\n/** Binds `Meta` to the block while keeping pynamodb's declared type. */\nexport const META_BINDING = ' Meta: MetaProtocol = _Meta\\n';\n\n/**\n * The helper the vended `host` line reads the local port through. Workspaces\n * generated before the serve-local -> local-dev rename use `_serve_local`.\n */\nconst LOCAL_DEV_HELPERS = ['_local_dev', '_serve_local'];\n\n/**\n * The quote the block's string literals use: the template's single quotes, or\n * the double quotes `ruff format` rewrites them to.\n */\nconst QUOTES = ['\"', \"'\"];\n\n/** One way the generator has written the block's body. */\nexport interface MetaBlockShape {\n readonly helper: string;\n readonly quote: string;\n}\n\n/** Every shape of the block's body the generator has written. */\nexport const META_BLOCK_SHAPES: readonly MetaBlockShape[] =\n LOCAL_DEV_HELPERS.flatMap((helper) =>\n QUOTES.map((quote) => ({ helper, quote })),\n );\n\n/** The vended `BaseModel` Meta block, up to the blank line that ends it. */\nexport const metaBlock = (\n header: string,\n { helper, quote }: MetaBlockShape,\n binding = '',\n): string =>\n `${header} table_name: str\n region = os.environ.get(\"AWS_REGION\", ${quote}us-east-1${quote})\n host = f\"http://localhost:{${helper}()['port']}\" if is_local() else None\n aws_access_key_id = ${quote}UNUSED${quote} if is_local() else None\n aws_secret_access_key = ${quote}UNUSED${quote} if is_local() else None\n${binding ? `\\n${binding}` : ''}\n`;\n\n/** The shape of `content`'s block under `header`, if it has a vended one. */\nexport const findMetaBlockShape = (\n content: string,\n header: string,\n): MetaBlockShape | undefined =>\n META_BLOCK_SHAPES.find((shape) => content.includes(metaBlock(header, shape)));\n\n/** Rewrite the vended block under `header` to the shape vended today. */\nexport const bindMetaBlock = (\n content: string,\n header: string,\n shape: MetaBlockShape,\n): string =>\n content.replace(\n metaBlock(header, shape),\n metaBlock(BOUND_META_HEADER, shape, META_BINDING),\n );\n\n/** The nextSteps entry for a base.py whose Meta or import has diverged. */\nexport const divergedMetaNextStep = (filePath: string): string =>\n `${filePath}: its BaseModel Meta class or its pynamodb.models import has been customised, so it was left unchanged. If \\`ty check\\` reports invalid-attribute-override or invalid-mutable-override for it, declare the class as \\`class _Meta(MetaProtocol):\\` followed by \\`Meta: MetaProtocol = _Meta\\`, importing MetaProtocol from pynamodb.models.`;\n\n/** The base.py path of every py#dynamodb project, whether or not it exists. */\nexport const pyDynamoDbBasePyPaths = (tree: Tree): string[] =>\n [...getProjects(tree).values()].flatMap((project) => {\n const generator = (project.metadata as { generator?: string } | undefined)\n ?.generator;\n // py#dynamodb writes its package under the project's sourceRoot.\n return generator === PY_DYNAMODB_GENERATOR_INFO.id && project.sourceRoot\n ? [joinPathFragments(project.sourceRoot, 'entities', 'base.py')]\n : [];\n });\n"],"names":["getProjects","joinPathFragments","PY_DYNAMODB_GENERATOR_INFO","UNTYPED_IMPORT","TYPED_IMPORT","UNTYPED_META_HEADER","NESTED_PROTOCOL_META_HEADER","BOUND_META_HEADER","META_BINDING","LOCAL_DEV_HELPERS","QUOTES","META_BLOCK_SHAPES","flatMap","helper","map","quote","metaBlock","header","binding","findMetaBlockShape","content","find","shape","includes","bindMetaBlock","replace","divergedMetaNextStep","filePath","pyDynamoDbBasePyPaths","tree","values","project","generator","metadata","id","sourceRoot"],"mappings":"AAAA;;;CAGC,GACD,SAASA,WAAW,EAAEC,iBAAiB,QAAmB,aAAa;AACvE,SAASC,0BAA0B,QAAQ,iBAAiB;AAE5D;;;;CAIC,GAED,mEAAmE,GACnE,OAAO,MAAMC,iBAAiB,wCAAwC;AAEtE,iEAAiE,GACjE,OAAO,MAAMC,eACX,sDAAsD;AAExD,wEAAwE,GACxE,OAAO,MAAMC,sBAAsB,oBAAoB;AAEvD,iFAAiF,GACjF,OAAO,MAAMC,8BAA8B,kCAAkC;AAE7E,6EAA6E,GAC7E,OAAO,MAAMC,oBAAoB,mCAAmC;AAEpE,sEAAsE,GACtE,OAAO,MAAMC,eAAe,mCAAmC;AAE/D;;;CAGC,GACD,MAAMC,oBAAoB;IAAC;IAAc;CAAe;AAExD;;;CAGC,GACD,MAAMC,SAAS;IAAC;IAAK;CAAI;AAQzB,+DAA+D,GAC/D,OAAO,MAAMC,oBACXF,kBAAkBG,OAAO,CAAC,CAACC,SACzBH,OAAOI,GAAG,CAAC,CAACC,QAAW,CAAA;YAAEF;YAAQE;QAAM,CAAA,IACvC;AAEJ,0EAA0E,GAC1E,OAAO,MAAMC,YAAY,CACvBC,QACA,EAAEJ,MAAM,EAAEE,KAAK,EAAkB,EACjCG,UAAU,EAAE,GAEZ,GAAGD,OAAO;8CACkC,EAAEF,MAAM,SAAS,EAAEA,MAAM;mCACpC,EAAEF,OAAO;4BAChB,EAAEE,MAAM,MAAM,EAAEA,MAAM;gCAClB,EAAEA,MAAM,MAAM,EAAEA,MAAM;AACtD,EAAEG,UAAU,CAAC,EAAE,EAAEA,SAAS,GAAG,GAAG;AAChC,CAAC,CAAC;AAEF,2EAA2E,GAC3E,OAAO,MAAMC,qBAAqB,CAChCC,SACAH,SAEAN,kBAAkBU,IAAI,CAAC,CAACC,QAAUF,QAAQG,QAAQ,CAACP,UAAUC,QAAQK,SAAS;AAEhF,uEAAuE,GACvE,OAAO,MAAME,gBAAgB,CAC3BJ,SACAH,QACAK,QAEAF,QAAQK,OAAO,CACbT,UAAUC,QAAQK,QAClBN,UAAUT,mBAAmBe,OAAOd,eACpC;AAEJ,yEAAyE,GACzE,OAAO,MAAMkB,uBAAuB,CAACC,WACnC,GAAGA,SAAS,2UAA2U,CAAC,CAAC;AAE3V,6EAA6E,GAC7E,OAAO,MAAMC,wBAAwB,CAACC,OACpC;WAAI7B,YAAY6B,MAAMC,MAAM;KAAG,CAAClB,OAAO,CAAC,CAACmB;QACvC,MAAMC,YAAaD,QAAQE,QAAQ,EAC/BD;QACJ,iEAAiE;QACjE,OAAOA,cAAc9B,2BAA2BgC,EAAE,IAAIH,QAAQI,UAAU,GACpE;YAAClC,kBAAkB8B,QAAQI,UAAU,EAAE,YAAY;SAAW,GAC9D,EAAE;IACR,GAAG"}
|
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
|
|
3
3
|
from pynamodb.attributes import DiscriminatorAttribute, UnicodeAttribute
|
|
4
4
|
from pynamodb.indexes import AllProjection, GlobalSecondaryIndex
|
|
5
|
-
from pynamodb.models import Model
|
|
5
|
+
from pynamodb.models import MetaProtocol, Model
|
|
6
6
|
|
|
7
7
|
from ..client import _local_dev, get_table_name, is_local
|
|
8
8
|
|
|
@@ -38,13 +38,15 @@ class BaseModel(Model):
|
|
|
38
38
|
See https://pynamodb.readthedocs.io/en/stable/polymorphism.html
|
|
39
39
|
"""
|
|
40
40
|
|
|
41
|
-
class
|
|
41
|
+
class _Meta(MetaProtocol):
|
|
42
42
|
table_name: str
|
|
43
43
|
region = os.environ.get("AWS_REGION", 'us-east-1')
|
|
44
44
|
host = f"http://localhost:{_local_dev()['port']}" if is_local() else None
|
|
45
45
|
aws_access_key_id = 'UNUSED' if is_local() else None
|
|
46
46
|
aws_secret_access_key = 'UNUSED' if is_local() else None
|
|
47
47
|
|
|
48
|
+
Meta: MetaProtocol = _Meta
|
|
49
|
+
|
|
48
50
|
entity_type = DiscriminatorAttribute()
|
|
49
51
|
|
|
50
52
|
pk = UnicodeAttribute(hash_key=True)
|
|
@@ -35,6 +35,7 @@ exports[`smithyProjectGenerator > shape libraries > should generate a shape libr
|
|
|
35
35
|
},
|
|
36
36
|
"compile": {
|
|
37
37
|
"cache": true,
|
|
38
|
+
"inputs": ["default"],
|
|
38
39
|
"outputs": [
|
|
39
40
|
"{workspaceRoot}/dist/{projectRoot}/smithy",
|
|
40
41
|
"{workspaceRoot}/dist/{projectRoot}/build/model.json"
|
|
@@ -112,6 +113,7 @@ exports[`smithyProjectGenerator > should generate smithy project with all custom
|
|
|
112
113
|
},
|
|
113
114
|
"compile": {
|
|
114
115
|
"cache": true,
|
|
116
|
+
"inputs": ["default"],
|
|
115
117
|
"outputs": [
|
|
116
118
|
"{workspaceRoot}/dist/{projectRoot}/smithy",
|
|
117
119
|
"{workspaceRoot}/dist/{projectRoot}/build/openapi"
|
|
@@ -132,6 +134,7 @@ exports[`smithyProjectGenerator > should generate smithy project with all custom
|
|
|
132
134
|
},
|
|
133
135
|
"generate-ssdk": {
|
|
134
136
|
"cache": true,
|
|
137
|
+
"inputs": ["default"],
|
|
135
138
|
"outputs": ["{workspaceRoot}/dist/{projectRoot}/build/ssdk"],
|
|
136
139
|
"executor": "nx:run-commands",
|
|
137
140
|
"dependsOn": ["compile"],
|
|
@@ -170,6 +173,7 @@ exports[`smithyProjectGenerator > should generate smithy project with custom dir
|
|
|
170
173
|
},
|
|
171
174
|
"compile": {
|
|
172
175
|
"cache": true,
|
|
176
|
+
"inputs": ["default"],
|
|
173
177
|
"outputs": [
|
|
174
178
|
"{workspaceRoot}/dist/{projectRoot}/smithy",
|
|
175
179
|
"{workspaceRoot}/dist/{projectRoot}/build/openapi"
|
|
@@ -190,6 +194,7 @@ exports[`smithyProjectGenerator > should generate smithy project with custom dir
|
|
|
190
194
|
},
|
|
191
195
|
"generate-ssdk": {
|
|
192
196
|
"cache": true,
|
|
197
|
+
"inputs": ["default"],
|
|
193
198
|
"outputs": ["{workspaceRoot}/dist/{projectRoot}/build/ssdk"],
|
|
194
199
|
"executor": "nx:run-commands",
|
|
195
200
|
"dependsOn": ["compile"],
|
|
@@ -320,6 +325,7 @@ exports[`smithyProjectGenerator > should generate smithy project with default op
|
|
|
320
325
|
},
|
|
321
326
|
"compile": {
|
|
322
327
|
"cache": true,
|
|
328
|
+
"inputs": ["default"],
|
|
323
329
|
"outputs": [
|
|
324
330
|
"{workspaceRoot}/dist/{projectRoot}/smithy",
|
|
325
331
|
"{workspaceRoot}/dist/{projectRoot}/build/openapi"
|
|
@@ -340,6 +346,7 @@ exports[`smithyProjectGenerator > should generate smithy project with default op
|
|
|
340
346
|
},
|
|
341
347
|
"generate-ssdk": {
|
|
342
348
|
"cache": true,
|
|
349
|
+
"inputs": ["default"],
|
|
343
350
|
"outputs": ["{workspaceRoot}/dist/{projectRoot}/build/ssdk"],
|
|
344
351
|
"executor": "nx:run-commands",
|
|
345
352
|
"dependsOn": ["compile"],
|
|
@@ -464,6 +471,7 @@ exports[`smithyProjectGenerator > should generate smithy project with subdirecto
|
|
|
464
471
|
},
|
|
465
472
|
"compile": {
|
|
466
473
|
"cache": true,
|
|
474
|
+
"inputs": ["default"],
|
|
467
475
|
"outputs": [
|
|
468
476
|
"{workspaceRoot}/dist/{projectRoot}/smithy",
|
|
469
477
|
"{workspaceRoot}/dist/{projectRoot}/build/openapi"
|
|
@@ -484,6 +492,7 @@ exports[`smithyProjectGenerator > should generate smithy project with subdirecto
|
|
|
484
492
|
},
|
|
485
493
|
"generate-ssdk": {
|
|
486
494
|
"cache": true,
|
|
495
|
+
"inputs": ["default"],
|
|
487
496
|
"outputs": ["{workspaceRoot}/dist/{projectRoot}/build/ssdk"],
|
|
488
497
|
"executor": "nx:run-commands",
|
|
489
498
|
"dependsOn": ["compile"],
|
|
@@ -62,12 +62,21 @@ export declare const smithyGenerateSsdkCommands: () => string[];
|
|
|
62
62
|
* restoring `compile` from cache would otherwise discard the Server SDK beside it.
|
|
63
63
|
*/
|
|
64
64
|
export declare const smithyCompileOutputs: (type: SmithyProjectGeneratorSchema["type"]) => string[];
|
|
65
|
+
/**
|
|
66
|
+
* What a Smithy build target hashes: its own sources plus the outputs of the
|
|
67
|
+
* tasks it depends on. That covers a consumed shape library's output only
|
|
68
|
+
* when nx.json's `default` named input carries the `dependentTasksOutputFiles`
|
|
69
|
+
* entry, which only ts/lib/generator.ts writes - a Smithy-only workspace has
|
|
70
|
+
* no such entry, so `default` falls back to Nx's `{projectRoot}/**\/*`.
|
|
71
|
+
*/
|
|
72
|
+
export declare const SMITHY_TARGET_INPUTS: string[];
|
|
65
73
|
/**
|
|
66
74
|
* The target that builds a service's Server SDK, shared with the migration moving
|
|
67
75
|
* an existing project onto it.
|
|
68
76
|
*/
|
|
69
77
|
export declare const smithyGenerateSsdkTarget: () => {
|
|
70
78
|
cache: boolean;
|
|
79
|
+
inputs: string[];
|
|
71
80
|
outputs: string[];
|
|
72
81
|
executor: string;
|
|
73
82
|
dependsOn: string[];
|