@kensio/yulin 1.21.31 → 1.21.32

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 (63) hide show
  1. package/dist/serve/message/sim-message-log-console.js +3 -3
  2. package/dist/service/apigateway/api/deployment/sim-rest-api-deployment.js +3 -3
  3. package/dist/service/apigateway/api/method/sim-rest-api-method.js +12 -12
  4. package/dist/service/apigateway/api/resource/sim-rest-api-resource.js +11 -9
  5. package/dist/service/apigateway/api/sim-rest-api-view.js +3 -3
  6. package/dist/service/apigateway/api/stage/sim-rest-api-stage.js +8 -10
  7. package/dist/service/apigatewayv2/api/integration/sim-http-api-integration.js +3 -3
  8. package/dist/service/apigatewayv2/api/route/sim-http-api-route-view.js +6 -6
  9. package/dist/service/apigatewayv2/api/sim-http-api-view.js +3 -3
  10. package/dist/service/apigatewayv2/api/stage/settings/sim-http-api-stage-route-settings.js +8 -7
  11. package/dist/service/apigatewayv2/api/stage/sim-http-api-stage.js +9 -9
  12. package/dist/service/athena/engine/sim-athena-partitioned-objects.js +7 -7
  13. package/dist/service/athena/sim-athena-engine.fixture.js +3 -0
  14. package/dist/service/athena/table/sim-athena-partition-filters.js +6 -5
  15. package/dist/service/backup/command/sim-backup-authorizer.js +5 -4
  16. package/dist/service/bedrock/response/sim-bedrock-declared-content.js +5 -10
  17. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-changes.js +19 -10
  18. package/dist/service/cloudfront/cfn/response-headers-policy/sim-cfn-cf-rh-policy-security-values.js +2 -6
  19. package/dist/service/cognito/user-pool/trigger/sim-cognito-pre-sign-up-response.js +4 -7
  20. package/dist/service/dynamodb/command/transact/sim-dynamodb-transact-write.js +34 -25
  21. package/dist/service/dynamodb/secondary-index/sim-dynamodb-index-limits.js +5 -4
  22. package/dist/service/dynamodb/stream/sim-dynamodb-stream-shard-iterator.js +1 -3
  23. package/dist/service/dynamodb/time-to-live/sim-dynamodb-time-to-live.js +4 -3
  24. package/dist/service/iam/authorize/context/sim-iam-auth-z-allow-requirement.js +6 -6
  25. package/dist/service/kinesis/stream/sim-kinesis-shard-iterator.js +1 -3
  26. package/dist/service/lambda/destination/sim-lambda-destination-record.js +7 -7
  27. package/dist/service/lambda/function/policy/sim-lambda-permission.js +15 -14
  28. package/dist/service/lambda/metric/sim-lambda-function-metrics.js +11 -9
  29. package/dist/service/logs/metric/sim-logs-metric-filter.js +1 -8
  30. package/dist/service/route53/dns/answer/sim-route53-dns-zone-finder.js +5 -4
  31. package/dist/service/route53/resolve/sim-r53-zone-record-finder.js +8 -6
  32. package/dist/service/s3/bucket/notification/sim-s3-notification-filter.js +4 -7
  33. package/dist/service/s3/bucket/sim-s3-bucket.d.ts +8 -0
  34. package/dist/service/s3/bucket/sim-s3-bucket.js +11 -0
  35. package/dist/service/s3/bucket/sim-s3-key-writes.d.ts +16 -0
  36. package/dist/service/s3/bucket/sim-s3-key-writes.js +43 -0
  37. package/dist/service/s3/bucket/versioning/sim-s3-bucket-versioning.js +1 -3
  38. package/dist/service/s3/command/complete-multipart-upload/complete-multipart-upload.command.d.ts +2 -1
  39. package/dist/service/s3/command/complete-multipart-upload/complete-multipart-upload.handler.js +5 -1
  40. package/dist/service/s3/command/copy-object/copy-object-precondition.d.ts +23 -0
  41. package/dist/service/s3/command/copy-object/copy-object-precondition.js +34 -0
  42. package/dist/service/s3/command/copy-object/copy-object.command.d.ts +3 -1
  43. package/dist/service/s3/command/copy-object/copy-object.handler.d.ts +4 -0
  44. package/dist/service/s3/command/copy-object/copy-object.handler.js +11 -1
  45. package/dist/service/s3/command/object/sim-s3-write-precondition.d.ts +34 -0
  46. package/dist/service/s3/command/object/sim-s3-write-precondition.js +47 -0
  47. package/dist/service/s3/command/put-object/put-object.command.d.ts +2 -1
  48. package/dist/service/s3/command/put-object/put-object.handler.js +5 -1
  49. package/dist/service/s3/error/sim-s3.error.d.ts +10 -0
  50. package/dist/service/s3/error/sim-s3.error.js +12 -0
  51. package/dist/service/s3/object/s3-object-listing.js +4 -3
  52. package/dist/service/s3/serve/api/sim-s3-api-input.d.ts +4 -0
  53. package/dist/service/s3/serve/api/sim-s3-api-input.js +10 -0
  54. package/dist/service/s3/serve/api/sim-s3-api-multipart-input.js +2 -0
  55. package/dist/service/s3/serve/api/sim-s3-api-object-input.d.ts +6 -0
  56. package/dist/service/s3/serve/api/sim-s3-api-object-input.js +34 -2
  57. package/dist/service/s3/storage/filesystem/s3-filesystem-object-keys.js +9 -8
  58. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-template.test-support.js +15 -23
  59. package/dist/service/sqs/message/sim-sqs-message.js +3 -3
  60. package/dist/service/stepfunctions/machine/sim-state-machine.js +4 -3
  61. package/dist/util/schedule/cron/sim-cron-field-bounds.js +1 -1
  62. package/docs/services/s3/README.md +68 -3
  63. package/package.json +2 -2
