@distilled.cloud/core 1.0.0-rc.8 → 1.0.0-rc.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/LICENSE +201 -0
  2. package/lib/codegen/cli.d.ts +8 -6
  3. package/lib/codegen/cli.d.ts.map +1 -1
  4. package/lib/codegen/cli.js +6 -62
  5. package/lib/codegen/cli.js.map +1 -1
  6. package/lib/codegen/graphql.d.ts +6 -0
  7. package/lib/codegen/graphql.d.ts.map +1 -1
  8. package/lib/codegen/graphql.js +14 -4
  9. package/lib/codegen/graphql.js.map +1 -1
  10. package/lib/codegen/openapi-cli.d.ts +17 -3
  11. package/lib/codegen/openapi-cli.d.ts.map +1 -1
  12. package/lib/codegen/openapi-cli.js +50 -48
  13. package/lib/codegen/openapi-cli.js.map +1 -1
  14. package/lib/codegen/openapi.d.ts +27 -1
  15. package/lib/codegen/openapi.d.ts.map +1 -1
  16. package/lib/codegen/openapi.js +81 -3
  17. package/lib/codegen/openapi.js.map +1 -1
  18. package/lib/codegen/patches.d.ts +65 -0
  19. package/lib/codegen/patches.d.ts.map +1 -0
  20. package/lib/codegen/patches.js +236 -0
  21. package/lib/codegen/patches.js.map +1 -0
  22. package/lib/codegen/patches.test.d.ts +2 -0
  23. package/lib/codegen/patches.test.d.ts.map +1 -0
  24. package/lib/codegen/patches.test.js +105 -0
  25. package/lib/codegen/patches.test.js.map +1 -0
  26. package/lib/codegen/proto.d.ts +121 -0
  27. package/lib/codegen/proto.d.ts.map +1 -0
  28. package/lib/codegen/proto.js +962 -0
  29. package/lib/codegen/proto.js.map +1 -0
  30. package/lib/codegen/rewrite-operation-ids.d.ts +131 -0
  31. package/lib/codegen/rewrite-operation-ids.d.ts.map +1 -0
  32. package/lib/codegen/rewrite-operation-ids.js +1079 -0
  33. package/lib/codegen/rewrite-operation-ids.js.map +1 -0
  34. package/lib/codegen/rewrite-operation-ids.test.d.ts +2 -0
  35. package/lib/codegen/rewrite-operation-ids.test.d.ts.map +1 -0
  36. package/lib/codegen/rewrite-operation-ids.test.js +533 -0
  37. package/lib/codegen/rewrite-operation-ids.test.js.map +1 -0
  38. package/lib/codegen/spec-path.d.ts +16 -0
  39. package/lib/codegen/spec-path.d.ts.map +1 -0
  40. package/lib/codegen/spec-path.js +101 -0
  41. package/lib/codegen/spec-path.js.map +1 -0
  42. package/lib/json-patch.d.ts +18 -11
  43. package/lib/json-patch.d.ts.map +1 -1
  44. package/lib/json-patch.js +63 -25
  45. package/lib/json-patch.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/codegen/cli.ts +14 -91
  48. package/src/codegen/graphql.ts +25 -3
  49. package/src/codegen/openapi-cli.ts +75 -59
  50. package/src/codegen/openapi.ts +118 -4
  51. package/src/codegen/patches.test.ts +130 -0
  52. package/src/codegen/patches.ts +291 -0
  53. package/src/codegen/proto.ts +1128 -0
  54. package/src/codegen/rewrite-operation-ids.test.ts +563 -0
  55. package/src/codegen/rewrite-operation-ids.ts +1206 -0
  56. package/src/codegen/spec-path.ts +115 -0
  57. package/src/json-patch.ts +82 -25
