@kensio/yulin 1.21.12 → 1.21.14

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 (89) hide show
  1. package/dist/service/dynamodb/command/authorize/sim-dynamodb-attributes.d.ts +27 -0
  2. package/dist/service/dynamodb/command/authorize/sim-dynamodb-attributes.js +35 -0
  3. package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.d.ts +2 -1
  4. package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.js +10 -4
  5. package/dist/service/dynamodb/command/authorize/sim-dynamodb-leading-keys.d.ts +33 -0
  6. package/dist/service/dynamodb/command/authorize/sim-dynamodb-leading-keys.js +45 -0
  7. package/dist/service/dynamodb/command/authorize/sim-dynamodb-reached.d.ts +17 -0
  8. package/dist/service/dynamodb/command/authorize/sim-dynamodb-reached.js +11 -0
  9. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-get-item.js +5 -0
  10. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-read-limit.d.ts +6 -0
  11. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-read-limit.js +16 -0
  12. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-reads.d.ts +2 -0
  13. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-reads.js +14 -22
  14. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-tables.d.ts +10 -2
  15. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-tables.js +1 -1
  16. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write-item.js +11 -1
  17. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write.d.ts +6 -0
  18. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write.js +8 -8
  19. package/dist/service/dynamodb/command/item/sim-dynamodb-condition-check.d.ts +5 -1
  20. package/dist/service/dynamodb/command/item/sim-dynamodb-condition-check.js +9 -2
  21. package/dist/service/dynamodb/command/item/sim-dynamodb-delete-item.js +8 -1
  22. package/dist/service/dynamodb/command/item/sim-dynamodb-get-item.js +10 -2
  23. package/dist/service/dynamodb/command/item/sim-dynamodb-put-item.js +6 -1
  24. package/dist/service/dynamodb/command/item/sim-dynamodb-update-expressions.d.ts +2 -0
  25. package/dist/service/dynamodb/command/item/sim-dynamodb-update-expressions.js +1 -1
  26. package/dist/service/dynamodb/command/item/sim-dynamodb-update-item.js +8 -1
  27. package/dist/service/dynamodb/command/item/sim-dynamodb-update-plan.d.ts +8 -6
  28. package/dist/service/dynamodb/command/item/sim-dynamodb-update-plan.js +14 -13
  29. package/dist/service/dynamodb/command/item/sim-dynamodb-update-touched.d.ts +10 -0
  30. package/dist/service/dynamodb/command/item/sim-dynamodb-update-touched.js +16 -0
  31. package/dist/service/dynamodb/command/query/sim-dynamodb-query-expressions.d.ts +2 -0
  32. package/dist/service/dynamodb/command/query/sim-dynamodb-query-expressions.js +6 -1
  33. package/dist/service/dynamodb/command/query/sim-dynamodb-query.js +9 -1
  34. package/dist/service/dynamodb/command/scan/sim-dynamodb-scan-expressions.d.ts +2 -0
  35. package/dist/service/dynamodb/command/scan/sim-dynamodb-scan-expressions.js +2 -2
  36. package/dist/service/dynamodb/command/scan/sim-dynamodb-scan.js +1 -1
  37. package/dist/service/dynamodb/command/table/sim-dynamodb-table-access.d.ts +16 -6
  38. package/dist/service/dynamodb/command/table/sim-dynamodb-table-access.js +18 -8
  39. package/dist/service/dynamodb/command/table/sim-dynamodb-table-reach.d.ts +25 -0
  40. package/dist/service/dynamodb/command/table/sim-dynamodb-table-reach.js +27 -0
  41. package/dist/service/dynamodb/command/transact/sim-dynamodb-transact-gets.js +1 -1
  42. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-expression.d.ts +9 -1
  43. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-expression.js +2 -2
  44. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-grammar.js +1 -1
  45. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-operand-parser.d.ts +3 -2
  46. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-operand-parser.js +3 -3
  47. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-parser.d.ts +2 -1
  48. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-parser.js +1 -1
  49. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-expression.js +1 -1
  50. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-operands.d.ts +3 -2
  51. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-operands.js +8 -6
  52. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-parser.d.ts +2 -1
  53. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-parser.js +1 -1
  54. package/dist/service/dynamodb/expression/projection/sim-dynamodb-projection-expression.d.ts +9 -1
  55. package/dist/service/dynamodb/expression/projection/sim-dynamodb-projection-expression.js +5 -5
  56. package/dist/service/dynamodb/expression/sim-dynamodb-document-path-parser.d.ts +3 -3
  57. package/dist/service/dynamodb/expression/sim-dynamodb-document-path-parser.js +8 -4
  58. package/dist/service/dynamodb/expression/sim-dynamodb-expression-attributes.d.ts +33 -0
  59. package/dist/service/dynamodb/expression/sim-dynamodb-expression-attributes.js +40 -0
  60. package/dist/service/dynamodb/expression/sim-dynamodb-expression-parameters.d.ts +6 -0
  61. package/dist/service/dynamodb/expression/sim-dynamodb-expression-parameters.js +7 -0
  62. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-clause.d.ts +3 -3
  63. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-clause.js +3 -3
  64. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-expression.js +1 -1
  65. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-operand-parser.d.ts +3 -2
  66. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-operand-parser.js +3 -3
  67. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-parser.d.ts +2 -1
  68. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-all-values-string-operator.d.ts +4 -1
  69. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-all-values-string-operator.js +6 -1
  70. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-any-value-string-operator.d.ts +4 -1
  71. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-any-value-string-operator.js +6 -1
  72. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-scalar-string-operator.d.ts +4 -1
  73. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-scalar-string-operator.js +6 -1
  74. package/dist/service/iam/authorize/match/condition/null/sim-iam-null.d.ts +23 -0
  75. package/dist/service/iam/authorize/match/condition/null/sim-iam-null.js +59 -0
  76. package/dist/service/iam/authorize/match/condition/numeric/less-than-equals/sim-iam-number-lte.d.ts +4 -1
  77. package/dist/service/iam/authorize/match/condition/numeric/less-than-equals/sim-iam-number-lte.js +6 -1
  78. package/dist/service/iam/authorize/match/condition/sim-iam-condition-operator-parser.js +2 -0
  79. package/dist/service/iam/authorize/match/condition/sim-iam-condition-operator.d.ts +9 -2
  80. package/dist/service/iam/authorize/match/condition/sim-iam-policy-condition-matcher.js +1 -1
  81. package/dist/service/iam/authorize/match/condition/string/all-values/sim-iam-for-all-values-string-condition-operator.d.ts +8 -1
  82. package/dist/service/iam/authorize/match/condition/string/all-values/sim-iam-for-all-values-string-condition-operator.js +10 -1
  83. package/dist/service/iam/authorize/match/condition/string/any-value/sim-iam-for-any-value-string-condition-operator.d.ts +4 -1
  84. package/dist/service/iam/authorize/match/condition/string/any-value/sim-iam-for-any-value-string-condition-operator.js +6 -1
  85. package/dist/service/iam/authorize/match/condition/string/sim-iam-scalar-string-condition-operator.d.ts +4 -1
  86. package/dist/service/iam/authorize/match/condition/string/sim-iam-scalar-string-condition-operator.js +6 -1
  87. package/docs/services/dynamodb/README.md +127 -0
  88. package/docs/services/iam/README.md +23 -3
  89. package/package.json +2 -2