@@ -60,14 +60,15 @@ export class FilesystemS3ObjectKeys {
60
60
  nestedKeyPromises.push(this.listInDirectory(entryPath));
61
61
  continue;
62
62
  }
63
- if (entry.isFile()) {
64
- const key = path
65
- .relative(this.directoryPath, entryPath)
66
- .split(path.sep)
67
- .join("/");
68
- if (this.safety.isAllowedObjectKeyExtension(key)) {
69
- keys.push(key);
70
- }
63
+ if (!entry.isFile()) {
64
+ continue;
65
+ }
66
+ const key = path
67
+ .relative(this.directoryPath, entryPath)
68
+ .split(path.sep)
69
+ .join("/");
70
+ if (this.safety.isAllowedObjectKeyExtension(key)) {
71
+ keys.push(key);
71
72
  }
72
73
  }
73
74
  const nestedKeys = await Promise.all(nestedKeyPromises);
@@ -18,29 +18,21 @@ export function secretTemplate(properties = {}) {
18
18
  }
19
19
  function secretProperties(properties) {
20
20
  const { name, description, kmsKeyId, tags, secretString } = properties;
21
- const declared = {};
22
- if (name !== undefined) {
23
- declared["Name"] = name;
24
- }
25
- if (description !== undefined) {
26
- declared["Description"] = description;
27
- }
28
- if (kmsKeyId !== undefined) {
29
- declared["KmsKeyId"] = kmsKeyId;
30
- }
31
- if (tags !== undefined) {
32
- declared["Tags"] = [...tags];
33
- }
34
- if (secretString === undefined) {
35
- declared["GenerateSecretString"] = {
36
- SecretStringTemplate: "{}",
37
- GenerateStringKey: "current",
38
- PasswordLength: properties.passwordLength ?? 64,
39
- };
40
- }
41
- else {
42
- declared["SecretString"] = secretString;
43
- }
21
+ const declared = {
22
+ ...(name !== undefined && { Name: name }),
23
+ ...(description !== undefined && { Description: description }),
24
+ ...(kmsKeyId !== undefined && { KmsKeyId: kmsKeyId }),
25
+ ...(tags !== undefined && { Tags: [...tags] }),
26
+ ...(secretString === undefined
27
+ ? {
28
+ GenerateSecretString: {
29
+ SecretStringTemplate: "{}",
30
+ GenerateStringKey: "current",
31
+ PasswordLength: properties.passwordLength ?? 64,
32
+ },
33
+ }
34
+ : { SecretString: secretString }),
35
+ };
44
36
  return declared;
45
37
  }
46
38
  function readerResource() {
@@ -126,10 +126,10 @@ export class SimSqsMessage {
126
126
  "ApproximateFirstReceiveTimestamp",
127
127
  String(this.visibility.firstReceived.getTime()),
128
128
  ],
129
+ ...(this.deadLetterSourceArn === undefined
130
+ ? []
131
+ : [["DeadLetterQueueSourceArn", this.deadLetterSourceArn]]),
129
132
  ]);
130
- if (this.deadLetterSourceArn !== undefined) {
131
- values.set("DeadLetterQueueSourceArn", this.deadLetterSourceArn);
132
- }
133
133
  return values;
134
134
  }
135
135
  }
@@ -56,9 +56,10 @@ export class SimStateMachine {
56
56
  */
57
57
  update(changes) {
58
58
  this.#roleArn = changes.roleArn ?? this.#roleArn;
59
- if (changes.definition !== undefined && changes.parsed !== undefined) {
60
- this.#definition = changes.definition;
61
- this.#parsed = changes.parsed;
59
+ if (changes.definition === undefined || changes.parsed === undefined) {
60
+ return;
62
61
  }
62
+ this.#definition = changes.definition;
63
+ this.#parsed = changes.parsed;
63
64
  }
64
65
  }
@@ -58,5 +58,5 @@ export function boundsOf(spec, written, stepped) {
58
58
  ];
59
59
  }
60
60
  const only = valueOf(spec, written);
61
- return stepped ? [only, spec.maximum] : [only, only];
61
+ return [only, stepped ? spec.maximum : only];
62
62
  }
@@ -531,6 +531,74 @@ reports it there.
531
531
  An Object uploaded in parts gets a different form. See
