@kensio/yulin 1.20.14 → 1.20.16

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 (154) hide show
  1. package/README.md +13 -0
  2. package/{dist/config/oxlint/cffjs2.oxlintrc.json → cffjs2.oxlintrc.json} +1 -1
  3. package/dist/config/oxlint/cffjs2.oxlint.config.js +4 -3
  4. package/dist/sdk/module/sim-sdk-module-client-interceptor.d.ts +5 -0
  5. package/dist/sdk/module/sim-sdk-module-client-interceptor.js +14 -0
  6. package/dist/sdk/module/sim-sdk-static-client-factory.d.ts +26 -0
  7. package/dist/sdk/module/sim-sdk-static-client-factory.js +59 -0
  8. package/dist/sdk/send-patch.d.ts +8 -0
  9. package/dist/sdk/send-patch.js +11 -0
  10. package/dist/service/aws/caller/sim-aws-caller-resolver.d.ts +28 -6
  11. package/dist/service/aws/caller/sim-aws-caller-resolver.js +18 -11
  12. package/dist/service/aws/caller/sim-aws-caller.d.ts +9 -0
  13. package/dist/service/aws/caller/sim-aws-resolved-caller.d.ts +19 -0
  14. package/dist/service/aws/caller/sim-aws-resolved-caller.js +26 -0
  15. package/dist/service/aws/factory/sim-aws-account-region-service-builder.d.ts +6 -0
  16. package/dist/service/aws/factory/sim-aws-account-region-service-builder.js +3 -0
  17. package/dist/service/aws/factory/sim-aws-account-service-cache.d.ts +6 -0
  18. package/dist/service/aws/factory/sim-aws-account-service-cache.js +5 -0
  19. package/dist/service/aws/factory/sim-aws-service-factory.d.ts +5 -0
  20. package/dist/service/aws/factory/sim-aws-service-factory.js +6 -0
  21. package/dist/service/aws/sim-aws-properties.d.ts +15 -0
  22. package/dist/service/aws/sim-aws.js +2 -1
  23. package/dist/service/cloudformation/resource/resolve/property/sim-cfn-resource-property-resolver.d.ts +11 -4
  24. package/dist/service/cloudformation/resource/resolve/property/sim-cfn-resource-property-resolver.js +21 -8
  25. package/dist/service/cloudformation/resource/sim-cfn-resource.type.d.ts +9 -0
  26. package/dist/service/cloudformation/template/dynamic/make-sim-cfn-dynamic-references.d.ts +2 -0
  27. package/dist/service/cloudformation/template/dynamic/make-sim-cfn-dynamic-references.js +2 -2
  28. package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-reference-resolvers.d.ts +10 -0
  29. package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-reference-resolvers.js +4 -3
  30. package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-reference-scan.d.ts +11 -0
  31. package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-reference-scan.js +21 -0
  32. package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-references.d.ts +24 -2
  33. package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-references.js +50 -11
  34. package/dist/service/cloudformation/template/dynamic/sim-cfn-prefetched-dynamic-references.d.ts +42 -0
  35. package/dist/service/cloudformation/template/dynamic/sim-cfn-prefetched-dynamic-references.js +80 -0
  36. package/dist/service/eventbridge/delivery/sim-event-bridge-delivery-failures.d.ts +16 -0
  37. package/dist/service/eventbridge/delivery/sim-event-bridge-delivery-failures.js +19 -0
  38. package/dist/service/iam/authorize/context/sim-iam-auth-z-caller-context-builder.d.ts +30 -7
  39. package/dist/service/iam/authorize/context/sim-iam-auth-z-caller-context-builder.js +20 -11
  40. package/dist/service/iam/authorize/context/sim-iam-auth-z-context-builder.d.ts +2 -1
  41. package/dist/service/iam/authorize/context/sim-iam-auth-z-context-builder.js +5 -1
  42. package/dist/service/iam/authorize/match/sim-iam-policy-principal-matcher.d.ts +10 -0
  43. package/dist/service/iam/authorize/match/sim-iam-policy-principal-matcher.js +15 -3
  44. package/dist/service/iam/authorize/sim-iam-account-auth-z.d.ts +11 -4
  45. package/dist/service/iam/authorize/sim-iam-account-auth-z.js +5 -3
  46. package/dist/service/iam/authorize/sim-iam-authorizer.d.ts +2 -1
  47. package/dist/service/iam/authorize/sim-iam-authorizer.js +1 -0
  48. package/dist/service/iam/command/policy/create-policy/create-policy-input-resolver.js +4 -1
  49. package/dist/service/iam/command/policy/put-role-policy/put-role-policy.handler.js +5 -1
  50. package/dist/service/iam/command/policy/put-user-policy/put-user-policy.handler.js +5 -1
  51. package/dist/service/iam/policy/parse/sim-iam-document-parser.d.ts +4 -1
  52. package/dist/service/iam/policy/parse/sim-iam-document-parser.js +13 -15
  53. package/dist/service/iam/policy/sim-iam-statement-label.d.ts +28 -0
  54. package/dist/service/iam/policy/sim-iam-statement-label.js +17 -0
  55. package/dist/service/iam/policy/sim-iam-statement-strings.d.ts +11 -0
  56. package/dist/service/iam/policy/sim-iam-statement-strings.js +27 -0
  57. package/dist/service/iam/role/sim-iam-role-with-policy.factory.d.ts +9 -0
  58. package/dist/service/iam/role/sim-iam-role-with-policy.factory.js +4 -2
  59. package/dist/service/iam/sim-iam-account-parts.d.ts +8 -0
  60. package/dist/service/iam/sim-iam-account-parts.js +1 -0
  61. package/dist/service/iam/validate/sim-iam-policy-document-validator.d.ts +9 -2
  62. package/dist/service/iam/validate/sim-iam-policy-document-validator.js +37 -19
  63. package/dist/service/lambda/function/code/sim-lambda-vm-zip-function.factory.d.ts +7 -0
  64. package/dist/service/lambda/function/code/sim-lambda-vm-zip-function.factory.js +1 -0
  65. package/dist/service/lambda/function/code/vm/sdk/sim-lambda-provided-sdk-module.d.ts +19 -0
  66. package/dist/service/lambda/function/code/vm/sdk/sim-lambda-provided-sdk-module.js +78 -0
  67. package/dist/service/lambda/function/code/vm/sdk/sim-lambda-sdk-packages-not-installed.error.d.ts +15 -0
  68. package/dist/service/lambda/function/code/vm/sdk/sim-lambda-sdk-packages-not-installed.error.js +27 -0
  69. package/dist/service/lambda/function/code/vm/sdk/sim-lambda-vm-sdk-module-provider.d.ts +9 -0
  70. package/dist/service/lambda/function/code/vm/sdk/sim-sdk-lambda-vm-module-provider.d.ts +10 -0
  71. package/dist/service/lambda/function/code/vm/sdk/sim-sdk-lambda-vm-module-provider.js +28 -34
  72. package/dist/service/lambda/function/code/vm/sim-lambda-host-modules.d.ts +15 -0
  73. package/dist/service/lambda/function/code/vm/sim-lambda-host-modules.js +31 -0
  74. package/dist/service/lambda/function/code/vm/sim-lambda-vm-module-resolver.d.ts +7 -0
  75. package/dist/service/lambda/function/code/vm/sim-lambda-vm-module-resolver.js +15 -0
  76. package/dist/service/lambda/function/code/vm/sim-lambda-vm-modules.js +5 -4
  77. package/dist/service/s3/command/put-bucket-policy/put-bucket-policy.handler.js +4 -1
  78. package/dist/service/scheduler/delivery/sim-scheduler-delivery-failures.d.ts +16 -0
  79. package/dist/service/scheduler/delivery/sim-scheduler-delivery-failures.js +19 -0
  80. package/dist/service/scheduler/index.d.ts +1 -1
  81. package/dist/service/scheduler/index.js +1 -1
  82. package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-dynamic-reference-resolver.d.ts +13 -15
  83. package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-dynamic-reference-resolver.js +22 -43
  84. package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-reference-stand-in.d.ts +9 -0
  85. package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-reference-stand-in.js +14 -0
  86. package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-reference-value.d.ts +12 -0
  87. package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-reference-value.js +29 -0
  88. package/dist/service/ssm/cfn/dynamic/sim-cfn-ssm-dynamic-reference-resolver.d.ts +22 -6
  89. package/dist/service/ssm/cfn/dynamic/sim-cfn-ssm-dynamic-reference-resolver.js +39 -24
  90. package/dist/service/ssm/cfn/dynamic/sim-cfn-ssm-secure-dynamic-reference-resolver.d.ts +10 -5
  91. package/dist/service/ssm/cfn/dynamic/sim-cfn-ssm-secure-dynamic-reference-resolver.js +10 -8
  92. package/dist/service/ssm/sim-ssm.d.ts +10 -2
  93. package/dist/service/ssm/sim-ssm.js +12 -4
  94. package/dist/service/sts/auth-z/assume-role-auth-z-coordinator.d.ts +9 -2
  95. package/dist/service/sts/auth-z/assume-role-auth-z-coordinator.js +2 -2
  96. package/dist/service/sts/auth-z/assume-role-source-account-auth-z.d.ts +10 -4
  97. package/dist/service/sts/auth-z/assume-role-source-account-auth-z.js +8 -7
  98. package/dist/service/sts/auth-z/assume-role-target-auth-z.d.ts +2 -2
  99. package/dist/service/sts/auth-z/assume-role-trust-policy-authorizer.d.ts +6 -2
  100. package/dist/service/sts/auth-z/assume-role-trust-policy-authorizer.js +14 -11
  101. package/dist/service/sts/command/assume-role/assume-role.handler.d.ts +5 -1
  102. package/dist/service/sts/command/assume-role/assume-role.handler.js +5 -2
  103. package/dist/service/sts/command/get-caller-identity/get-caller-identity.handler.d.ts +5 -1
  104. package/dist/service/sts/command/get-caller-identity/get-caller-identity.handler.js +4 -1
  105. package/dist/service/sts/service-role/sim-service-role.js +5 -1
  106. package/dist/service/sts/sim-sts.d.ts +9 -1
  107. package/dist/service/sts/sim-sts.js +4 -0
  108. package/docs/README.md +54 -0
  109. package/docs/ai-skill/README.md +69 -0
  110. package/docs/cli/README.md +284 -0
  111. package/docs/factories/README.md +140 -0
  112. package/docs/lint/README.md +176 -0
  113. package/docs/non-aws-dependencies/README.md +261 -0
  114. package/docs/sdk/README.md +272 -0
  115. package/docs/serve/README.md +1126 -0
  116. package/docs/services/acm/README.md +823 -0
  117. package/docs/services/apigateway/README.md +1919 -0
  118. package/docs/services/apigatewayv2/README.md +2678 -0
  119. package/docs/services/athena/README.md +1044 -0
  120. package/docs/services/bedrock/README.md +350 -0
  121. package/docs/services/cloudformation/README.md +3440 -0
  122. package/docs/services/cloudfront/README.md +2874 -0
  123. package/docs/services/cloudwatch/README.md +420 -0
  124. package/docs/services/cognito/README.md +4923 -0
  125. package/docs/services/dynamodb/README.md +3784 -0
  126. package/docs/services/ecr/README.md +302 -0
  127. package/docs/services/ecs/README.md +2010 -0
  128. package/docs/services/elbv2/README.md +1897 -0
  129. package/docs/services/eventbridge/README.md +1110 -0
  130. package/docs/services/firehose/README.md +805 -0
  131. package/docs/services/glue/README.md +501 -0
  132. package/docs/services/iam/README.md +1283 -0
  133. package/docs/services/kinesis/README.md +454 -0
  134. package/docs/services/kms/README.md +659 -0
  135. package/docs/services/lambda/README.md +3645 -0
  136. package/docs/services/logs/README.md +868 -0
  137. package/docs/services/organizations/README.md +574 -0
  138. package/docs/services/personalize/README.md +835 -0
  139. package/docs/services/rekognition/README.md +1070 -0
  140. package/docs/services/route53/README.md +1783 -0
  141. package/docs/services/s3/README.md +3024 -0
  142. package/docs/services/scheduler/README.md +806 -0
  143. package/docs/services/secretsmanager/README.md +667 -0
  144. package/docs/services/ses/README.md +1011 -0
  145. package/docs/services/sns/README.md +1904 -0
  146. package/docs/services/sqs/README.md +1185 -0
  147. package/docs/services/ssm/README.md +1072 -0
  148. package/docs/services/stepfunctions/README.md +1332 -0
  149. package/docs/services/sts/README.md +245 -0
  150. package/docs/services/wafv2/README.md +1378 -0
  151. package/docs/terraform/README.md +248 -0
  152. package/docs/time/README.md +284 -0
  153. package/llms.txt +59 -0
  154. package/package.json +9 -5