@@ -0,0 +1,27 @@
1
+ import type { SimDynamoDbItem } from "../../item/sim-dynamodb-item.js";
2
+ /**
3
+ * The condition key DynamoDB names a request's attributes by.
4
+ */
5
+ export declare const simDynamoDbAttributesConditionKey = "dynamodb:Attributes";
6
+ /**
7
+ * The top-level attribute names a `Key` or an `Item` carries.
8
+ *
9
+ * Those parameters name their attributes outright, so there is no expression
10
+ * to read them out of. Every name is top-level, since an item's attributes are
11
+ * what nesting starts from.
12
+ *
13
+ * The items are read when authorization asks for them, and whatever reading
14
+ * them throws is dropped. Authorization runs ahead of the checks a command
15
+ * makes on what it was given, so a request too malformed to read is authorized
16
+ * naming no attributes and refused by the check that follows.
17
+ */
18
+ export declare function simDynamoDbItemAttributeNames(items: () => readonly SimDynamoDbItem[]): readonly string[];
19
+ /**
20
+ * Gather the attribute names a request reaches, keeping each one once.
21
+ *
22
+ * A request names the same attribute in more than one place often enough for
23
+ * this to matter. An UpdateItem names its key in `Key` and may name it again
24
+ * in a condition, and AWS supplies one list of attributes rather than one per
25
+ * parameter.
26
+ */
27
+ export declare function simDynamoDbAttributesOf(...sources: readonly (readonly string[])[]): readonly string[];
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The condition key DynamoDB names a request's attributes by.
3
+ */
4
+ export const simDynamoDbAttributesConditionKey = "dynamodb:Attributes";
5
+ /**
6
+ * The top-level attribute names a `Key` or an `Item` carries.
7
+ *
8
+ * Those parameters name their attributes outright, so there is no expression
9
+ * to read them out of. Every name is top-level, since an item's attributes are
10
+ * what nesting starts from.
11
+ *
12
+ * The items are read when authorization asks for them, and whatever reading
13
+ * them throws is dropped. Authorization runs ahead of the checks a command
14
+ * makes on what it was given, so a request too malformed to read is authorized
15
+ * naming no attributes and refused by the check that follows.
16
+ */
17
+ export function simDynamoDbItemAttributeNames(items) {
18
+ try {
19
+ return items().flatMap((item) => item.attributeNames());
20
+ }
21
+ catch {
22
+ return [];
23
+ }
24
+ }
25
+ /**
26
+ * Gather the attribute names a request reaches, keeping each one once.
27
+ *
28
+ * A request names the same attribute in more than one place often enough for
29
+ * this to matter. An UpdateItem names its key in `Key` and may name it again
30
+ * in a condition, and AWS supplies one list of attributes rather than one per
31
+ * parameter.
32
+ */
33
+ export function simDynamoDbAttributesOf(...sources) {
34
+ return [...new Set(sources.flat())];
35
+ }
@@ -2,6 +2,7 @@ import type { SimAwsCaller } from "../../../aws/caller/sim-aws-caller.js";
2
2
  import type { SimAwsResolvedCaller } from "../../../aws/caller/sim-aws-caller-resolver.js";
