@kensio/yulin 1.21.6 → 1.21.8

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 (74) hide show
  1. package/README.md +22 -2
  2. package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-creator.js +1 -0
  3. package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.d.ts +4 -0
  4. package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.js +26 -1
  5. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-creator.js +3 -1
  6. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-properties.d.ts +0 -7
  7. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-properties.js +0 -20
  8. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-unsimulated-properties.d.ts +13 -0
  9. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-unsimulated-properties.js +59 -0
  10. package/dist/service/eventbridge/cfn/sim-cfn-event-bridge-resource-error.d.ts +1 -1
  11. package/dist/service/eventbridge/cfn/sim-cfn-event-bridge-resource-error.js +1 -1
  12. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +1 -1
  13. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.d.ts +3 -2
  14. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.js +25 -3
  15. package/dist/service/ses/command/authorize/sim-ses-authorizer.d.ts +9 -0
  16. package/dist/service/ses/command/authorize/sim-ses-authorizer.js +18 -2
  17. package/dist/service/ses/command/send/sim-ses-send-email.js +2 -1
  18. package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-creator.js +1 -0
  19. package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-properties.d.ts +9 -4
  20. package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-properties.js +18 -6
  21. package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-property-names.d.ts +10 -0
  22. package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-property-names.js +16 -1
  23. package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-creator.js +1 -1
  24. package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.d.ts +11 -6
  25. package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.js +18 -9
  26. package/docs/README.md +40 -4
  27. package/docs/ai-skill/README.md +57 -54
  28. package/docs/cli/README.md +84 -94
  29. package/docs/factories/README.md +42 -54
  30. package/docs/lint/README.md +41 -67
  31. package/docs/non-aws-dependencies/README.md +72 -168
  32. package/docs/sdk/README.md +109 -95
  33. package/docs/serve/README.md +192 -898
  34. package/docs/services/acm/README.md +24 -40
  35. package/docs/services/apigateway/README.md +52 -71
  36. package/docs/services/apigatewayv2/README.md +55 -74
  37. package/docs/services/athena/README.md +17 -26
  38. package/docs/services/backup/README.md +29 -39
  39. package/docs/services/bedrock/README.md +38 -52
  40. package/docs/services/cloudformation/README.md +43 -55
  41. package/docs/services/cloudfront/README.md +69 -95
  42. package/docs/services/cloudwatch/README.md +40 -54
  43. package/docs/services/cognito/README.md +30 -45
  44. package/docs/services/dynamodb/README.md +34 -51
  45. package/docs/services/ecr/README.md +36 -77
  46. package/docs/services/ecs/README.md +26 -46
  47. package/docs/services/elbv2/README.md +19 -29
  48. package/docs/services/eventbridge/README.md +24 -20
  49. package/docs/services/firehose/README.md +24 -32
  50. package/docs/services/glue/README.md +41 -78
  51. package/docs/services/iam/README.md +13 -15
  52. package/docs/services/kinesis/README.md +53 -93
  53. package/docs/services/kms/README.md +22 -32
  54. package/docs/services/lambda/README.md +60 -80
  55. package/docs/services/logs/README.md +41 -50
  56. package/docs/services/organizations/README.md +50 -85
  57. package/docs/services/personalize/README.md +28 -44
  58. package/docs/services/rekognition/README.md +26 -38
  59. package/docs/services/route53/README.md +17 -17
  60. package/docs/services/s3/README.md +47 -51
  61. package/docs/services/scheduler/README.md +41 -52
  62. package/docs/services/secretsmanager/README.md +27 -42
  63. package/docs/services/ses/README.md +24 -34
  64. package/docs/services/sns/README.md +31 -33
  65. package/docs/services/sqs/README.md +14 -14
  66. package/docs/services/ssm/README.md +19 -21
  67. package/docs/services/stepfunctions/README.md +18 -20
  68. package/docs/services/sts/README.md +32 -45
  69. package/docs/services/wafv2/README.md +12 -17
  70. package/docs/terraform/README.md +108 -126
  71. package/docs/testing/README.md +228 -0
  72. package/docs/time/README.md +87 -117
  73. package/llms.txt +2 -1
  74. package/package.json +1 -1
@@ -1,18 +1,14 @@
1
1
  # Simulated Organizations
2
2
 
3
- Yulin simulates AWS Organizations service control policies. A test can find out that the
4
- organization around an account forbids something before a deployment does.
3
+ Yulin simulates AWS Organizations service control policies (SCPs) and organization structure.
5
4
 