@@ -0,0 +1,501 @@
1
+ # Simulated Glue
2
+
3
+ Yulin includes a simulated Glue Data Catalog for tests and local development. It holds databases,
4
+ tables and the partitions registered against them, deploys databases and tables from
5
+ `AWS::Glue::Database` and `AWS::Glue::Table`, and hands them back through `GetDatabase` and
6
+ `GetTable`. A test can assert that a stack declared the table definition it meant to, including the
7
+ Athena partition projection its parameters configure.
8
+
9
+ A table here is a definition. The data it describes stays in S3, unread, and the catalog answers
10
+ with what it was told to hold.
11
+
12
+ Simulated [Athena](https://yulinsim.dev/services/athena/ "Simulated Athena usage docs") reads this
13
+ catalog. A query naming a table no database here holds fails the way real Athena fails it, and a
14
+ table's partition projection is evaluated when a query runs against it. All four projection types
15
+ are covered, `enum`, `integer`, `date` and `injected`. A projection with a mistake in its parameters
16
+ fails the query that reads it.
17
+
18
+ Glue-specific types are imported from the `@kensio/yulin/glue` subpath.
19
+
20
+ ## Deploying a database and a table
21
+
22
+ A stack declares the database, and the table names it through `Ref`. Both read back through the SDK
23
+ once the deploy finishes.
24
+
25
+ ```typescript sim-glue-cloudformation
26
+ /**
27
+ * Deploying a Glue database and a table, then reading the table back.
28
+ */
29
+
30
+ import { GetTableCommand } from "@aws-sdk/client-glue";
31
+
32
+ import { SimAws } from "@kensio/yulin";
33
+
34
+ const simAws = new SimAws();
35
+
36
+ const stack = await simAws.cloudFormation().deployTemplate({
37
+ stackName: "analytics-stack",
38
+ template: {
39
+ Resources: {
40
+ LogDatabase: {
41
+ Type: "AWS::Glue::Database",
42
+ Properties: {
43
+ CatalogId: { Ref: "AWS::AccountId" },
44
+ DatabaseInput: { Name: "site_logs" },
45
+ },
46
+ },
47
+ LogTable: {
48
+ Type: "AWS::Glue::Table",
49
+ Properties: {
50
+ CatalogId: { Ref: "AWS::AccountId" },
51
+ DatabaseName: { Ref: "LogDatabase" },
52
+ TableInput: {
53
+ Name: "access_logs",
54
+ TableType: "EXTERNAL_TABLE",
55
+ PartitionKeys: [{ Name: "year", Type: "string" }],
56
+ StorageDescriptor: {
57
+ Columns: [{ Name: "status", Type: "int" }],
58
+ Location: "s3://site-logs/cloudfront/",
59
+ },
60
+ Parameters: {
61
+ "projection.enabled": "true",
62
+ "projection.year.type": "date",
63
+ "projection.year.format": "yyyy",
64
+ "projection.year.range": "2026,NOW",
65
+ // eslint-disable-next-line no-template-curly-in-string
66
+ "storage.location.template": "s3://site-logs/cloudfront/${year}/",
67
+ },
68
+ },
69
+ },
70
+ },
71
+ },
72
+ },
73
+ });
74
+
75
+ await stack.waitForDeployComplete();
76
+
77
+ const { Table } = simAws
78
+ .glue()
79
+ .getTable(
80
+ new GetTableCommand({ DatabaseName: "site_logs", Name: "access_logs" }),
81
+ );
82
+
83
+ // true
84
+ console.log(Table.Parameters["projection.enabled"]);
85
+ ```
86
+
87
+ Partition projection lives entirely in `TableInput.Parameters`. Those parameters are read into the
88
+ table, and never recorded as ignored. A table whose parameters were dropped on the way in deploys
89
+ green while projecting none of them. Simulated Athena reads those same parameters when a query runs.
90
+ A broken projection fails that query.
91
+
92
+ `Ref` answers with the database name and with the table name.
93
+
94
+ ## Names are folded to lower case
95
+
96
+ A database name and a table name are both folded to lower case when they are stored. Real Glue folds
97
+ them the same way, for compatibility with Apache Hive. A database created as `Rainlytics` is stored,
98
+ reported and queried as `rainlytics`.
99
+
100
+ ```typescript sim-glue-name-folding
101
+ /**
102
+ * A catalog name folded on its way in.
103
+ */
104
+
105
+ import {
106
+ CreateDatabaseCommand,
107
+ GetDatabaseCommand,
108
+ } from "@aws-sdk/client-glue";
109
+
110
+ import { SimAws } from "@kensio/yulin";
111
+
112
+ const glue = new SimAws().glue();
113
+
114
+ glue.createDatabase(
115
+ new CreateDatabaseCommand({ DatabaseInput: { Name: "Rainlytics" } }),
116
+ );
117
+
118
+ const { Database } = glue.getDatabase(
119
+ new GetDatabaseCommand({ Name: "Rainlytics" }),
120
+ );
121
+
122
+ // rainlytics
123
+ console.log(Database.Name);
124
+ ```
125
+
126
+ Two names differing only by case are one name. A second `CreateDatabase` for `Rainlytics` after one
127
+ for `rainlytics` is an `AlreadyExistsException`, and a `GetTable` finds the table under whichever
128
+ spelling it is asked for.
129
+
130
+ Column names keep the case they were given, including partition keys. Real Glue leaves those alone,
131
+ and simulated [Athena](https://yulinsim.dev/services/athena/ "Simulated Athena usage docs") folds a
132
+ column name only when it runs a query.
133
+
134
+ ## Reading the catalog back
135
+
136
+ `GetDatabase`, `GetDatabases`, `GetTable` and `GetTables` answer through the SDK. A `SimGlue` also
137
+ carries `findDatabase`, `findTable`, `allDatabases`, `tablesInDatabase`, `findPartition` and
138
+ `partitionsInTable`. Those read the same state without going through a Command or its
139
+ authorization.
140
+
141
+ The storage descriptor keeps its columns in the order they were declared, and the partition keys
142
+ keep theirs. The two stay apart, the way real Glue keeps them. A partition key repeated among the
143
+ storage descriptor's columns gives a table Athena refuses to query.
144
+
145
+ ## Registering partitions
146
+
147
+ A crawler, `MSCK REPAIR TABLE` or `ALTER TABLE ADD PARTITION` fills a real catalog's partition list.
148
+ Here the six partition commands do it. A partition is keyed by its values in the order the table's
149
+ `PartitionKeys` declares them, and carries a storage descriptor saying where its own data sits.
150
+
151
+ ```typescript sim-glue-partitions
152
+ /**
153
+ * Registering two days of partitions against a table, then listing them.
154
+ */
155
+
156
+ import {
157
+ BatchCreatePartitionCommand,
158
+ CreateDatabaseCommand,
159
+ CreateTableCommand,
160
+ GetPartitionsCommand,
161
+ } from "@aws-sdk/client-glue";
162
+
163
+ import { SimAws } from "@kensio/yulin";
164
+
165
+ const simAws = new SimAws();
166
+ const glue = simAws.glue();
167
+
168
+ glue.createDatabase(
169
+ new CreateDatabaseCommand({ DatabaseInput: { Name: "site_logs" } }),
170
+ );
171
+ glue.createTable(
172
+ new CreateTableCommand({
173
+ DatabaseName: "site_logs",
174
+ TableInput: {
175
+ Name: "access_logs",
176
+ PartitionKeys: [{ Name: "day", Type: "string" }],
177
+ StorageDescriptor: { Location: "s3://site-logs/cloudfront/" },
178
+ },
179
+ }),
180
+ );
181
+
182
+ const { Errors } = glue.batchCreatePartition(
183
+ new BatchCreatePartitionCommand({
184
+ DatabaseName: "site_logs",
185
+ TableName: "access_logs",
186
+ PartitionInputList: [
187
+ {
188
+ Values: ["2026-08-25"],
189
+ StorageDescriptor: {
190
+ Location: "s3://site-logs/cloudfront/day=2026-08-25/",
191
+ },
192
+ },
193
+ {
194
+ Values: ["2026-08-26"],
195
+ StorageDescriptor: {
196
+ Location: "s3://site-logs/cloudfront/day=2026-08-26/",
197
+ },
198
+ },
199
+ ],
200
+ }),
201
+ );
202
+
203
+ const { Partitions } = glue.getPartitions(
204
+ new GetPartitionsCommand({
205
+ DatabaseName: "site_logs",
206
+ TableName: "access_logs",
207
+ }),
208
+ );
209
+
210
+ // 0
211
+ console.log(Errors.length);
212
+ // s3://site-logs/cloudfront/day=2026-08-26/
213
+ console.log(Partitions[1]?.StorageDescriptor?.Location);
214
+ ```
215
+
216
+ `CreatePartition` registers one partition and `BatchCreatePartition` registers a list of them.
217
+ `GetPartition` reads one back by its values, and `GetPartitions` answers with a table's partitions in
218
+ registration order. `DeletePartition` and `BatchDeletePartition` remove them.
219
+
220
+ A batch reports what it could not do in `Errors` and carries on with the rest, the way real Glue
221
+ does. Each entry there carries the values it was given and an `ErrorCode` naming the refusal, so a
222
+ job re-run over a week of days learns which days were already registered and registers the others.
223
+
224
+ Values are positional. A `Values` list of a different length from the table's `PartitionKeys` lines
225
+ up with the wrong keys, and is refused with `InvalidInputException` naming both counts. Registering
226
+ one day twice is an `AlreadyExistsException`, since registration is not idempotent on real Glue.
227
+ Deleting a table removes the partitions registered against it, the way deleting a database removes
228
+ its tables.
229
+
230
+ ## Filtering partitions
231
+
232
+ `GetPartitions` takes an `Expression` and answers with the partitions it matches. A request carrying
233
+ none reads them all.
234
+
235
+ ```typescript sim-glue-partition-expressions
236
+ /**
237
+ * Reading back the partitions an expression matches.
238
+ */
239
+
240
+ import {
241
+ BatchCreatePartitionCommand,
242
+ CreateDatabaseCommand,
243
+ CreateTableCommand,
244
+ GetPartitionsCommand,
245
+ } from "@aws-sdk/client-glue";
246
+
247
+ import { SimAws } from "@kensio/yulin";
248
+
249
+ const simAws = new SimAws();
250
+ const glue = simAws.glue();
251
+
252
+ glue.createDatabase(
253
+ new CreateDatabaseCommand({ DatabaseInput: { Name: "site_logs" } }),
254
+ );
255
+ glue.createTable(
256
+ new CreateTableCommand({
257
+ DatabaseName: "site_logs",
258
+ TableInput: {
259
+ Name: "access_logs",
260
+ PartitionKeys: [
261
+ { Name: "day", Type: "string" },
262
+ { Name: "region", Type: "string" },
263
+ ],
264
+ },
265
+ }),
266
+ );
267
+
268
+ glue.batchCreatePartition(
269
+ new BatchCreatePartitionCommand({
270
+ DatabaseName: "site_logs",
271
+ TableName: "access_logs",
272
+ PartitionInputList: [
273
+ { Values: ["2026-07-31", "eu-west-2"] },
274
+ { Values: ["2026-08-01", "eu-west-2"] },
275
+ { Values: ["2026-08-02", "us-east-1"] },
276
+ ],
277
+ }),
278
+ );
279
+
280
+ const { Partitions } = glue.getPartitions(
281
+ new GetPartitionsCommand({
282
+ DatabaseName: "site_logs",
283
+ TableName: "access_logs",
284
+ Expression: "day >= '2026-08-01' AND region IN ('eu-west-2', 'us-east-1')",
285
+ }),
286
+ );
287
+
288
+ // [["2026-08-01","eu-west-2"],["2026-08-02","us-east-1"]]
289
+ console.log(JSON.stringify(Partitions.map((partition) => partition.Values)));
290
+ ```
291
+
292
+ A term names a partition key, an operator, and whatever the operator takes. The operators are `=`,
293
+ `<>` and `!=`, `>`, `<`, `>=`, `<=`, `LIKE`, `IN` and `BETWEEN`, and `BETWEEN` takes both of its
294
+ ends. `AND`, `OR` and `NOT` combine terms and brackets group them, with the precedence SQL gives
295
+ them (`NOT` binds tightest, then `AND`, then `OR`). `NOT` also sits in front of `LIKE`, `IN` and
296
+ `BETWEEN` to reverse that one term.
297
+
298
+ Comparison follows the type the table declares for the key. `tinyint`, `smallint`, `int`, `integer`,
299
+ `bigint`, `float`, `double` and `decimal` compare as numbers, so `10` sorts above `9` rather than
300
+ below it. Every other declared type compares as text, which is the order a date written the ISO way
301
+ already has. A key declared as a number is held to a numeric literal, and `hour = 'noon'` is refused
302
+ rather than matching nothing.
303
+
304
+ `LIKE` matches the value's text whatever type the key is declared with, since that is what `LIKE`
305
+ does. `%` stands for any run of characters and `_` for exactly one.
306
+
307
+ An expression naming a column the table does not partition by is refused, naming the column and the
308
+ keys the table does have. So is one that cannot be read, and the message says where reading stopped.
309
+
310
+ ## Intercepting a GlueClient
311
+
312
+ An intercepted `GlueClient` reaches the simulated catalog, so code under test builds its own client
313
+ and sends its own Commands.
314
+
315
+ ```typescript sim-glue-sdk-interception
316
+ /**
317
+ * Ordinary Glue SDK code reaching the simulated catalog.
318
+ */
319
+
320
+ import {
321
+ CreateDatabaseCommand,
322
+ CreateTableCommand,
323
+ GetTablesCommand,
324
+ GlueClient,
325
+ } from "@aws-sdk/client-glue";
326
+
327
+ import { SimSdk } from "@kensio/yulin/sdk";
328
+
329
+ using simSdk = new SimSdk();
330
+ simSdk.intercept(GlueClient);
331
+
332
+ const client = new GlueClient({ region: "eu-west-2" });
333
+
334
+ await client.send(
335
+ new CreateDatabaseCommand({ DatabaseInput: { Name: "site_logs" } }),
336
+ );
337
+ await client.send(
338
+ new CreateTableCommand({
339
+ DatabaseName: "site_logs",
340
+ TableInput: { Name: "access_logs" },
341
+ }),
342
+ );
343
+
344
+ const { TableList } = await client.send(
345
+ new GetTablesCommand({ DatabaseName: "site_logs" }),
346
+ );
347
+
348
+ // access_logs
349
+ console.log(TableList?.[0]?.Name);
350
+ ```
351
+
352
+ ## Permissions
353
+
354
+ Every Command authorizes through simulated IAM. Data Catalog resources are a hierarchy with the
355
+ catalog at the root, and an operation on one needs permission on that resource and on every ancestor
356
+ of it. Reading a table needs the table, the database and the catalog, and a policy naming only the
357
+ table ARN is denied. Deleting a database needs permission on every table in it as well, since the
358
+ tables go with it.
359
+
360
+ ```typescript sim-glue-permissions
361
+ /**
362
+ * A Role that may read one table and nothing else in the catalog.
363
+ */
364
+
365
+ import {
366
+ CreateDatabaseCommand,
367
+ CreateTableCommand,
368
+ GetTableCommand,
369
+ } from "@aws-sdk/client-glue";
370
+ import { CreateRoleCommand, PutRolePolicyCommand } from "@aws-sdk/client-iam";
371
+
372
+ import { SimAws } from "@kensio/yulin";
373
+
374
+ const simAws = new SimAws({ defaultAccountId: "111111111111" });
375
+ const glue = simAws.glue();
376
+
377
+ glue.createDatabase(
378
+ new CreateDatabaseCommand({ DatabaseInput: { Name: "site_logs" } }),
379
+ );
380
+ glue.createTable(
381
+ new CreateTableCommand({
382
+ DatabaseName: "site_logs",
383
+ TableInput: { Name: "access_logs" },
384
+ }),
385
+ );
386
+
387
+ await simAws.iam().createRole(
388
+ new CreateRoleCommand({
389
+ RoleName: "ReportingRole",
390
+ AssumeRolePolicyDocument: JSON.stringify({
391
+ Version: "2012-10-17",
392
+ Statement: [
393
+ {
394
+ Effect: "Allow",
395
+ Principal: { Service: "lambda.amazonaws.com" },
396
+ Action: "sts:AssumeRole",
397
+ },
398
+ ],
399
+ }),
400
+ }),
401
+ );
402
+
403
+ await simAws.iam().putRolePolicy(
404
+ new PutRolePolicyCommand({
405
+ RoleName: "ReportingRole",
406
+ PolicyName: "ReadAccessLogs",
407
+ PolicyDocument: JSON.stringify({
408
+ Version: "2012-10-17",
409
+ Statement: [
410
+ {
411
+ Effect: "Allow",
412
+ Action: "glue:GetTable",
413
+ Resource: [
414
+ "arn:aws:glue:us-east-1:111111111111:catalog",
415
+ "arn:aws:glue:us-east-1:111111111111:database/site_logs",
416
+ "arn:aws:glue:us-east-1:111111111111:table/site_logs/access_logs",
417
+ ],
418
+ },
419
+ ],
420
+ }),
421
+ }),
422
+ );
423
+
424
+ const { Table } = glue.getTable(
425
+ new GetTableCommand({ DatabaseName: "site_logs", Name: "access_logs" }),
426
+ {
427
+ caller: {
428
+ kind: "arn",
429
+ arn: "arn:aws:iam::111111111111:role/ReportingRole",
430
+ },
431
+ },
432
+ );
433
+
434
+ // access_logs
435
+ console.log(Table.Name);
436
+ ```
437
+
438
+ A policy listing only the table ARN is refused here, and refused by real Glue for the same reason.
439
+
440
+ ## Available functionality
441
+
442
+ - `CreateDatabase`, `GetDatabase`, `GetDatabases` and `DeleteDatabase`.
443
+ - `CreateTable`, `GetTable`, `GetTables` and `DeleteTable`.
444
+ - `CreatePartition`, `BatchCreatePartition`, `GetPartition`, `GetPartitions`, `DeletePartition` and
445
+ `BatchDeletePartition`.
446
+ - `GetPartitions` `Expression` filtering, over the SQL operators, `AND`, `OR`, `NOT` and brackets.
447
+ - `AWS::Glue::Database` and `AWS::Glue::Table`, deployed and deleted with the stack.
448
+ - `TableInput.Parameters`, `PartitionKeys` and `StorageDescriptor`, held and read back as declared.
449
+ - IAM authorization on every Command, over the resource and its ancestors.
450
+ - Database and table names folded to lower case, as the Data Catalog folds them.
451
+
452
+ ## Limitations
453
+
454
+ - **Partition projection is held here and evaluated by Athena.** The catalog stores the parameters
455
+ as they were declared and materializes no partition from them. Simulated
456
+ [Athena](https://yulinsim.dev/services/athena/ "Simulated Athena usage docs") expands them when a
457
+ query runs, which is where a broken projection is refused.
458
+ - **The catalog reads no query and no object.** SQL reaches Athena rather than the catalog, no S3
459
+ prefix is listed here, and no object is opened.
460
+ - **`AWS::Glue::Crawler` is absent.** A crawler fills a catalog by reading objects, and every object
461
+ stays unread here. A template declaring one deploys, with the crawler recorded on the stack's
462
+ `skippedResources`.
463
+ - **A registered partition is read by Athena, and a projected one wins over it.** Simulated
464
+ [Athena](https://yulinsim.dev/services/athena/ "Simulated Athena usage docs") reads a table's
465
+ registered partitions when a query runs, unless `projection.enabled` is true. Real Athena stops
466
+ reading the catalog's partitions once projection is on.
467
+ - **A `GetPartitions` `Expression` is the whole of the filtering.** `Segment` and parallel listing,
468
+ `ExcludeColumnSchema` and `TransactionId` are absent, and so are partition indexes, which change
469
+ which expressions real Glue will serve rather than what any of them mean.
470
+ - **An expression compares a partition key against a literal.** That is the whole grammar.
471
+ Functions, arithmetic and comparing one column against another sit outside it, and a column name
472
+ is written unquoted.
473
+ - **A partition keeps what it was registered with.** `UpdatePartition` and `BatchUpdatePartition`
474
+ are absent, along with `BatchGetPartition` and partition indexes.
475
+ - **`AWS::Glue::Partition` is absent.** Real CloudFormation has the resource type, and a template
476
+ declaring one deploys here with the partition recorded on the stack's `skippedResources`. A stack
477
+ that needs its partitions registered does it through the SDK once the deploy finishes.
478
+ - **A table keeps the definition it was created with.** `UpdateTable` and `UpdateDatabase` are
479
+ absent, and `GetTable` reports the creation time as the update time.
480
+ - **`Fn::GetAtt Id` on a table answers with a guess.** It resolves to the catalog id, the database
481
+ name and the table name joined with `|`, as in `111111111111|site_logs|access_logs`. This is the
482
+ one piece of behaviour here that nothing has checked against AWS. CloudFormation documents that
483
+ the attribute exists and documents nothing about its value, so a template asserting on it agrees
484
+ with this simulation and may disagree with a real deploy. Confirming it takes one stack deployed
485
+ to an account with `!GetAtt Table.Id` as an output.
486
+ - **A lookup finds a name under any spelling.** Real Glue documents the fold on the way in and asks
487
+ the caller to pass lower case on the way back out. `GetDatabase` and `GetTable` fold the name they
488
+ are given here, so a request naming `Rainlytics` reaches the database stored as `rainlytics`. This
489
+ is the one piece of the fold nothing has checked against AWS. A test relying on it agrees with
490
+ this simulation and may disagree with a real call, and passing the name in lower case avoids the
491
+ question.
492
+ - **Cross-account catalogs are refused.** A `CatalogId` naming another account is refused, in a
493
+ Command and in a template.
494
+ - **Versions, statistics and Lake Formation are absent.** A table has no version history and no
495
+ column statistics, and IAM is the whole of the permission model.
496
+ - **Iceberg and other open table formats are recorded, never built.** `OpenTableFormatInput` is
497
+ reported as ignored and the table is created as an ordinary one.
498
+ - **Listings come back whole.** `GetDatabases`, `GetTables` and `GetPartitions` answer with
499
+ everything in creation order. `MaxResults` and `NextToken` are absent.
500
+ - **Connections, jobs, triggers, workflows and the Schema Registry are absent.** The Data Catalog is
501
+ the whole of the simulated surface.