3
3
  import type { SimAwsAccountRegionScope } from "../../../aws/sim-aws-account-region-scope.js";
4
4
  import type { SimIamInterServiceAuthZ } from "../../../iam/authorize/sim-iam-inter-service-auth-z.js";
5
+ import type { SimDynamoDbReached } from "./sim-dynamodb-reached.js";
5
6
  interface SimDynamoDbAuthorizerProperties {
6
7
  readonly iam: SimIamInterServiceAuthZ;
7
8
  readonly accountRegionScope: SimAwsAccountRegionScope;
@@ -29,7 +30,7 @@ export declare class SimDynamoDbAuthorizer {
29
30
  * omitted caller, which defaults to Account root, from an explicit anonymous
30
31
  * caller.
31
32
  */
32
- authorizeTable(action: string, tableName: string, caller?: SimAwsCaller): SimAwsResolvedCaller;
33
+ authorizeTable(action: string, tableName: string, caller?: SimAwsCaller, reached?: SimDynamoDbReached): SimAwsResolvedCaller;
33
34
  /**
34
35
  * Ensure the caller may perform an action on a table's stream.
35
36
  *
@@ -1,5 +1,6 @@
1
1
  import { SimIamAccessDenied } from "../../../iam/error/sim-iam.error.js";
2
2
  import { simDynamoDbTableArn } from "../../table/sim-dynamodb-table-arn.js";
3
+ import { simDynamoDbConditionContextOf } from "./sim-dynamodb-reached.js";
3
4
  /**
4
5
  * Applies simulated IAM authorization to DynamoDB requests.
5
6
  *
@@ -26,8 +27,8 @@ export class SimDynamoDbAuthorizer {
26
27
  * omitted caller, which defaults to Account root, from an explicit anonymous
27
28
  * caller.
28
29
  */
29
- authorizeTable(action, tableName, caller) {
30
- return this.authorizeResource(action, simDynamoDbTableArn(this.accountRegionScope, tableName), caller);
30
+ authorizeTable(action, tableName, caller, reached = {}) {
31
+ return this.authorizeResource(action, simDynamoDbTableArn(this.accountRegionScope, tableName), caller, reached);
31
32
  }
32
33
  /**
33
34
  * Ensure the caller may perform an action on a table's stream.
@@ -51,8 +52,13 @@ export class SimDynamoDbAuthorizer {
51
52
  authorizeAnyTable(action, caller) {
52
53
  return this.authorizeResource(action, "*", caller);
53
54
  }
54
- authorizeResource(action, resource, caller) {
55
- const decision = this.iam.authorize({ action, resource, caller });
55
+ authorizeResource(action, resource, caller, reached = {}) {
56
+ const decision = this.iam.authorize({
57
+ action,
58
+ resource,
59
+ caller,
60
+ conditionContext: simDynamoDbConditionContextOf(reached),
61
+ });
56
62
  if (decision.isDenied) {
57
63
  throw new SimIamAccessDenied({
58
64
  principal: decision.caller.principal,
@@ -0,0 +1,33 @@
1
+ import type { SimDynamoDbItem } from "../../item/sim-dynamodb-item.js";
2
+ import type { SimDynamoDbValue } from "../../item/sim-dynamodb-value.js";
3
+ import type { SimDynamoDbTable } from "../../table/sim-dynamodb-table.js";
4
+ /**
5
+ * The condition key DynamoDB names a request's partition key values by.
6
+ */
7
+ export declare const simDynamoDbLeadingKeysConditionKey = "dynamodb:LeadingKeys";
8
+ /**
9
+ * The partition key values one request reaches in one table.
10
+ *
11
+ * Which attribute holds the partition key is the table's to say, so a command
12
+ * hands over how to read the values rather than the values themselves. The
13
+ * table is found before the caller is authorized against it, and the values
14
+ * are read from it in between.
15
+ */
16
+ export type SimDynamoDbLeadingKeys = (table: SimDynamoDbTable) => readonly string[];
17
+ /**
18
+ * Read the partition key values a run of items or keys carries.
19
+ *
20
+ * The items are read when authorization asks for them rather than at the call
21
+ * site, which is what keeps a request DynamoDB would refuse being refused
22
+ * after the caller has been authorized rather than before.
23
+ *
24
+ * An item with no readable partition key value is left out.
25
+ */
26
+ export declare function simDynamoDbLeadingKeysOf(items: () => readonly SimDynamoDbItem[]): SimDynamoDbLeadingKeys;
27
+ /**
28
+ * Read the one partition key value a key condition names.
29
+ *
30
+ * The value is already held against the key schema of what is being read, so
31
+ * the table says nothing more about it here.
32
+ */
33
+ export declare function simDynamoDbLeadingKeyOf(value: SimDynamoDbValue): readonly string[];
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The condition key DynamoDB names a request's partition key values by.
3
+ */
4
+ export const simDynamoDbLeadingKeysConditionKey = "dynamodb:LeadingKeys";
5
+ /**
6
+ * Write one partition key value the way an IAM policy condition holds it.
7
+ *
8
+ * A policy condition value is a string. A string key is its text and a number
9
+ * key is its digits. Binary has no form documented for the condition key, and
10
+ * a request carrying a binary partition key leaves the value out rather than
11
+ * inventing an encoding for it.
12
+ */
13
+ function leadingKeyText(value) {
14
+ if (value?.kind === "S") {
15
+ return value.text;
16
+ }
17
+ if (value?.kind === "N") {
18
+ return value.number.text;
19
+ }
20
+ return undefined;
21
+ }
22
+ /**
23
+ * Read the partition key values a run of items or keys carries.
24
+ *
25
+ * The items are read when authorization asks for them rather than at the call
26
+ * site, which is what keeps a request DynamoDB would refuse being refused
27
+ * after the caller has been authorized rather than before.
28
+ *
29
+ * An item with no readable partition key value is left out.
30
+ */
31
+ export function simDynamoDbLeadingKeysOf(items) {
32
+ return (table) => items()
33
+ .map((item) => leadingKeyText(item.attribute(table.keySchema.hashKeyAttributeName)))
34
+ .filter((text) => text !== undefined);
35
+ }
36
+ /**
37
+ * Read the one partition key value a key condition names.
38
+ *
39
+ * The value is already held against the key schema of what is being read, so
40
+ * the table says nothing more about it here.
41
+ */
42
+ export function simDynamoDbLeadingKeyOf(value) {
43
+ const text = leadingKeyText(value);
44
+ return text === undefined ? [] : [text];
45
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * What one request reaches in the table it names.
3
+ *
4
+ * Each entry becomes a DynamoDB condition key. A request reaching nothing
5
+ * under one of them leaves it out, as AWS leaves a key out for an operation
6
+ * that reaches nothing it names.
7
+ */
8
+ export interface SimDynamoDbReached {
9
+ /** The partition key values the request reaches. */
10
+ readonly leadingKeys?: readonly string[] | undefined;
11
+ /** The top-level attribute names the request names. */
12
+ readonly attributes?: readonly string[] | undefined;
13
+ }
14
+ /**
15
+ * The condition values a request carries for the table it names.
16
+ */
17
+ export declare function simDynamoDbConditionContextOf(reached: SimDynamoDbReached): Readonly<Record<string, readonly string[]>>;
@@ -0,0 +1,11 @@
1
+ import { simDynamoDbAttributesConditionKey } from "./sim-dynamodb-attributes.js";
2
+ import { simDynamoDbLeadingKeysConditionKey } from "./sim-dynamodb-leading-keys.js";
3
+ /**
4
+ * The condition values a request carries for the table it names.
5
+ */
6
+ export function simDynamoDbConditionContextOf(reached) {
7
+ return Object.fromEntries([
8
+ [simDynamoDbLeadingKeysConditionKey, reached.leadingKeys],
9
+ [simDynamoDbAttributesConditionKey, reached.attributes],
10
+ ].filter(([, values]) => (values ?? []).length > 0));
11
+ }
@@ -2,6 +2,7 @@ import { readSimDynamoDbBatchReads, } from "./sim-dynamodb-batch-reads.js";
2
2
  import { assertDistinctBatchItems } from "./sim-dynamodb-batch-request-items.js";
3
3
  import { reachSimDynamoDbBatchTables, } from "./sim-dynamodb-batch-tables.js";
4
4
  import { refuseUnsimulatedBatchReadInput } from "./sim-dynamodb-unsimulated-batch-input.js";
5
+ import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
5
6
  /**
6
7
  * The BatchGetItem command.
7
8
  *
@@ -27,6 +28,10 @@ export class SimDynamoDbBatchGetItem {
27
28
  access: this.access,
28
29
  operation: "BatchGetItem",
29
30
  caller: options?.caller,
31
+ reached: (entry) => ({
32
+ leadingKeys: simDynamoDbLeadingKeysOf(() => entry.keys),
33
+ attributes: entry.attributes,
34
+ }),
30
35
  });
31
36
  const responses = Object.fromEntries(reached.map((entry) => [entry.requested.reference, readItems(entry)]));
32
37
  // Nothing here throttles or stops at a response size, so no key is ever
@@ -0,0 +1,6 @@
1
+ import type { SimDynamoDbKeysAndAttributes } from "./batch.command.js";
2
+ import type { SimDynamoDbBatchTableRequest } from "./sim-dynamodb-batch-request-items.js";
3
+ /**
4
+ * Refuse a batch asking for more items than DynamoDB reads at once.
5
+ */
6
+ export declare function assertSimDynamoDbBatchReadLimit(tables: readonly SimDynamoDbBatchTableRequest<SimDynamoDbKeysAndAttributes>[], operation: string): void;
@@ -0,0 +1,16 @@
1
+ import { SimDynamoDbValidationException } from "../../error/dynamodb.error.js";
2
+ /**
3
+ * Real DynamoDB reads 100 items in one batch, counted across every table the
4
+ * request names rather than per table.
5
+ */
6
+ const greatestKeys = 100;
7
+ /**
8
+ * Refuse a batch asking for more items than DynamoDB reads at once.
9
+ */
10
+ export function assertSimDynamoDbBatchReadLimit(tables, operation) {
11
+ const total = tables.reduce((count, { requested }) => count + (requested.Keys ?? []).length, 0);
12
+ if (total > greatestKeys) {
13
+ throw new SimDynamoDbValidationException(`Too many items requested for the ${operation} call: ${total.toString()} ` +
14
+ `keys, where ${greatestKeys.toString()} is the most a batch reads`);
15
+ }
16
+ }
@@ -11,6 +11,8 @@ export interface SimDynamoDbBatchTableReads {
11
11
  readonly reference: string;
12
12
  readonly keys: readonly SimDynamoDbItem[];
13
13
  readonly projection: SimDynamoDbProjection | undefined;
14
+ /** The top-level attributes this table is asked for, per table as above. */
15
+ readonly attributes: readonly string[];
14
16
  }
15
17
  /**
16
18
  * Read every key a batch asks for, before any table is reached.
@@ -1,14 +1,11 @@
1
1
  import { SimDynamoDbValidationException } from "../../error/dynamodb.error.js";
2
2
  import { readSimDynamoDbProjection } from "../../expression/projection/sim-dynamodb-projection-expression.js";
3
3
  import { readSimDynamoDbKey } from "../item/sim-dynamodb-key-input.js";
4
- import { readSimDynamoDbBatchRequestItems, } from "./sim-dynamodb-batch-request-items.js";
4
+ import { simDynamoDbAttributesOf, simDynamoDbItemAttributeNames, } from "../authorize/sim-dynamodb-attributes.js";
5
+ import { readSimDynamoDbBatchRequestItems } from "./sim-dynamodb-batch-request-items.js";
6
+ import { assertSimDynamoDbBatchReadLimit } from "./sim-dynamodb-batch-read-limit.js";
5
7
  import { refuseUnsimulatedBatchKeysAndAttributes } from "./sim-dynamodb-unsimulated-batch-input.js";
6
8
  const operation = "BatchGetItem";
7
- /**
8
- * Real DynamoDB reads 100 items in one batch, counted across every table the
9
- * request names rather than per table.
10
- */
11
- const greatestKeys = 100;
12
9
  /**
13
10
  * Read every key a batch asks for, before any table is reached.
14
11
  *
@@ -17,22 +14,17 @@ const greatestKeys = 100;
17
14
  */
18
15
  export function readSimDynamoDbBatchReads(requestItems) {
19
16
  const tables = readSimDynamoDbBatchRequestItems(requestItems, operation);
20
- assertWithinKeyLimit(tables);
21
- return tables.map(({ reference, requested }) => ({
22
- reference,
23
- keys: readTableKeys(reference, requested),
24
- projection: readSimDynamoDbProjection(requested),
25
- }));
26
- }
27
- /**
28
- * Refuse a batch asking for more items than DynamoDB reads at once.
29
- */
30
- function assertWithinKeyLimit(tables) {
31
- const total = tables.reduce((count, { requested }) => count + (requested.Keys ?? []).length, 0);
32
- if (total > greatestKeys) {
33
- throw new SimDynamoDbValidationException(`Too many items requested for the ${operation} call: ${total.toString()} ` +
34
- `keys, where ${greatestKeys.toString()} is the most a batch reads`);
35
- }
17
+ assertSimDynamoDbBatchReadLimit(tables, operation);
18
+ return tables.map(({ reference, requested }) => {
19
+ const keys = readTableKeys(reference, requested);
20
+ const read = readSimDynamoDbProjection(requested);
21
+ return {
22
+ reference,
23
+ keys,
24
+ projection: read.projection,
25
+ attributes: simDynamoDbAttributesOf(read.attributes, simDynamoDbItemAttributeNames(() => keys)),
26
+ };
27
+ });
36
28
  }
37
29
  /**
38
30
  * Read the keys one table of the batch is asked for.
@@ -1,6 +1,7 @@
1
1
  import type { SimAwsCaller } from "../../../aws/caller/sim-aws-caller.js";
2
2
  import type { SimDynamoDbTable } from "../../table/sim-dynamodb-table.js";
3
3
  import type { SimDynamoDbTableAccess } from "../table/sim-dynamodb-table-access.js";
4
+ import type { SimDynamoDbTableReach } from "../table/sim-dynamodb-table-reach.js";
4
5
  /**
5
6
  * What one table of a batch asks for, against the table it names.
6
7
  */
@@ -8,10 +9,17 @@ export interface SimDynamoDbBatchTable<Requested> {
8
9
  readonly table: SimDynamoDbTable;
9
10
  readonly requested: Requested;
10
11
  }
11
- interface SimDynamoDbBatchReach {
12
+ interface SimDynamoDbBatchReach<Requested> {
12
13
  readonly access: SimDynamoDbTableAccess;
13
14
  readonly operation: string;
14
15
  readonly caller: SimAwsCaller | undefined;
16
+ /**
17
+ * What one table of the batch is asked for.
18
+ *
19
+ * Each table is authorized on its own, so each carries the keys and the
20
+ * attributes the batch names in that table and no others.
21
+ */
22
+ readonly reached: (requested: Requested) => SimDynamoDbTableReach;
15
23
  }
16
24
  /**
17
25
  * Reach every table a batch names, authorizing the caller against each.
@@ -24,5 +32,5 @@ interface SimDynamoDbBatchReach {
24
32
  */
25
33
  export declare function reachSimDynamoDbBatchTables<Requested extends {
26
34
  readonly reference: string;
27
- }>(requested: readonly Requested[], reach: SimDynamoDbBatchReach): readonly SimDynamoDbBatchTable<Requested>[];
35
+ }>(requested: readonly Requested[], reach: SimDynamoDbBatchReach<Requested>): readonly SimDynamoDbBatchTable<Requested>[];
28
36
  export {};
@@ -11,7 +11,7 @@ import { SimDynamoDbValidationException } from "../../error/dynamodb.error.js";
11
11
  export function reachSimDynamoDbBatchTables(requested, reach) {
12
12
  const action = `dynamodb:${reach.operation}`;
13
13
  const reached = requested.map((entry) => ({
14
- table: reach.access.required(action, entry.reference, reach.caller),
14
+ table: reach.access.required(action, entry.reference, reach.caller, reach.reached(entry)),
15
15
  requested: entry,
16
16
  }));
17
17
  assertDistinctTables(reached, reach.operation);
@@ -2,6 +2,8 @@ import { assertDistinctBatchItems } from "./sim-dynamodb-batch-request-items.js"
2
2
  import { reachSimDynamoDbBatchTables } from "./sim-dynamodb-batch-tables.js";
3
3
  import { readSimDynamoDbBatchWrites } from "./sim-dynamodb-batch-writes.js";
4
4
  import { refuseUnsimulatedBatchWriteInput } from "./sim-dynamodb-unsimulated-batch-input.js";
5
+ import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
6
+ import { simDynamoDbItemAttributeNames } from "../authorize/sim-dynamodb-attributes.js";
5
7
  const operation = "BatchWriteItem";
6
8
  /**
7
9
  * The BatchWriteItem command.
@@ -25,7 +27,15 @@ export class SimDynamoDbBatchWriteItem {
25
27
  handle(command, options) {
26
28
  const input = command.input;
27
29
  refuseUnsimulatedBatchWriteInput(input);
28
- const reached = reachSimDynamoDbBatchTables(readSimDynamoDbBatchWrites(input.RequestItems), { access: this.access, operation, caller: options?.caller });
30
+ const reached = reachSimDynamoDbBatchTables(readSimDynamoDbBatchWrites(input.RequestItems), {
31
+ access: this.access,
32
+ operation,
33
+ caller: options?.caller,
34
+ reached: (entry) => ({
35
+ leadingKeys: simDynamoDbLeadingKeysOf(() => entry.writes.map((write) => write.names)),
36
+ attributes: simDynamoDbItemAttributeNames(() => entry.writes.map((write) => write.names)),
37
+ }),
38
+ });
29
39
  // Marshalling every key checks it against the table's key schema, and is
30
40
  // what tells two operations on one item apart. Every table is checked
31
41
  // before any of them is written to.
@@ -1,3 +1,4 @@
1
+ import { SimDynamoDbItem } from "../../item/sim-dynamodb-item.js";
1
2
  import type { SimDynamoDbTable } from "../../table/sim-dynamodb-table.js";
2
3
  import type { SimDynamoDbWriteRequest } from "./batch.command.js";
3
4
  /**
@@ -8,6 +9,11 @@ import type { SimDynamoDbWriteRequest } from "./batch.command.js";
8
9
  * before it applies anything, since the check and the write read the same item.
9
10
  */
10
11
  export interface SimDynamoDbBatchWrite {
12
+ /**
13
+ * The item or key this write names, which is where a policy condition reads
14
+ * its partition key value from.
15
+ */
16
+ readonly names: SimDynamoDbItem;
11
17
  /**
12
18
  * The primary key this write works on, as the table marshals it.
13
19
  */
@@ -5,15 +5,15 @@ import { readSimDynamoDbKey } from "../item/sim-dynamodb-key-input.js";
5
5
  * A put in a batch write, which replaces the whole item as PutItem does.
6
6
  */
7
7
  class SimDynamoDbBatchPut {
8
- item;
8
+ names;
9
9
  constructor(item) {
10
- this.item = item;
10
+ this.names = item;
11
11
  }
12
12
  keyIn(table) {
13
- return table.keyOfItem(this.item);
13
+ return table.keyOfItem(this.names);
14
14
  }
15
15
  applyTo(table) {
16
- table.putItem(this.item);
16
+ table.putItem(this.names);
17
17
  }
18
18
  }
19
19
  /**
@@ -21,15 +21,15 @@ class SimDynamoDbBatchPut {
21
21
  * that is already free is deleted successfully.
22
22
  */
23
23
  class SimDynamoDbBatchDelete {
24
- key;
24
+ names;
25
25
  constructor(key) {
26
- this.key = key;
26
+ this.names = key;
27
27
  }
28
28
  keyIn(table) {
29
- return table.keyOfKey(this.key);
29
+ return table.keyOfKey(this.names);
30
30
  }
31
31
  applyTo(table) {
32
- table.deleteItem(this.key);
32
+ table.deleteItem(this.names);
33
33
  }
34
34
  }
35
35
  /**
@@ -15,6 +15,10 @@ interface SimDynamoDbConditionCheckInput {
15
15
  * be nothing: that is what makes `attribute_not_exists` an insert if absent.
16
16
  */
17
17
  export declare class SimDynamoDbConditionCheck {
18
+ /**
19
+ * The top-level attributes the condition named, for `dynamodb:Attributes`.
20
+ */
21
+ readonly attributeNames: readonly string[];
18
22
  private readonly condition;
19
23
  private readonly reportsItem;
20
24
  private constructor();
@@ -31,7 +35,7 @@ export declare class SimDynamoDbConditionCheck {
31
35
  * UpdateItem reads its condition alongside its update expression, since the
32
36
  * two share the request's placeholders, and then hands the condition here.
33
37
  */
34
- static of(condition: SimDynamoDbCondition | undefined, input: SimDynamoDbConditionCheckInput, operation: string): SimDynamoDbConditionCheck;
38
+ static of(condition: SimDynamoDbCondition | undefined, input: SimDynamoDbConditionCheckInput, operation: string, attributeNames?: readonly string[]): SimDynamoDbConditionCheck;
35
39
  /**
36
40
  * Whether the request asked for the item that turned it away.
37
41
  */
@@ -13,11 +13,16 @@ const failureModes = new Set(["NONE", "ALL_OLD"]);
13
13
  * be nothing: that is what makes `attribute_not_exists` an insert if absent.
14
14
  */
15
15
  export class SimDynamoDbConditionCheck {
16
+ /**
17
+ * The top-level attributes the condition named, for `dynamodb:Attributes`.
18
+ */
19
+ attributeNames;
16
20
  condition;
17
21
  reportsItem;
18
22
  constructor(properties) {
19
23
  this.condition = properties.condition;
20
24
  this.reportsItem = properties.reportsItem;
25
+ this.attributeNames = properties.attributeNames;
21
26
  }
22
27
  /**
23
28
  * Read the condition a request carries, before anything is looked up.
@@ -26,7 +31,8 @@ export class SimDynamoDbConditionCheck {
26
31
  * anything, so a bad expression fails the same way every time.
27
32
  */
28
33
  static read(input, operation) {
29
- return this.of(readSimDynamoDbCondition(input), input, operation);
34
+ const read = readSimDynamoDbCondition(input);
35
+ return this.of(read.condition, input, operation, read.attributes);
30
36
  }
31
37
  /**
32
38
  * Guard a write with a condition that has already been read.
@@ -34,10 +40,11 @@ export class SimDynamoDbConditionCheck {
34
40
  * UpdateItem reads its condition alongside its update expression, since the
35
41
  * two share the request's placeholders, and then hands the condition here.
36
42
  */
37
- static of(condition, input, operation) {
43
+ static of(condition, input, operation, attributeNames = []) {
38
44
  return new this({
39
45
  condition,
40
46
  reportsItem: this.reportsItemFor(input, operation),
47
+ attributeNames,
41
48
  });
42
49
  }
43
50
  /**
@@ -1,3 +1,5 @@
1
+ import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
2
+ import { simDynamoDbAttributesOf, simDynamoDbItemAttributeNames, } from "../authorize/sim-dynamodb-attributes.js";
1
3
  import { readSimDynamoDbKey } from "./sim-dynamodb-key-input.js";
2
4
  import { SimDynamoDbConditionCheck } from "./sim-dynamodb-condition-check.js";
3
5
  import { SimDynamoDbReturnValues } from "./sim-dynamodb-return-values.js";
@@ -26,7 +28,12 @@ export class SimDynamoDbDeleteItem {
26
28
  // The condition is read before the table is reached, so an expression
27
29
  // DynamoDB would refuse is refused whether or not the key holds anything.
28
30
  const check = SimDynamoDbConditionCheck.read(input, "DeleteItem");
29
- const table = this.access.required("dynamodb:DeleteItem", input.TableName, options?.caller);
31
+ const table = this.access.required("dynamodb:DeleteItem", input.TableName, options?.caller, {
32
+ leadingKeys: simDynamoDbLeadingKeysOf(() => [
33
+ readSimDynamoDbKey(input.Key),
34
+ ]),
35
+ attributes: simDynamoDbAttributesOf(check.attributeNames, simDynamoDbItemAttributeNames(() => [readSimDynamoDbKey(input.Key)])),
36
+ });
30
37
  const asked = SimDynamoDbReturnValues.read(input.ReturnValues, "DeleteItem");
31
38
  const key = readSimDynamoDbKey(input.Key);
32
39
  check.assertHoldsFor(table.getItem(key));
@@ -1,6 +1,8 @@
1
+ import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
1
2
  import { readSimDynamoDbKey } from "./sim-dynamodb-key-input.js";
2
3
  import { refuseUnsimulatedItemReadInput } from "./sim-dynamodb-unsimulated-item-read-input.js";
3
4
  import { readSimDynamoDbProjection } from "../../expression/projection/sim-dynamodb-projection-expression.js";
5
+ import { simDynamoDbAttributesOf, simDynamoDbItemAttributeNames, } from "../authorize/sim-dynamodb-attributes.js";
4
6
  /**
5
7
  * The GetItem command.
6
8
  *
@@ -25,8 +27,14 @@ export class SimDynamoDbGetItem {
25
27
  refuseUnsimulatedItemReadInput(input);
26
28
  // The projection is read before the table is reached, so an expression
27
29
  // DynamoDB would refuse is refused whether or not the key holds anything.
28
- const projection = readSimDynamoDbProjection(input);
29
- const table = this.access.required("dynamodb:GetItem", input.TableName, options?.caller);
30
+ const read = readSimDynamoDbProjection(input);
31
+ const projection = read.projection;
32
+ const table = this.access.required("dynamodb:GetItem", input.TableName, options?.caller, {
33
+ leadingKeys: simDynamoDbLeadingKeysOf(() => [
34
+ readSimDynamoDbKey(input.Key),
35
+ ]),
36
+ attributes: simDynamoDbAttributesOf(read.attributes, simDynamoDbItemAttributeNames(() => [readSimDynamoDbKey(input.Key)])),
37
+ });
30
38
  const found = table.getItem(readSimDynamoDbKey(input.Key));
31
39
  if (found === undefined) {
32
40
  return { $metadata: {} };
@@ -1,5 +1,7 @@
1
1
  import { SimDynamoDbValidationException } from "../../error/dynamodb.error.js";
2
2
  import { SimDynamoDbItem } from "../../item/sim-dynamodb-item.js";
3
+ import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
4
+ import { simDynamoDbAttributesOf, simDynamoDbItemAttributeNames, } from "../authorize/sim-dynamodb-attributes.js";
3
5
  import { SimDynamoDbConditionCheck } from "./sim-dynamodb-condition-check.js";
4
6
  import { SimDynamoDbReturnValues } from "./sim-dynamodb-return-values.js";
5
7
  import { refuseUnsimulatedItemWriteInput } from "./sim-dynamodb-unsimulated-item-write-input.js";
@@ -36,7 +38,10 @@ export class SimDynamoDbPutItem {
36
38
  // The condition is read before the table is reached, so an expression
37
39
  // DynamoDB would refuse is refused whether or not the key holds anything.
38
40
  const check = SimDynamoDbConditionCheck.read(input, "PutItem");
39
- const table = this.access.required("dynamodb:PutItem", input.TableName, options?.caller);
41
+ const table = this.access.required("dynamodb:PutItem", input.TableName, options?.caller, {
42
+ leadingKeys: simDynamoDbLeadingKeysOf(() => [readItem(input)]),
43
+ attributes: simDynamoDbAttributesOf(check.attributeNames, simDynamoDbItemAttributeNames(() => [readItem(input)])),
44
+ });
40
45
  const asked = SimDynamoDbReturnValues.read(input.ReturnValues, "PutItem");
41
46
  const item = readItem(input);
42
47
  check.assertHoldsFor(table.itemUnder(item));
@@ -14,6 +14,8 @@ export interface SimDynamoDbUpdateExpressionInput extends SimDynamoDbExpressionP
14
14
  export interface SimDynamoDbUpdateExpressions {
15
15
  readonly update: SimDynamoDbUpdate;
16
16
  readonly condition: SimDynamoDbCondition | undefined;
17
+ /** The top-level attributes the expressions named, for `dynamodb:Attributes`. */
18
+ readonly attributes: readonly string[];
17
19
  }
18
20
  /**
19
21
  * Read both expressions of an update against one set of placeholders.
@@ -13,7 +13,7 @@ export function readSimDynamoDbUpdateExpressions(input, expression) {
13
13
  const update = parseSimDynamoDbUpdate(expression, parameters);
14
14
  const condition = conditionIn(input.ConditionExpression, parameters);
15
15
  parameters.assertAllUsed();
16
- return { update, condition };
16
+ return { update, condition, attributes: parameters.attributes.topLevel };
17
17
  }
18
18
  /**
19
19
  * Read the condition guarding an update, if it names one.
@@ -1,3 +1,5 @@
1
+ import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
2
+ import { simDynamoDbAttributesOf, simDynamoDbItemAttributeNames, } from "../authorize/sim-dynamodb-attributes.js";
1
3
  import { readSimDynamoDbKey } from "./sim-dynamodb-key-input.js";
2
4
  import { SimDynamoDbReturnValues } from "./sim-dynamodb-return-values.js";
3
5
  import { refuseUnsimulatedItemUpdateInput } from "./sim-dynamodb-unsimulated-item-update-input.js";
@@ -28,7 +30,12 @@ export class SimDynamoDbUpdateItem {
28
30
  // The expressions are read before the table is reached, so one DynamoDB
29
31
  // would refuse is refused whether or not the key holds anything.
30
32
  const plan = SimDynamoDbUpdatePlan.read(input, "UpdateItem");
31
- const table = this.access.required("dynamodb:UpdateItem", input.TableName, options?.caller);
33
+ const table = this.access.required("dynamodb:UpdateItem", input.TableName, options?.caller, {
34
+ leadingKeys: simDynamoDbLeadingKeysOf(() => [
35
+ readSimDynamoDbKey(input.Key),
36
+ ]),
37
+ attributes: simDynamoDbAttributesOf(plan.attributeNames, simDynamoDbItemAttributeNames(() => [readSimDynamoDbKey(input.Key)])),
38
+ });
32
39
  const asked = SimDynamoDbReturnValues.readForUpdate(input.ReturnValues, "UpdateItem");
33
40
  const key = readSimDynamoDbKey(input.Key);
34
41
  const existing = table.getItem(key);