6
- A service control policy filters what an account's principals may do and grants nothing. Policies
7
- attach to the organization root, to an organizational unit, or to one account, and an account
8
- inherits every policy on the path down to it. Sim IAM evaluates them ahead of that account's
9
- identity and resource policies. An SCP therefore applies to a CloudFormation deployment, an
10
- intercepted SDK client, and a direct service call alike.
5
+ An SCP limits permissions granted by identity and resource policies. Attach one to the organization
6
+ root, an organizational unit or an account. An account inherits policies from every node on its path
7
+ from the root. Simulated IAM applies them to direct calls, intercepted clients and CloudFormation.
11
8
 
12
9
  ## Attach a policy to an organizational unit
13
10
 
14
- A policy is usually attached to an organizational unit rather than to one account, and every account
15
- under that unit inherits it. Units nest, and the root sits above all of them.
11
+ Create an organizational unit and attach an SCP to it. Accounts below the unit inherit the policy.
16
12
 
17
13
  ```typescript sim-organizations-organizational-unit
18
14
  /**
@@ -44,16 +40,13 @@ const decision = simAws.account("123456789012").iam().authorize({
44
40
  console.log(decision.value); // "ExplicitDeny"
45
41
  ```
46
42
 
47
- The policy hangs two levels above the account and still reaches it. `createOrganizationalUnit` takes
48
- a parent unit as its second argument, and leaves the unit under the root without one.
49
- `organizations.root()` is the node above everything, and a policy attached there covers every
50
- account in the organization.
43
+ Pass a parent as the second argument to `createOrganizationalUnit` to nest units. With no parent, the
44
+ new unit sits below `organizations.root()`.
51
45
 
52
46
  ## Every level has to allow the action
53
47
 
54
- An account is filtered by each node on the path from the root down to it, and each one has to allow
55
- an action on its own. A root allowing S3 and a unit allowing DynamoDB leave an account beneath them
56
- able to do neither.
48
+ Every node on the path must allow the action. An allow at one level cannot supply an allow missing
49
+ at another. A deny at any level rejects the request.
57
50
 
58
51
  ```typescript sim-organizations-every-level-allows
59
52
  /**
@@ -89,15 +82,12 @@ console.log(decision.value); // "ImplicitDeny"
89
82
  console.log(decision.serviceControlPolicy.unallowedLevels); // [ "Workloads" ]
90
83
  ```
91
84
 
92
- `unallowedLevels` names the nodes that allowed nothing matching. That is the part of a real SCP
93
- denial that takes longest to track down.
94
-
95
- A `Deny` at any level ends the request whatever another level allows.
85
+ `unallowedLevels` identifies the nodes with no matching allow.
96
86
 
97
87
  ## The management account
98
88
 
99
- `setManagementAccount` names the account AWS exempts from every service control policy. That account
100
- is decided by its identity and resource policies alone, whatever is attached above it.
89
+ Use `setManagementAccount` to exempt one account from SCP evaluation. Its identity and resource
90
+ policies still apply.
101
91
 
102
92
  ```typescript sim-organizations-management-account
103
93
  /**
@@ -126,9 +116,8 @@ console.log(decision.serviceControlPolicy.isApplied); // false
126
116
 
127
117
  ## Attach a service control policy to an Account
128
118
 
129
- A policy attached straight to an account applies to that account alone. An organization spans
130
- accounts, so it belongs to the whole simulated environment and is reached as
131
- `simAws.organizations()`, not from an account scope.
119
+ An SCP attached directly to an account applies only to that account. Organization state belongs to
120
+ the whole `SimAws` instance and is available through `simAws.organizations()`.
132
121
 
133
122
  ```typescript sim-organizations-attach-scp