532
532
  [Uploading an Object in parts](#uploading-an-object-in-parts).
533
533
 
534
+ ## Conditional writes
535
+
536
+ `PutObject`, `CopyObject` and `CompleteMultipartUpload` take `IfNoneMatch` and `IfMatch`, which
537
+ state what the key has to hold before the write goes ahead. `IfNoneMatch: "*"` writes only to a key
538
+ that holds nothing. `IfMatch` writes only over an Object with that ETag. A write whose condition
539
+ fails is refused with `PreconditionFailed` (status `412`), and the Object already under the key is
540
+ left as it was.
541
+
542
+ ```typescript sim-s3-conditional-write
543
+ /**
544
+ * Writing a simulated S3 Object only where the key is free.
545
+ */
546
+
547
+ import {
548
+ CopyObjectCommand,
549
+ CreateBucketCommand,
550
+ PutObjectCommand,
551
+ } from "@aws-sdk/client-s3";
552
+ import { SimAws } from "@kensio/yulin";
553
+
554
+ const simAws = new SimAws();
555
+ const simS3 = simAws.s3();
556
+
557
+ await simS3.createBucket(new CreateBucketCommand({ Bucket: "audio-bucket" }));
558
+
559
+ await simS3.putObject(
560
+ new PutObjectCommand({
561
+ Bucket: "audio-bucket",
562
+ Key: "uploads/take-1.mp3",
563
+ Body: "first take",
564
+ }),
565
+ );
566
+
567
+ for (let attempt = 0; attempt < 2; attempt++) {
568
+ try {
569
+ await simS3.copyObject(
570
+ new CopyObjectCommand({
571
+ Bucket: "audio-bucket",
572
+ Key: "shared/take.mp3",
573
+ CopySource: "audio-bucket/uploads/take-1.mp3",
574
+ IfNoneMatch: "*",
575
+ }),
576
+ );
577
+ console.log("copied");
578
+ } catch (error) {
579
+ // The second attempt finds the shared key taken.
580
+ if (error instanceof Error && error.name === "PreconditionFailed") {
581
+ console.log("already there");
582
+ } else {
583
+ throw error;
584
+ }
585
+ }
586
+ }
587
+ ```
588
+
589
+ Writes to one key run one at a time. Of several conditional writes racing for an empty key, the first
590
+ to finish succeeds and the rest are refused. `IfMatch` on a key holding nothing answers `NoSuchKey`. On a versioned Bucket both conditions look at
591
+ the current version, and a delete marker counts as nothing. `IfNoneMatch` on a write accepts only
592
+ `*`, and real S3 refuses anything else with `NotImplemented`.
593
+
594
+ A copy also takes conditions on its source. `CopySourceIfMatch`, `CopySourceIfNoneMatch`,
595
+ `CopySourceIfModifiedSince` and `CopySourceIfUnmodifiedSince` refuse the copy with
596
+ `PreconditionFailed` when the source does not meet them. An ETag condition outranks the date
597
+ condition paired with it, as RFC 7232 has it. The served endpoint reads all of these from the same
598
+ headers real S3 does.
599
+
600
+ A failed completion leaves the upload in place, and it can be completed again.
601
+
534
602
  ## Uploading an Object in parts
535
603
 
536
604
  Yulin supports multipart upload through the SDK and served S3 endpoint. This covers the operations
@@ -834,9 +902,6 @@ See [Serve simulated S3 on localhost](#serve-simulated-s3-on-localhost) for sett
834
902
  - Both Buckets have to belong to the same simulated S3. A copy across Accounts or Regions is left
835
903
  out.
836
904
  - A presigned copy is left out, and so is a copy reaching a Bucket through simulated CloudFront.
837
- - `CopySourceIfMatch`, `CopySourceIfNoneMatch`, `CopySourceIfModifiedSince` and
838
- `CopySourceIfUnmodifiedSince` are ignored. A conditional copy happens whatever the condition
839
- says.
840
905
  - `ACL` is ignored. Sim S3 models no Object ACL.
841
906
  - A `versionId` in `CopySource` is refused with `NotImplemented`.
842
907
  - A copy of an Object that was uploaded in parts gets a plain ETag rather than the multipart form.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kensio/yulin",
3
- "version": "1.21.31",
3
+ "version": "1.21.32",
4
4
  "description": "AWS system behaviour simulation for isolated unit testing",
5
5
  "repository": "https://github.com/KensioSoftware/yulin",
6
6
  "homepage": "https://yulinsim.dev/",
@@ -293,7 +293,7 @@
293
293
  "eslint-plugin-jsdoc": "^64.0.1",
294
294
  "eslint-plugin-no-secrets": "^2.3.3",
295
295
  "eslint-plugin-security": "^4.0.1",
296
- "eslint-plugin-unicorn": "^74.0.0",
296
+ "eslint-plugin-unicorn": "^75.0.0",
297
297
  "execa": "^10.0.0",
298
298
  "fta-cli": "^3.0.0",
299
299
  "hyparquet": "^1.29.2",