@@ -0,0 +1,563 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ isMechanicalOperationId,
4
+ isVerbatimRouteId,
5
+ pathToVerbNoun,
6
+ singularize,
7
+ toVerbNoun,
8
+ verbNounSmithyModel,
9
+ } from "./rewrite-operation-ids.ts";
10
+
11
+ const cases: ReadonlyArray<readonly [string, string]> = [
12
+ // go-swagger (Fly Machines)
13
+ ["Apps_list", "listApps"],
14
+ ["Apps_show", "getApp"],
15
+ ["Apps_create", "createApp"],
16
+ ["Apps_delete", "deleteApp"],
17
+ ["App_Certificates_show", "getAppCertificate"],
18
+ ["Machines_list_events", "listMachineEvents"],
19
+ ["Machines_delete_metadata", "deleteMachineMetadata"],
20
+ ["Postgres_users_create", "createPostgresUser"],
21
+ ["Volumes_list", "listVolumes"],
22
+ ["Platform_regions_get", "getPlatformRegion"],
23
+ ["Secretkeys_set", "setSecretKey"],
24
+ ["App_Certificates_acme_create", "createAppAcmeCertificate"],
25
+ ["App_Certificates_acme_delete", "deleteAppAcmeCertificate"],
26
+ ["App_Certificates_list", "listAppCertificates"],
27
+ // Azure autorest `Resource_action`
28
+ ["addsServices_list", "listAddsServices"],
29
+ ["addsServices_get", "getAddsService"],
30
+ ["ServiceMembers_getMetrics", "getServiceMemberMetrics"],
31
+ [
32
+ "ServiceMembers_listGlobalConfiguration",
33
+ "listServiceMemberGlobalConfiguration",
34
+ ],
35
+ ["VirtualMachines_start", "startVirtualMachine"],
36
+ ["OpenIdConnectProvider_get", "getOpenIdConnectProvider"],
37
+ ["Report_verify", "verifyReport"],
38
+ ["Deployments_whatIf", "deployments_whatIf"],
39
+ ["get_apps", "getApps"],
40
+ ["list_all_users", "listAllUsers"],
41
+ ["Get_Apps_list", "getAppsList"],
42
+ ["VirtualMachines_listAll", "listVirtualMachineAll"],
43
+ ["VirtualMachines_createOrUpdate", "virtualMachines_createOrUpdate"],
44
+ ["VirtualMachines_powerOff", "virtualMachines_powerOff"],
45
+ ["ip_address_aggregate_settings_update", "updateIpAddressAggregateSettings"],
46
+ // REST trailing verb
47
+ ["ConfigsList", "listConfigs"],
48
+ ["ContainerCreate", "createContainer"],
49
+ ["PlansGet", "getPlan"],
50
+ ["IssueGet", "getIssue"],
51
+ ["EnvironmentVersionsGet", "getEnvironmentVersion"],
52
+ ["MonitorsList", "listMonitors"],
53
+ ["SitesAppConfigurationList", "listSitesAppConfiguration"],
54
+ ["ExperimentHoldoutsPartialUpdate", "updateExperimentHoldoutPartial"],
55
+ ["SchemaPropertyGroupsList", "listSchemaPropertyGroups"],
56
+ ["QueueList", "listQueue"],
57
+ ["ImageListTags", "imageListTags"],
58
+ // GraphQL nounVerb
59
+ ["projectCreate", "createProject"],
60
+ // Swagger tag-prefixed (Forgejo/Gitea)
61
+ ["orgDelete", "deleteOrg"],
62
+ ["orgGetAll", "orgGetAll"],
63
+ ["issueListIssues", "listIssues"],
64
+ ["issueCreateIssue", "createIssue"],
65
+ ["repoGetRepo", "getRepo"],
66
+ ["issueGetRepoComments", "issueGetRepoComments"],
67
+ ["userGetCurrent", "userGetCurrent"],
68
+ ["environmentPatchCommit", "environmentPatchCommit"],
69
+ ["adminCreateOrg", "adminCreateOrg"],
70
+ ["activitypubInstanceActor", "activitypubInstanceActor"],
71
+ ["serviceDelete", "deleteService"],
72
+ ["projectTokenCreate", "createProjectToken"],
73
+ ["volumeInstanceUpdate", "updateVolumeInstance"],
74
+ ["accountById", "accountById"],
75
+ ["tcpProxies", "tcpProxies"],
76
+ // already verb-first: untouched apart from aliasing
77
+ ["GetObject", "getObject"],
78
+ ["listSprites", "listSprites"],
79
+ ["showContact", "getContact"],
80
+ ["index", "list"],
81
+ ["RetrieveAccount", "getAccount"],
82
+ ["WatchCoreV1PersistentVolumeList", "watchCoreV1PersistentVolumeList"],
83
+ [
84
+ "ReadAppsV1NamespacedReplicaSetStatus",
85
+ "readAppsV1NamespacedReplicaSetStatus",
86
+ ],
87
+ [
88
+ "ReplaceAppsV1NamespacedStatefulSetScale",
89
+ "replaceAppsV1NamespacedStatefulSetScale",
90
+ ],
91
+ [
92
+ "ConnectCoreV1DeleteNamespacedPodProxyWithPath",
93
+ "connectCoreV1DeleteNamespacedPodProxyWithPath",
94
+ ],
95
+ ["DeleteStorageV1CSIDriver", "deleteStorageV1CSIDriver"],
96
+ ["GetSchedulingAPIGroup", "getSchedulingAPIGroup"],
97
+ ["BulkDeleteMessages", "bulkDeleteMessages"],
98
+ ["BulkUpdateGuildRoles", "bulkUpdateGuildRoles"],
99
+ ["AddStatusCheckContexts", "addStatusCheckContexts"],
100
+ ["UploadReleaseAsset", "uploadReleaseAsset"],
101
+ ["PostRadarValueListItems", "postRadarValueListItems"],
102
+ [
103
+ "PostSetupIntentsIntentVerifyMicrodeposits",
104
+ "postSetupIntentsIntentVerifyMicrodeposits",
105
+ ],
106
+ ["InsertCalendarList", "insertCalendarList"],
107
+ ["FetchThreatListUpdates", "fetchThreatListUpdates"],
108
+ ["StreamGenerateContent", "streamGenerateContent"],
109
+ ["QueryCreate", "createQuery"],
110
+ ["ReleaseGet", "getRelease"],
111
+ ["RequestGet", "getRequest"],
112
+ ["SetGet", "getSet"],
113
+ ["CheckGet", "getCheck"],
114
+ ["CheckRunsList", "listCheckRuns"],
115
+ ["QueryRun", "queryRun"],
116
+ ["ReportVerify", "verifyReport"],
117
+ ["ReportList", "listReport"],
118
+ // compounds and non-verb ids: untouched
119
+ ["AppGetOrCreate", "appGetOrCreate"],
120
+ ["AddOrUpdateMembershipForUserInOrg", "addOrUpdateMembershipForUserInOrg"],
121
+ ["DnsRecordsBatch", "dnsRecordsBatch"],
122
+ ["DnsTimeseriesGroupsResponseCode", "dnsTimeseriesGroupsResponseCode"],
123
+ ["Root", "root"],
124
+ ["V1GetProjectFunctionCombinedStats", "v1GetProjectFunctionCombinedStats"],
125
+ ["PullRequestStacksAdd", "addPullRequestStack"],
126
+ ["TeamsControllerCreateTeam", "teamsControllerCreateTeam"],
127
+ // singularisation
128
+ ["AddressesGet", "getAddress"],
129
+ ["AliasesList", "listAliases"],
130
+ ["AliasGet", "getAlias"],
131
+ ["AnalyticsGet", "getAnalytics"],
132
+ ["TimeseriesGet", "getTimeseries"],
133
+ ["SeriesGet", "getSeries"],
134
+ ["SettingsUpdate", "updateSettings"],
135
+ ["CredentialsGet", "getCredentials"],
136
+ ["MetricsGet", "getMetrics"],
137
+ ["StatusesGet", "getStatus"],
138
+ ["CanvasGet", "getCanvas"],
139
+ ["LensGet", "getLens"],
140
+ ["CategoriesGet", "getCategory"],
141
+ ["DatabasesGet", "getDatabase"],
142
+ ["ResponsesGet", "getResponse"],
143
+ ["IndexesList", "listIndexes"],
144
+ // `index` is the Rails verb only on its own or as a whole `_` segment;
145
+ // joined into a camel id it names the index entity.
146
+ ["Apps_index", "listApps"],
147
+ ["InfoIndex", "infoIndex"],
148
+ ["QueryIndex", "queryIndex"],
149
+ ["IndexCreate", "createIndex"],
150
+ ["IndexDocument", "indexDocument"],
151
+ ["GetIndexInfo", "getIndexInfo"],
152
+ ["MetadataIndexList", "listMetadataIndex"],
153
+ ["ProxiesList", "listProxies"],
154
+ ["ProcessesGet", "getProcess"],
155
+ ["Ec2AddressesGet", "getEc2Address"],
156
+ ["IPsList", "listIPs"],
157
+ ["IPsGet", "getIP"],
158
+ ];
159
+
160
+ describe("toVerbNoun", () => {
161
+ for (const [input, expected] of cases) {
162
+ test(`${input} → ${expected}`, () => {
163
+ expect(toVerbNoun(input)).toBe(expected);
164
+ });
165
+ }
166
+ });
167
+
168
+ describe("singularize", () => {
169
+ test.each([
170
+ ["Apps", "App"],
171
+ ["Machines", "Machine"],
172
+ ["Addresses", "Address"],
173
+ ["Aliases", "Alias"],
174
+ ["Analytics", "Analytics"],
175
+ ["Series", "Series"],
176
+ ["Status", "Status"],
177
+ ["Statuses", "Status"],
178
+ ["Canvas", "Canvas"],
179
+ ["Categories", "Category"],
180
+ ["Boxes", "Box"],
181
+ ["Branches", "Branch"],
182
+ ["Releases", "Release"],
183
+ ["Databases", "Database"],
184
+ ["Postgres", "Postgres"],
185
+ ["Kubernetes", "Kubernetes"],
186
+ ["DNS", "DNS"],
187
+ ["IPs", "IP"],
188
+ ["Data", "Data"],
189
+ ["Ips", "Ips"],
190
+ ["Keys", "Key"],
191
+ ["Days", "Day"],
192
+ ["Toys", "Toy"],
193
+ ])("%s → %s", (input, expected) => {
194
+ expect(singularize(input)).toBe(expected);
195
+ });
196
+ });
197
+
198
+ describe("verbNounSmithyModel", () => {
199
+ test("renames operations, companions, and every reference", () => {
200
+ const model = {
201
+ shapes: {
202
+ "ns#Svc": {
203
+ type: "service",
204
+ operations: [{ target: "ns#AppsList" }, { target: "ns#AppsGet" }],
205
+ },
206
+ "ns#AppsList": {
207
+ type: "operation",
208
+ input: { target: "ns#AppsListRequest" },
209
+ output: { target: "ns#AppsListResponse" },
210
+ },
211
+ "ns#AppsListRequest": { type: "structure", members: {} },
212
+ "ns#AppsListResponse": {
213
+ type: "structure",
214
+ members: { items: { target: "ns#AppsListResponseItemsList" } },
215
+ },
216
+ "ns#AppsListResponseItemsList": {
217
+ type: "list",
218
+ member: { target: "smithy.api#String" },
219
+ },
220
+ "ns#AppsGet": {
221
+ type: "operation",
222
+ input: { target: "ns#AppsGetRequest" },
223
+ output: { target: "smithy.api#Unit" },
224
+ },
225
+ "ns#AppsGetRequest": { type: "structure", members: {} },
226
+ },
227
+ };
228
+ const r = verbNounSmithyModel(model);
229
+ expect(r.renamed).toBe(2);
230
+ expect(r.collisions).toEqual([]);
231
+ expect(Object.keys(model.shapes).sort()).toEqual(
232
+ [
233
+ "ns#Svc",
234
+ "ns#ListApps",
235
+ "ns#ListAppsRequest",
236
+ "ns#ListAppsResponse",
237
+ "ns#ListAppsResponseItemsList",
238
+ "ns#GetApp",
239
+ "ns#GetAppRequest",
240
+ ].sort(),
241
+ );
242
+ const shapes = model.shapes as Record<string, any>;
243
+ expect(shapes["ns#Svc"].operations).toEqual([
244
+ { target: "ns#ListApps" },
245
+ { target: "ns#GetApp" },
246
+ ]);
247
+ expect(shapes["ns#ListApps"].output.target).toBe("ns#ListAppsResponse");
248
+ expect(shapes["ns#ListAppsResponse"].members.items.target).toBe(
249
+ "ns#ListAppsResponseItemsList",
250
+ );
251
+ });
252
+
253
+ test("is idempotent", () => {
254
+ const model = {
255
+ shapes: {
256
+ "ns#AppsList": {
257
+ type: "operation",
258
+ input: { target: "ns#AppsListRequest" },
259
+ output: { target: "smithy.api#Unit" },
260
+ },
261
+ "ns#AppsListRequest": { type: "structure", members: {} },
262
+ },
263
+ };
264
+ verbNounSmithyModel(model);
265
+ const once = JSON.stringify(model);
266
+ const second = verbNounSmithyModel(model);
267
+ expect(second.renamed).toBe(0);
268
+ expect(JSON.stringify(model)).toBe(once);
269
+ });
270
+
271
+ test("reports collisions and keeps the original", () => {
272
+ const model = {
273
+ shapes: {
274
+ "ns#ListApps": { type: "operation" },
275
+ "ns#AppsList": { type: "operation" },
276
+ },
277
+ };
278
+ const r = verbNounSmithyModel(model);
279
+ expect(r.renamed).toBe(0);
280
+ expect(r.collisions).toEqual(["AppsList → ListApps"]);
281
+ expect(Object.keys(model.shapes).sort()).toEqual([
282
+ "ns#AppsList",
283
+ "ns#ListApps",
284
+ ]);
285
+ });
286
+ });
287
+
288
+ describe("isMechanicalOperationId", () => {
289
+ test.each([
290
+ [undefined, "get", "/users", true],
291
+ ["get-api-card", "get", "/api/card", true],
292
+ ["post_v1_users_id", "post", "/v1/users/{id}", true],
293
+ ["getApiV1AnnotationLayer", "get", "/api/v1/annotation_layer", true],
294
+ ["get-database-by-uuid", "get", "/databases/{uuid}", true],
295
+ ["getEvents", "get", "/events", true],
296
+ ["getMauUsage", "get", "/api/v2/usage/mau", false],
297
+ ["getExperimentSnapshot", "get", "/v1/snapshots/{id}", false],
298
+ [
299
+ "deleteBranches",
300
+ "post",
301
+ "/api/v2/code-refs/repositories/{repo}/branch-delete-tasks",
302
+ false,
303
+ ],
304
+ ["post-applePay-sessions", "post", "/applePay/sessions", true],
305
+ ["Apps_list", "get", "/apps", false],
306
+ ["listUsers", "get", "/users", false],
307
+ [
308
+ "delete-artifact",
309
+ "delete",
310
+ "/repos/{owner}/{repo}/actions/artifacts/{artifact_id}",
311
+ false,
312
+ ],
313
+ ["get_pricing", "get", "/pricing", true],
314
+ ["get_actions", "get", "/actions", true],
315
+ ["deletePolicy", "delete", "/v2/policies/{policyId}", true],
316
+ ["getCustomerById", "get", "/v2/customers/{customerId}", true],
317
+ [
318
+ "getBalanceByAsset",
319
+ "get",
320
+ "/accounts/{account_id}/balances/{asset}",
321
+ false,
322
+ ],
323
+ ])("%s %s %s → %s", (id, method, path, expected) => {
324
+ expect(isMechanicalOperationId(id, { method, path })).toBe(expected);
325
+ });
326
+ });
327
+
328
+ describe("pathToVerbNoun with a mechanical id", () => {
329
+ test.each([
330
+ ["get", "/feeds", "activity/get-feeds", false, "getFeeds"],
331
+ ["get", "/actions", "get_actions", undefined, "getActions"],
332
+ ["get", "/pricing", "get_pricing", false, "getPricing"],
333
+ [
334
+ "get",
335
+ "/load_balancers/{id}/metrics",
336
+ "get_load_balancer_metrics",
337
+ false,
338
+ "getLoadBalancerMetrics",
339
+ ],
340
+ [
341
+ "delete",
342
+ "/v2/policies/{policyId}",
343
+ "deletePolicy",
344
+ undefined,
345
+ "deletePolicy",
346
+ ],
347
+ [
348
+ "get",
349
+ "/v2/customers/{customerId}",
350
+ "getCustomerById",
351
+ undefined,
352
+ "getCustomer",
353
+ ],
354
+ ["post", "/api/action", "post-api-action", undefined, "createAction"],
355
+ [
356
+ "get",
357
+ "/orgs/{org}/rulesets/{ruleset_id}",
358
+ "repos/get-org-ruleset",
359
+ undefined,
360
+ "getOrgRuleset",
361
+ ],
362
+ [
363
+ "get",
364
+ "/orgs/{org}/rulesets",
365
+ "repos/get-org-rulesets",
366
+ true,
367
+ "listOrgRulesets",
368
+ ],
369
+ [
370
+ "delete",
371
+ "/api/jobs/{namespace}/scheduled/{id}",
372
+ "deleteScheduledJob",
373
+ undefined,
374
+ "deleteScheduledJob",
375
+ ],
376
+ [
377
+ "get",
378
+ "/v2/payments/{session_id}/authorizations/{authorization_id}",
379
+ "getPaymentSessionAuthorization",
380
+ undefined,
381
+ "getPaymentSessionAuthorization",
382
+ ],
383
+ ])("%s %s (%s) → %s", (method, path, id, coll, expected) => {
384
+ expect(
385
+ pathToVerbNoun({ method, path }, { returnsCollection: coll, nouns: id }),
386
+ ).toBe(expected);
387
+ });
388
+ });
389
+
390
+ describe("pathToVerbNoun with a verbatim id", () => {
391
+ test.each([
392
+ [
393
+ "post",
394
+ "/v1/apps/{appId}/promote",
395
+ "postV1AppsByAppIdPromote",
396
+ "createAppPromote",
397
+ ],
398
+ ["get", "/v1/apps", "getV1Apps", "getApps"],
399
+ ["get", "/v1/apps/{appId}", "getV1AppsByAppId", "getApp"],
400
+ ["post", "/v1/accounts/{account}", "PostAccountsAccount", "updateAccount"],
401
+ ["post", "/v1/accounts", "PostAccounts", "createAccount"],
402
+ ["get", "/v1/accounts/{account}", "GetAccountsAccount", "getAccount"],
403
+ [
404
+ "delete",
405
+ "/v1/accounts/{account}",
406
+ "DeleteAccountsAccount",
407
+ "deleteAccount",
408
+ ],
409
+ [
410
+ "delete",
411
+ "/v1/customers/{customer}/discount",
412
+ "DeleteCustomersCustomerDiscount",
413
+ "deleteCustomerDiscount",
414
+ ],
415
+ [
416
+ "get",
417
+ "/v1/customers/{customer}/balance_transactions/{transaction}",
418
+ "GetCustomersCustomerBalanceTransactionsTransaction",
419
+ "getCustomerBalanceTransaction",
420
+ ],
421
+ [
422
+ "delete",
423
+ "/organizations/{organization}/teams/{team}",
424
+ "delete_organization_team",
425
+ "deleteOrganizationTeam",
426
+ ],
427
+ [
428
+ "get",
429
+ "/organizations/{organization}/teams/{team}/members/{id}",
430
+ "get_organization_team_member",
431
+ "getOrganizationTeamMember",
432
+ ],
433
+ [
434
+ "delete",
435
+ "/projects/{project_id}/branches/{branch_id}/custom-domains/{domain}",
436
+ "deleteProjectBranchCustomDomain",
437
+ "deleteProjectBranchCustomDomain",
438
+ ],
439
+ [
440
+ "delete",
441
+ "/v1/buckets/{bucketId}/keys/{keyId}",
442
+ "deleteV1BucketsByBucketIdKeysByKeyId",
443
+ "deleteBucketKey",
444
+ ],
445
+ ["get", "/v1/users/{id}", "get_v1_users_id", "getUser"],
446
+ [
447
+ "delete",
448
+ "/projects/{project_id}/jwks/{jwks_id}",
449
+ "deleteProjectJWKS",
450
+ "deleteProjectJWKS",
451
+ ],
452
+ [
453
+ "get",
454
+ "/projects/{project_id}/branches/{branch_id}/data-api/{database_name}",
455
+ "getProjectBranchDataAPI",
456
+ "getProjectBranchDataAPI",
457
+ ],
458
+ ["get", "/v2/customers/{customerId}", "getCustomerById", "getCustomer"],
459
+ [
460
+ "get",
461
+ "/{teamSlug}/{projectSlug}/{repositoryName}/blobs/{digest}",
462
+ "getByTeamSlugByProjectSlugByRepositoryNameBlobsByDigest",
463
+ "getBlob",
464
+ ],
465
+ [
466
+ "delete",
467
+ "/v1/security/firewall/config/{configVersion}",
468
+ "deleteSecurityFirewallConfigByConfigVersion",
469
+ "deleteSecurityFirewallConfig",
470
+ ],
471
+ ])("%s %s (%s) → %s", (method, path, id, expected) => {
472
+ expect(
473
+ pathToVerbNoun({ method, path }, { nouns: id, verbatim: true }),
474
+ ).toBe(expected);
475
+ });
476
+ });
477
+
478
+ describe("pathToVerbNoun", () => {
479
+ test.each([
480
+ ["get", "/users", "listUsers"],
481
+ ["get", "/users/{id}", "getUser"],
482
+ ["post", "/users", "createUser"],
483
+ ["put", "/users/{id}", "putUser"],
484
+ ["post", "/users/{id}", "updateUser"],
485
+ ["patch", "/users/{id}", "updateUser"],
486
+ ["delete", "/users/{id}", "deleteUser"],
487
+ ["post", "/users/{id}/reset", "resetUser"],
488
+ ["post", "/emails/{email_id}/cancel", "cancelEmail"],
489
+ ["post", "/api/papers/index", "createPaperIndex"],
490
+ ["get", "/orgs/{org}/repos", "listOrgRepos"],
491
+ ["get", "/api/v1/annotation_layer", "listAnnotationLayer"],
492
+ ["get", "/api/card", "listCard"],
493
+ [
494
+ "get",
495
+ "/api/public/dashboard/{uuid}/dashcard/{dashcard-id}/card/{card-id}",
496
+ "getDashboardDashcardCard",
497
+ ],
498
+ ["post", "/applePay/sessions", "createApplePaySession"],
499
+ ["post", "/cancels", "createCancel"],
500
+ [
501
+ "get",
502
+ "/v1/benefit-offers/country-summaries",
503
+ "listBenefitOfferCountrySummaries",
504
+ ],
505
+ ["get", "/v1/contractor-invoices/{id}", "getContractorInvoice"],
506
+ ["post", "/auth/oauth2/token", "createAuthOauth2Token"],
507
+ ["post", "/login", "login"],
508
+ ["get", "/", "getRoot"],
509
+ ["get", "/api/v2/usage/mau", "listUsageMau"],
510
+ ["post", "/api/ee/action-v2/execute-bulk", "executeEeActionV2Bulk"],
511
+ ["get", "/api/collections/{namespace}/{slug}-{id}", "getCollection"],
512
+ ["get", "/zen", "getZen"],
513
+ ["get", "/feeds", "getFeeds"],
514
+ ["get", "/guilds/{guild_id}/webhooks", "getGuildWebhooks"],
515
+ ["delete", "/api/notifications", "deleteNotifications"],
516
+ ])("%s %s → %s", (method, path, expected) => {
517
+ expect(
518
+ pathToVerbNoun(
519
+ { method, path },
520
+ { returnsCollection: expected.startsWith("list") },
521
+ ),
522
+ ).toBe(expected);
523
+ });
524
+ });
525
+
526
+ describe("isVerbatimRouteId", () => {
527
+ test.each([
528
+ [undefined, "get", "/apps", true],
529
+ ["postV1AppsByAppIdPromote", "post", "/v1/apps/{appId}/promote", true],
530
+ ["getV1Apps", "get", "/v1/apps", true],
531
+ ["get_v1_users_id", "get", "/v1/users/{id}", true],
532
+ ["get-api-card", "get", "/api/card", true],
533
+ [
534
+ "getByTeamSlugByProjectSlugByRepositoryNameBlobsByDigest",
535
+ "get",
536
+ "/{teamSlug}/{projectSlug}/{repositoryName}/blobs/{digest}",
537
+ true,
538
+ ],
539
+ ["activity/get-feeds", "get", "/feeds", true],
540
+ ["get_actions", "get", "/actions", true],
541
+ [
542
+ "deleteScheduledJob",
543
+ "delete",
544
+ "/api/jobs/{namespace}/scheduled/{id}",
545
+ false,
546
+ ],
547
+ ["getCustomerById", "get", "/v2/customers/{customerId}", true],
548
+ [
549
+ "get_webhook_by_token",
550
+ "get",
551
+ "/webhooks/{webhook_id}/{webhook_token}",
552
+ false,
553
+ ],
554
+ [
555
+ "getFirewallConfig",
556
+ "get",
557
+ "/v1/security/firewall/config/{configVersion}",
558
+ false,
559
+ ],
560
+ ])("%s %s %s → %s", (id, method, path, expected) => {
561
+ expect(isVerbatimRouteId(id, { method, path })).toBe(expected);
562
+ });
563
+ });