134
123
  /**
@@ -159,17 +148,15 @@ console.log(decision.serviceControlPolicy.isDenied); // true
159
148
  console.log(decision.serviceControlPolicy.denyStatements[0]?.Sid); // "DenyBucketCreation"
160
149
  ```
161
150
 
162
- The account also gets AWS's own `FullAWSAccess` policy, as it would in a real organization. One
163
- `Deny` statement therefore denies that one action and leaves the rest of the account working.
151
+ New organization nodes receive the AWS-managed `FullAWSAccess` policy. A deny-list SCP can then
152
+ block one action while leaving other actions allowed.
164
153
 
165
- An account with no policy attached to it stays outside the organization's reach, and its identity
166
- and resource policies decide its requests as they did before.
154
+ An account outside the organization is unaffected by SCPs.
167
155
 
168
156
  ## Catch a deployment the policy denies
169
157
 
170
- Sim CloudFormation creates each resource through the owning service's command handler, and that
171
- handler authorizes. A deployment that names no principal is decided as the account root. An SCP
172
- applies to a member account's root the same way AWS does.
158
+ CloudFormation creates resources through each service's authorized command path. A deployment with
159
+ no caller runs as the account root, and SCPs apply to that root.
173
160
 
174
161
  ```typescript sim-organizations-scp-deployment
175
162
  /**
@@ -213,14 +200,13 @@ const failed = simAws
213
200
  console.log(failed?.status); // "CREATE_FAILED"
214
201
  ```
215
202
 
216
- The resource is left `CREATE_FAILED` and the deployment rejects. A test asserting that a stack
217
- deploys then fails on the policy, with the policy named in the message.
203
+ When an SCP denies creation, the resource enters `CREATE_FAILED` and the deployment rejects. The
204
+ error names the policy.
218
205
 
219
206
  ## Name the principal a deployment runs as
220
207
 
221
- An organization that denies its accounts' root principals is ordinary, and a deployment decided as
222
- the root fails under one. `caller` says which principal the resources are created as, and a
223
- statement conditioned on `aws:PrincipalArn` then has a deploy role to match against.
208
+ Pass `caller` when the deployment should run as a role. SCP conditions on `aws:PrincipalArn` then
209
+ evaluate against that role.
224
210
 
225
211
  ```typescript sim-organizations-scp-deploy-role
226
212
  /**
@@ -285,17 +271,15 @@ const stack = await simAws.cloudFormation().deployTemplate({
285
271
  console.log(stack.getResource("ReportsBucket")?.status); // "CREATE_COMPLETE"
286
272
  ```
287
273
 
288
- The Role is created before the policy is attached. Creating it afterwards is a call the policy
289
- denies the root, and the account root is who a bare `createRole` runs as.
274
+ Create the role before attaching a policy that would deny the setup call.
290
275
 
291
276
  ## Name the caller the rest of a test reads as
292
277
 
293
- A deployment names its own principal. Every other call in the test still names none, and each one is
294
- the account root. Under a policy denying that root, a test reading back what a stack made is denied
295
- on every read.
278
+ A deployment caller applies only to that deployment. Other calls still use the account root unless
279
+ the simulation has a default caller.
296
280
 
297
- `defaultCaller` on `SimAws` says who those calls are. A test then reads the account as the person or
298
- role that would really be looking at it, and an explicit `caller` still wins wherever one is given.
281
+ Set `defaultCaller` on `SimAws` to choose the principal for unattributed calls. An explicit `caller`
282
+ still takes precedence.
299
283
 
300
284
  ```typescript sim-organizations-scp-default-caller
301
285
  /**
@@ -369,17 +353,13 @@ console.log(read.Parameter?.Value); // "reports-bucket"
369
353
  console.log(identity.Arn); // "arn:aws:iam::123456789012:role/Administrator"
370
354
  ```
371
355
 
372
- Setup runs before the policy is attached, for the same reason the deploy Role above does. The Role a
373
- simulation reads as has to exist and hold a policy, and creating it is itself a call.
374
-
375
- Naming a default caller says who an unattributed call comes from, and leaves the root's own identity
376
- access where it was. A test about root behaviour names `simAws.account().rootPrincipal` and gets the
377
- root. Under the policy above that call is denied, which is what the statement is written to do.
356
+ Create and configure the default caller before attaching policies that would deny those setup calls.
357
+ Pass `simAws.account().rootPrincipal` explicitly when a test needs to make a request as root.
378
358
 
379
359
  ## Write an allow list instead of a deny list
380
360
 
381
- `detachFullAwsAccess` takes AWS's own policy off an account. What remains has to allow an action
382
- for the account to be allowed it. That is an organization run as an allow list.
361
+ Call `detachFullAwsAccess` to use an allow-list SCP. The remaining policies must explicitly allow an
362
+ action.
383
363
 
384
364
  ```typescript sim-organizations-scp-allow-list
385
365
  /**
@@ -407,16 +387,13 @@ console.log(decision.denialReason);
407
387
  // "because no service control policy allows the s3:GetObject action"
408
388
  ```
409
389
 
410
- An account root holds unrestricted access in sim IAM, and this denies it anyway. That is what an
411
- SCP does in AWS, and it is why an allow list is worth writing in a test at all.
412
-
413
- Detaching `FullAWSAccess` on its own leaves the account holding no policy, and every action is then
414
- denied. AWS behaves the same way, and warns about it.
390
+ SCPs still limit an account root. Removing `FullAWSAccess` without adding another allow policy denies
391
+ every action.
415
392
 
416
393
  ## Deploy an organization from CloudFormation
417
394
 
418
- A template's `AWS::Organizations::*` resources build the organization they describe, so a stack
419
- already managing the org chart is the same one a test deploys.
395
+ Simulated CloudFormation supports the organization, organizational unit, account and policy
396
+ resource types.
420
397
 
421
398
  ```typescript sim-organizations-cloudformation
422
399
  /**
@@ -470,17 +447,13 @@ const decision = simAws.account("123456789012").iam().authorize({
470
447
  console.log(decision.value); // "ExplicitDeny"
471
448
  ```
472
449
 
473
- `Content` takes the policy document inline or as JSON text. `TargetIds` takes a list or a single
474
- value, and each entry names the root, a unit, or an Account. A policy reaches every target it names
475
- or none of them. A target this organization has never heard of fails the resource before anything is
476
- attached.
450
+ `Content` accepts an object or JSON text. `TargetIds` accepts one target or a list of roots, units and
451
+ accounts. An unknown target fails the resource before the policy is attached anywhere.
477
452
 
478
- A simulated environment has one organization from the start, so
479
- `AWS::Organizations::Organization` records the one already there rather than making another. It is
480
- worth declaring for `RootId`, which is what a unit hangs off.
453
+ A `SimAws` instance starts with one organization. `AWS::Organizations::Organization` adopts it and
454
+ exposes its `RootId`.
481
455
 
482
- `AWS::Organizations::Account` creates an Account with an id nobody chose, as AWS does. Read that id
483
- back with `Ref` or `Fn::GetAtt AccountId`.
456
+ `AWS::Organizations::Account` generates an account ID. Read it with `Ref` or `Fn::GetAtt AccountId`.
484
457
 
485
458
  These are the properties read from each resource:
486
459
 
@@ -491,10 +464,8 @@ These are the properties read from each resource:
491
464
  | `AWS::Organizations::Account` | `AccountName`, `Email`, `ParentIds` | `Tags`, `RoleName` |
492
465
  | `AWS::Organizations::Policy` | `Name`, `Type`, `Content`, `TargetIds` | `Tags`, `Description` |
493
466
 
494
- Tearing the stack down takes its own policies off the nodes they were attached to, removes the
495
- units, and takes any Account the stack created back out of the organization. A node holding
496
- policies from more than one stack keeps the others. A unit that still holds something when it goes
497
- hands what it holds to its parent, so every Account keeps a path to the root.
467
+ Stack teardown removes resources created by that stack. Policies from other stacks remain attached.
468
+ Accounts and child units move to the deleted unit's parent.
498
469
 
499
470
  ## Reading a denial
500
471
 
@@ -510,21 +481,16 @@ sides, through `decision.serviceControlPolicy`:
510
481
  | `denyStatements` | The matching `Deny` statements. |
511
482
  | `allowStatements` | The matching `Allow` statements. |
512
483
 
513
- `decision.denialReason` carries the wording AWS puts on the `AccessDenied` message, and every
514
- simulated service passes it through to the error it throws.
484
+ `decision.denialReason` contains the text used by service access-denied errors.
515
485
 
516
- `simAws.organizations().serviceControlPoliciesFor(accountId)` returns the policies in force for an
517
- account, in the order they were evaluated, including `FullAWSAccess` where it is still attached.
486
+ `serviceControlPoliciesFor(accountId)` returns the policies in evaluation order.
518
487
 
519
- `serviceControlPolicySetFor(accountId).levels` keeps the policies grouped by the node they hang on,
520
- root first. That grouping is what sim IAM evaluates.
488
+ `serviceControlPolicySetFor(accountId).levels` groups policies by node, starting at the root.
521
489
 
522
- The flattened list is empty in three cases that behave differently. An account that was never named
523
- sits outside the organization and stays unrestricted. The management account is exempt and equally
524
- unrestricted. An account left holding no policy is denied everything. `applies` separates the last
525
- of those from the other two.
526
- `serviceControlPolicySetFor(accountId).applies` tells the two apart, and so does
527
- `decision.serviceControlPolicy.isApplied`.
490
+ An empty policy list can mean that the account is outside the organization, is the management
491
+ account or has no policies. The first two are exempt. The last is denied every action. Check
492
+ `serviceControlPolicySetFor(accountId).applies` or `decision.serviceControlPolicy.isApplied` to
493
+ distinguish them.
528
494
 
529
495
  ## Available functionality
530
496
 
@@ -567,7 +533,6 @@ Simulated Organizations supports:
567
533
  | Service-linked roles | Evaluated like any other principal. AWS exempts a service-linked role from SCPs. |
568
534
  | Other policy types | Resource control policies, declarative policies, tag policies, backup policies and AI services opt-out policies are not simulated. |
569
535
  | The Organizations SDK | `CreatePolicy`, `AttachPolicy`, `ListAccounts` and the rest of the API are not handled. Policies are attached through the accessor. |
570
- | CloudFormation | `AWS::Organizations::Organization`, `::OrganizationalUnit`, `::Account` and `::Policy` are not created from a template. |
571
536
  | Organization condition keys | `aws:PrincipalOrgID` and `aws:PrincipalOrgPaths` are not populated. A condition naming either fails to match. |
572
537
  | Service principals | A request whose caller is a service principal or anonymous belongs to no Account and is subject to no policy. |
573
538
  | Condition operator coverage | The operators above are evaluated. Anything else fails closed and the statement holding it matches nothing. |
@@ -1,17 +1,15 @@
1
1
  # Simulated Personalize
2
2
 
3
- Simulated Personalize holds the resources a recommendation is served from. No model is trained and
4
- no data is read, in the way simulated ACM issues certificates without producing real TLS
5
- certificates. A dataset group, a solution, a solution version and a campaign all exist, carry what
6
- the request gave them, and reach `ACTIVE` straight away.
3
+ Yulin simulates the Personalize resources and API calls used to serve recommendations. It does not
4
+ train a model or derive results from datasets. Tests declare the recommendations that a campaign or
5
+ recommender should return. Created resources become `ACTIVE` immediately.
7
6
 
8
7
  Personalize-specific types are imported from the `@kensio/yulin/personalize` subpath.
9
8
 
10
9
  ## Building the chain to a campaign
11
10
 
12
- Every runtime recommendation names a campaign, and a campaign is the far end of a chain. The dataset
13
- group holds the data, the solution picks a recipe, the solution version is the trained model, and
14
- the campaign serves it.
11
+ A campaign is built from a resource chain. A dataset group contains the datasets, a solution selects
12
+ a recipe, a solution version represents the trained model, and a campaign serves that version.
15
13
 
16
14
  ```typescript sim-personalize-campaign-chain
17
15
  /**
@@ -73,9 +71,8 @@ expects.
73
71
 
74
72
  ## Recommendations from a campaign
75
73
 
76
- A campaign answers the runtime API from results declared against it. No model is fitted and no
77
- interaction history is held. A test says what one campaign recommends for one item, and the code
78
- under test makes the calls it would make against AWS.
74
+ A campaign returns results declared during test setup. Application code still sends the same
75
+ Personalize Runtime commands it sends to AWS.
79
76
 
80
77
  The two runtime operations live on `simAws.personalizeRuntime()`, and an intercepted
81
78
  `PersonalizeRuntimeClient` reaches the same place. They arrive from a separate SDK package
@@ -140,9 +137,8 @@ const recommended = await simAws.personalizeRuntime().getRecommendations(
140
137
  console.log(recommended.itemList?.map((item) => item.itemId).join(" "));
141
138
  ```
142
139
 
143
- Results are declared per campaign, through `recommendations()` and `rankings()`. A campaign serves
144
- one solution version trained on one recipe, and two campaigns in a dataset group answer the same
145
- entry differently.
140
+ Declare results per campaign through `recommendations()` and `rankings()`. Two campaigns in the same
141
+ dataset group can return different results for the same input.
146
142
 
147
143
  An item rule is read first where the request carries an item, then a user rule, then the default.
148
144
  That order follows the recipes. `aws-similar-items` requires an `itemId` and looks at no user, and
@@ -152,9 +148,8 @@ would have used. Matching is exact, with no pattern syntax.
152
148
  `numResults` cuts a declared list to length. A request no rule matches gets the campaign's default,
153
149
  and an empty `itemList` where no default is declared.
154
150
 
155
- An item is declared as an id on its own, or as an id with a score for a test to assert on. The
156
- number comes from the declaration, and a rule that leaves it out answers with an item carrying no
157
- score.
151
+ Declare an item as an ID or as an ID with a score. An item declared without a score is returned
152
+ without one.
158
153
 
159
154
  ```typescript sim-personalize-recommendation-scores
160
155
  /**
@@ -228,10 +223,8 @@ Runtime does.
228
223
 
229
224
  ## Datasets and schemas
230
225
 
231
- A dataset belongs to a dataset group and has one of five types. The dataset ARN carries the group
232
- and the type rather than the name the request gave, which is how real Personalize builds it. One
233
- dataset group therefore holds one dataset of each type, and two dataset groups can each hold an
234
- `INTERACTIONS` dataset without colliding.
226
+ A dataset belongs to a dataset group and has one of five types. Its ARN contains the group and type,
227
+ not the requested name. Each group can contain one dataset of each type.
235
228
 
236
229
  ```typescript sim-personalize-dataset
237
230
  /**
@@ -289,8 +282,8 @@ domain dataset group refuses them here too.
289
282
 
290
283
  ## Recording events
291
284
 
292
- An event tracker is where `PutEvents` sends item interactions. It is created against a dataset
293
- group and reports a tracking ID back, and every `PutEvents` names that ID.
285
+ An event tracker records the item interactions sent through `PutEvents`. Create it for a dataset
286
+ group, then pass its tracking ID to each request.
294
287
 
295
288
  ```typescript sim-personalize-event-tracker
296
289
  /**
@@ -464,10 +457,9 @@ console.log(group.domain);
464
457
 
465
458
  ## Recommenders and their use cases
466
459
 
467
- A recommender goes straight onto a Domain dataset group, for one of the ten use cases AWS trained.
468
- There is no solution and no solution version in between. `GetRecommendations` then names a
469
- `recommenderArn` where the custom path names a `campaignArn`, and results are declared against it
470
- through the same `recommendations()` rules.
460
+ A recommender belongs directly to a domain dataset group and selects one of ten AWS use cases. It
461
+ does not use a solution or solution version. Pass its ARN to `GetRecommendations` and declare its
462
+ results with the same `recommendations()` API used for campaigns.
471
463
 
472
464
  ```typescript sim-personalize-recommender
473
465
  /**
@@ -525,9 +517,8 @@ Real Personalize trains for hours and retrains every seven days.
525
517
 
526
518
  ### The ten use cases
527
519
 
528
- The recipe ARN picks the use case, and the use case decides what a request has to carry. A request
529
- leaving out a parameter its use case requires is refused, which is what real Personalize does with
530
- it. That refusal is the part of the domain path worth simulating, and everything else here is state.
520
+ The recipe ARN selects the use case. Each use case defines whether `itemId` and `userId` are
521
+ required, optional, or unused. Requests missing a required parameter are rejected.
531
522
 
532
523
  | Use case | Recipe ARN suffix | `itemId` | `userId` |
533
524
  | ---------------------------------- | ---------------------------------------------- | -------- | -------- |
@@ -555,9 +546,6 @@ A parameter marked unused is ignored rather than matched on. A `Top picks for yo
555
546
  an `itemId` as well as its `userId` is answered from the user rule, since that is the tier real
556
547
  Personalize would have used. An optional one is matched where the request carries it.
557
548
 
558
- These are the requirements AWS documents against each use case. Both pages are worth reading before
559
- writing a request, since two of them differ from the e-commerce use case they otherwise mirror.
560
-
561
549
  ### Starting and stopping
562
550
 
563
551
  `StopRecommender` leaves the recommender in place and stops it serving. `GetRecommendations` against
@@ -602,8 +590,7 @@ and solutions gone. A group still holding one is reported as `ResourceInUseExcep
602
590
 
603
591
  `AWS::Personalize::DatasetGroup`, `AWS::Personalize::Schema`, `AWS::Personalize::Dataset`,
604
592
  `AWS::Personalize::Solution` and `AWS::Personalize::EventTracker` deploy into simulated Personalize.
605
- A project that declares them in CDK or CloudFormation can deploy the same template its application
606
- deploys, with no hand-written test setup.
593
+ A project can deploy its synthesized template without hand-written Personalize setup.
607
594
 
608
595
  Each one goes through the ordinary create command. A template and an SDK caller get the same
609
596
  validation, the same refusals and the same ARN.
@@ -710,19 +697,16 @@ wiring one resource into another reads `Fn::GetAtt`. An event tracker publishes
710
697
  does not publish fails the deploy, since answering one would let a template deploy here and fail on
711
698
  AWS.
712
699
 
713
- ### A stack stops at the solution
700
+ ### Resources without CloudFormation types
714
701
 
715
- CloudFormation has no `AWS::Personalize::Campaign` type and no `AWS::Personalize::Recommender` type.
716
- A campaign is what every runtime call names, and it is always created out of band through the SDK,
717
- the CLI or the console. A deployed stack gets as far as a solution and stops there. A test creates
718
- the solution version and the campaign itself, as the example above does. This is real Personalize
719
- behaviour and worth knowing before reading it as a gap in the simulation.
702
+ CloudFormation has no `AWS::Personalize::Campaign` or `AWS::Personalize::Recommender` type. Create
703
+ these resources through the SDK after deploying the stack, as the example above does.
720
704
 
721
705
  The domain path stops in the same place. `Domain` is a property of `AWS::Personalize::DatasetGroup`,
722
706
  and a template can declare a Domain dataset group. No `AWS::Personalize::Recommender` type exists to
723
707
  put a recommender on it.
724
708
 
725
- ### Types a stack steps over
709
+ ### Skipped resource types
726
710
 
727
711
  `AWS::Personalize::BatchInferenceJob`, `AWS::Personalize::BatchSegmentJob`,
728
712
  `AWS::Personalize::DataDeletionJob`, `AWS::Personalize::MetricAttribution` and
@@ -763,9 +747,9 @@ console.log(group?.name, group?.status);
763
747
 
764
748
  ## Deleting resources
765
749
 
766
- Deletion follows real Personalize. A dataset group holding datasets, solutions or an event tracker
767
- is reported as `ResourceInUseException`, and so is a solution a campaign still deploys. Tear a chain
768
- down from the campaign end.
750
+ Delete resources from the campaign end of the chain. A dataset group that still contains datasets,
751
+ solutions, or an event tracker raises `ResourceInUseException`. A solution still used by a campaign
752
+ raises the same error.
769
753
 
770
754
  Deleting an event tracker leaves the events it accepted recorded, as real Personalize leaves the
771
755
  interactions it wrote in the dataset behind it.
@@ -1,8 +1,8 @@
1
1
  # Simulated Rekognition
2
2
 
3
- Simulated Rekognition answers detection calls from results declared against images. A test can say
4
- which image fails moderation or holds a cat, with no image analysis happening. The bytes are never
5
- looked at.
3
+ Yulin simulates Rekognition by returning results declared for an image name or content hash. It does
4
+ not analyze the image. Tests can define labels, moderation results, faces, and face matches while
5
+ application code uses the normal Rekognition commands.
6
6
 
7
7
  Rekognition-specific types are imported from the `@kensio/yulin/rekognition` subpath.
8
8
 
@@ -67,9 +67,8 @@ const detected = await simAws
67
67
 
68
68
  ## Detecting labels in an image
69
69
 
70
- `DetectLabels` answers with the objects, scenes and concepts an image is declared to hold. Each
71
- label carries the parents, aliases, categories and instances it was declared with, and no more. A
72
- label is reported as written.
70
+ `DetectLabels` returns the objects, scenes, and concepts declared for an image. Each label contains
71
+ only the parents, aliases, categories, and instances in its declaration.
73
72
 
74
73
  ```typescript sim-rekognition-detect-labels
75
74
  /**
@@ -131,19 +130,16 @@ console.log(detected.LabelModelVersion); // "3.0"
131
130
  Labels come back in descending order of confidence, which is the order real Rekognition reports them
132
131
  in. A declared instance with no confidence of its own takes its label's.
133
132
 
134
- An image no rule matches gets the built-in default result. That is the one `Mobile Phone` label from
135
- the example response in the AWS `DetectLabels` documentation, with the parent, alias, category and
136
- bounding box AWS documents it with. It is a real Rekognition response, though which labels an
137
- unconfigured image gets is a simulator convention rather than what AWS would return for it.
133
+ An image that matches no rule gets a built-in `Mobile Phone` result based on the AWS
134
+ `DetectLabels` example response. This default is a Yulin convention. AWS results depend on the image.
138
135
 
139
- A label name fills in nothing of its own. Declaring `Cat` with no parents reports `Cat` with no
140
- parents, and declaring a `Pizza` nobody has heard of reports `Pizza`. Yulin ships no general label
141
- ontology to check a name against or to expand one from.
136
+ Yulin does not validate or expand general detection labels. Declaring `Cat` without parents returns
137
+ `Cat` without parents.
142
138
 
143
139
  ## Detecting faces in an image
144
140
 
145
- `DetectFaces` answers with the faces an image is declared to hold. A face says where it is and what
146
- it looks like, and the response carries the attributes the request asked for.
141
+ `DetectFaces` returns the faces declared for an image, including their positions and requested
142
+ attributes.
147
143
 
148
144
  ```typescript sim-rekognition-detect-faces
149
145
  /**
@@ -204,8 +200,7 @@ Faces come back in the order they were declared. An attribute with no confidence
204
200
  the face's, and a face detected at 99.4 is reported as smiling at 99.4. A face declared with no
205
201
  confidence at all is detected at the built-in one.
206
202
 
207
- An image with nobody in it is `{ faces: [] }`. Two built-in results cover the counting a test
208
- usually does:
203
+ Use `{ faces: [] }` for an image with no faces. Two built-in results cover common face-count tests:
209
204
 
210
205
  ```typescript
211
206
  import {
@@ -219,10 +214,8 @@ faces.onName("incoming/landscape.png", simRekognitionNoFaces);
219
214
  faces.onName("incoming/crowd.png", simRekognitionSeveralFaces);
220
215
  ```
221
216
 
222
- An image no rule matches gets the built-in default result. That is the one face from the example
223
- response in the AWS `DetectFaces` documentation, with the attributes and all thirty landmarks AWS
224
- documents it with. It is a real Rekognition response, though which face an unconfigured image gets is
225
- a simulator convention rather than what AWS would return for it.
217
+ An image that matches no rule gets the face from the AWS `DetectFaces` example response, including
218
+ its attributes and 30 landmarks. This default is a Yulin convention.
226
219
 
227
220
  ## Choosing the facial attributes
228
221
 
@@ -305,11 +298,10 @@ excludes the chin.
305
298
 
306
299
  ## Declaring results
307
300
 
308
- Results are declared per operation. `moderation()` holds the rules `DetectModerationLabels` answers
309
- from, `labels()` holds the rules `DetectLabels` answers from, `faces()` holds the rules
310
- `DetectFaces` answers from, and `faceMatches()` holds the rules `SearchFacesByImage` answers from.
311
- All four take the same three kinds of rule, being an exact S3 object name, an exact content hash, or
312
- anything at all.
301
+ Declare results separately for each operation. Use `moderation()` for `DetectModerationLabels`,
302
+ `labels()` for `DetectLabels`, `faces()` for `DetectFaces`, and `faceMatches()` for
303
+ `SearchFacesByImage`. Each API accepts rules for an exact S3 object name, an exact content hash, or a
304
+ default result.
313
305
 
314
306
  ```typescript sim-rekognition-moderation-rules
315
307
  /**
@@ -340,8 +332,8 @@ moderation.onHash(simRekognitionImageHash(fixture), {
340
332
  });
341
333
  ```
342
334
 
343
- A hash rule wins, then a name rule, then the default. Matching is exact, with no pattern syntax.
344
- Which rule applies never depends on how specific a pattern looks.
335
+ Rules use exact matching. Hash rules take precedence over name rules, which take precedence over the
336
+ default.
345
337
 
346
338
  A name is the `Name` in the request, the S3 object key. It is matched on its own, with the Bucket
347
339
  left out, so a rule for a key applies to that key in whichever Bucket the request names. An image
@@ -412,9 +404,8 @@ refused where the rule is written.
412
404
 
413
405
  ## Sample images
414
406
 
415
- Simulated Rekognition ships with five images whose hashes are already declared. A test uploads one
416
- through its own code and gets a known answer without registering anything. That is what makes an
417
- application generating its own object keys testable, since the test never has to know the key.
407
+ Yulin includes five small images with predeclared hash rules. Application code can upload one under
408
+ any object key and receive a known result without registering another rule.
418
409
 
419
410
  | Image | Format | Detected as |
420
411
  | -------------------------------------------------- | ------ | ------------------------------------------------- |
@@ -477,9 +468,8 @@ simAws
477
468
  .onHash(simRekognitionImageHash(sample), { labels: [] });
478
469
  ```
479
470
 
480
- The images are real 16 by 16 PNG and JPEG files, 1,909 bytes in total. The format check reads their
481
- magic bytes as it does for any other image. What they are pictures of decides nothing, since no
482
- image is looked at.
471
+ The images are valid 16 by 16 PNG and JPEG files. Rekognition checks their file signatures but does
472
+ not inspect their visual content.
483
473
 
484
474
  ## Moderation labels come back with their parents
485
475
 
@@ -772,7 +762,7 @@ ever. Filter the notification configuration by prefix or suffix, as this one doe
772
762
 
773
763
  ## Face collections
774
764
 
775
- A collection is what lets an application recognise the same person twice, where a detection answers what is in one image.
765
+ A collection stores indexed faces so an application can search for the same person later.
776
766
 
777
767
  ```typescript sim-rekognition-collections
778
768
  /**
@@ -970,9 +960,7 @@ An image is read from a Bucket in another Account when that Bucket's policy allo
970
960
  real Rekognition reads across Accounts. A Bucket in another Region is refused, as real Rekognition
971
961
  reads only Buckets in its own Region.
972
962
 
973
- ## Available functionality
974
-
975
- Simulated Rekognition currently supports:
963
+ ## Supported operations
976
964
 
977
965
  - `DetectModerationLabelsCommand`, `DetectLabelsCommand` and `DetectFacesCommand`, for an image
978
966
  supplied as `Image.Bytes` or as `Image.S3Object`