@checkdigit/eslint-athena-plugin 1.0.0-PR.2-dcdf

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 (66) hide show
  1. package/LICENSE.txt +21 -0
  2. package/README.md +17 -0
  3. package/SECURITY.md +13 -0
  4. package/dist-mjs/athena/api-locator.mjs +66 -0
  5. package/dist-mjs/athena/api-matcher.mjs +206 -0
  6. package/dist-mjs/athena/athena.mjs +165 -0
  7. package/dist-mjs/athena/column.mjs +1 -0
  8. package/dist-mjs/athena/context.mjs +21 -0
  9. package/dist-mjs/athena/index.mjs +1 -0
  10. package/dist-mjs/athena/service-table.mjs +45 -0
  11. package/dist-mjs/athena/sql-file.mjs +123 -0
  12. package/dist-mjs/athena/types.mjs +1 -0
  13. package/dist-mjs/athena/validate.mjs +619 -0
  14. package/dist-mjs/athena/visitor.mjs +291 -0
  15. package/dist-mjs/get-documentation-url.mjs +9 -0
  16. package/dist-mjs/index.mjs +56 -0
  17. package/dist-mjs/openapi/deref-schema.mjs +20 -0
  18. package/dist-mjs/openapi/generate-schema.mjs +375 -0
  19. package/dist-mjs/openapi/service-schema-generator.mjs +176 -0
  20. package/dist-mjs/peggy/athena-peggy.mjs +20700 -0
  21. package/dist-mjs/service.mjs +9 -0
  22. package/dist-mjs/sql-parser.mjs +28 -0
  23. package/dist-types/athena/api-locator.d.ts +2 -0
  24. package/dist-types/athena/api-matcher.d.ts +14 -0
  25. package/dist-types/athena/athena.d.ts +5 -0
  26. package/dist-types/athena/column.d.ts +1 -0
  27. package/dist-types/athena/context.d.ts +21 -0
  28. package/dist-types/athena/index.d.ts +8 -0
  29. package/dist-types/athena/service-table.d.ts +8 -0
  30. package/dist-types/athena/sql-file.d.ts +5 -0
  31. package/dist-types/athena/types.d.ts +493 -0
  32. package/dist-types/athena/validate.d.ts +14 -0
  33. package/dist-types/athena/visitor.d.ts +75 -0
  34. package/dist-types/get-documentation-url.d.ts +1 -0
  35. package/dist-types/index.d.ts +5 -0
  36. package/dist-types/openapi/deref-schema.d.ts +1 -0
  37. package/dist-types/openapi/generate-schema.d.ts +33 -0
  38. package/dist-types/openapi/service-schema-generator.d.ts +5 -0
  39. package/dist-types/peggy/athena-peggy.d.ts +13 -0
  40. package/dist-types/service.d.ts +2 -0
  41. package/dist-types/sql-parser.d.ts +25 -0
  42. package/package.json +1 -0
  43. package/src/api/v1/swagger.yml +619 -0
  44. package/src/api/v2/swagger.yml +477 -0
  45. package/src/athena/api-locator.ts +78 -0
  46. package/src/athena/api-matcher.ts +323 -0
  47. package/src/athena/athena.ts +224 -0
  48. package/src/athena/column.ts +4 -0
  49. package/src/athena/context.ts +47 -0
  50. package/src/athena/index.ts +13 -0
  51. package/src/athena/service-table.ts +78 -0
  52. package/src/athena/sql-file.ts +161 -0
  53. package/src/athena/types.ts +568 -0
  54. package/src/athena/validate.ts +902 -0
  55. package/src/athena/visitor.ts +406 -0
  56. package/src/get-documentation-url.ts +7 -0
  57. package/src/index.ts +67 -0
  58. package/src/openapi/deref-schema.ts +20 -0
  59. package/src/openapi/generate-schema.ts +553 -0
  60. package/src/openapi/service-schema-generator.ts +241 -0
  61. package/src/peggy/athena-peggy.ts +22149 -0
  62. package/src/peggy/athena.peggy +2971 -0
  63. package/src/service.ts +11 -0
  64. package/src/services/eslintAthenaPlugin/v1/swagger.schema.deref.json +1931 -0
  65. package/src/services/eslintAthenaPlugin/v2/swagger.schema.deref.json +978 -0
  66. package/src/sql-parser.ts +53 -0
@@ -0,0 +1,477 @@
1
+ openapi: 3.0.0
2
+ info:
3
+ title: Eslint Plugin Service
4
+ description: |
5
+ A service that accepts incoming pings. This is only used for testing.
6
+
7
+ © Check Digit LLC. 2019-2026
8
+ version: 1.0.0
9
+ contact:
10
+ name: Check Digit
11
+ servers:
12
+ - url: /eslint-athena-plugin/v2
13
+ tags:
14
+ - name: Service Health
15
+ - name: API
16
+
17
+ x-firehose-logged: false
18
+
19
+ paths:
20
+ /ping:
21
+ get:
22
+ tags:
23
+ - Service Health
24
+ operationId: 'ping-get'
25
+ description: Tests the availability of the service and returns the current server time.
26
+ x-firehose-logged: true
27
+ responses:
28
+ '200':
29
+ $ref: '#/components/responses/Ping'
30
+ default:
31
+ $ref: '#/components/responses/ServerError'
32
+
33
+ /tenant/{tenantId}/entry/{entryId}:
34
+ put:
35
+ tags:
36
+ - API
37
+ operationId: 'tenant-entry-put'
38
+ x-firehose-logged: true
39
+ description: >-
40
+ Creates a journal entry with a specific id. This operation is
41
+ idempotent.
42
+ parameters:
43
+ - $ref: '#/components/parameters/tenantId'
44
+ - $ref: '#/components/parameters/entryId'
45
+ - $ref: '#/components/parameters/ifMatch'
46
+ - $ref: '#/components/parameters/createdOn'
47
+ requestBody:
48
+ required: true
49
+ content:
50
+ application/json:
51
+ schema:
52
+ $ref: '#/components/schemas/NewEntry'
53
+ responses:
54
+ '204':
55
+ $ref: '#/components/responses/Created'
56
+ '409':
57
+ $ref: '#/components/responses/Conflict'
58
+ '412':
59
+ $ref: '#/components/responses/PreConditionFailed'
60
+ default:
61
+ $ref: '#/components/responses/ServerError'
62
+ get:
63
+ tags:
64
+ - API
65
+ operationId: 'tenant-entry-get'
66
+ description: Obtains a journal entry with a specified id.
67
+ parameters:
68
+ - $ref: '#/components/parameters/tenantId'
69
+ - $ref: '#/components/parameters/entryId'
70
+ - $ref: '#/components/parameters/at'
71
+ responses:
72
+ '200':
73
+ description: Entry obtained successfully.
74
+ content:
75
+ application/json:
76
+ schema:
77
+ $ref: '#/components/schemas/ResponseEntry'
78
+ headers:
79
+ Created-On:
80
+ $ref: '#/components/headers/Created-On'
81
+ Updated-On:
82
+ $ref: '#/components/headers/Updated-On'
83
+ '404':
84
+ $ref: '#/components/responses/NotFound'
85
+ default:
86
+ $ref: '#/components/responses/ServerError'
87
+
88
+ /tenant/{tenantId}/card/{cardId}:
89
+ get:
90
+ tags:
91
+ - API
92
+ operationId: 'card-get'
93
+ description: Retrieves a card by id.
94
+ parameters:
95
+ - $ref: '#/components/parameters/tenantId'
96
+ - $ref: '#/components/parameters/cardId'
97
+ - $ref: '#/components/parameters/at'
98
+ - $ref: '#/components/parameters/publicKeyHashQuery'
99
+ responses:
100
+ '200':
101
+ $ref: '#/components/responses/CardResponse'
102
+ '404':
103
+ $ref: '#/components/responses/NotFound'
104
+ default:
105
+ $ref: '#/components/responses/ServerError'
106
+ put:
107
+ x-firehose-logged: true
108
+ tags:
109
+ - API
110
+ operationId: 'card-put'
111
+ description: Creates a new card.
112
+ parameters:
113
+ - $ref: '#/components/parameters/tenantId'
114
+ - $ref: '#/components/parameters/cardId'
115
+ - $ref: '#/components/parameters/createdOn'
116
+ requestBody:
117
+ $ref: '#/components/requestBodies/NewCardRequest'
118
+ responses:
119
+ '200':
120
+ $ref: '#/components/responses/CardResponse'
121
+ '409':
122
+ $ref: '#/components/responses/Conflict'
123
+ default:
124
+ $ref: '#/components/responses/ServerError'
125
+
126
+ components:
127
+ schemas:
128
+ Ping:
129
+ type: object
130
+ additionalProperties: false
131
+ required:
132
+ - serverTime
133
+ - v2Only
134
+ properties:
135
+ serverTime:
136
+ type: string
137
+ format: date-time
138
+ description: Current server time.
139
+ v2Only:
140
+ type: boolean
141
+ description: Indicates if the server is running in v2-only mode.
142
+
143
+ Error:
144
+ type: object
145
+ additionalProperties: false
146
+ description: Error message.
147
+ required:
148
+ - message
149
+ - code
150
+ properties:
151
+ message:
152
+ type: string
153
+ code:
154
+ type: string
155
+ enum:
156
+ - INVALID AT
157
+ - INVALID FROM
158
+
159
+ Currency:
160
+ type: string
161
+ enum:
162
+ - AED
163
+ - AFN
164
+
165
+ ResponseEntry:
166
+ type: object
167
+ additionalProperties: false
168
+ required:
169
+ - entryId
170
+ - createdOn
171
+ - postings
172
+ properties:
173
+ entryId:
174
+ type: string
175
+ description: Journal entry ID
176
+ createdOn:
177
+ type: string
178
+ format: date-time
179
+ description: Journal entry creation date/time
180
+ postings:
181
+ type: array
182
+ items:
183
+ $ref: '#/components/schemas/ResponsePost'
184
+
185
+ NewEntry:
186
+ type: object
187
+ additionalProperties: false
188
+ required:
189
+ - postings
190
+ properties:
191
+ postings:
192
+ type: array
193
+ description: List of posts contained within the journal entry.
194
+ items:
195
+ $ref: '#/components/schemas/Post'
196
+
197
+ Post:
198
+ type: object
199
+ additionalProperties: false
200
+ required:
201
+ - amount
202
+ - currency
203
+ - type
204
+ - createdOn
205
+ - accountId
206
+ properties:
207
+ amount:
208
+ type: string
209
+ currency:
210
+ $ref: '#/components/schemas/Currency'
211
+ type:
212
+ $ref: '#/components/schemas/Type'
213
+ createdOn:
214
+ type: string
215
+ format: date-time
216
+ accountId:
217
+ type: string
218
+ description: account ID
219
+
220
+ ResponsePost:
221
+ type: object
222
+ additionalProperties: false
223
+ required:
224
+ - amount
225
+ - currency
226
+ - type
227
+ - accountId
228
+ - createdOn
229
+ properties:
230
+ amount:
231
+ type: string
232
+ currency:
233
+ $ref: '#/components/schemas/Currency'
234
+ type:
235
+ $ref: '#/components/schemas/Type'
236
+ createdOn:
237
+ type: string
238
+ format: date-time
239
+ accountId:
240
+ type: string
241
+ description: account ID
242
+
243
+ Type:
244
+ type: string
245
+ description: CREDIT or DEBIT
246
+ enum:
247
+ - CREDIT
248
+ - DEBIT
249
+
250
+ CardId:
251
+ type: string
252
+ description: Card identifier
253
+ format: uuid
254
+
255
+ Hash:
256
+ type: string
257
+ format: uuid
258
+ example: '15a85f64-5717-4562-b3fc-2c963f66afa6'
259
+ description: UUID derived using @checkdigit/hash
260
+
261
+ CardResponse:
262
+ type: object
263
+ additionalProperties: false
264
+ required:
265
+ - dataEncryptionKeyId
266
+ - storageKeyId
267
+ - card
268
+ properties:
269
+ encryptedDataEncryptionKey:
270
+ description: |
271
+ Encrypted DEK matching the publicKeyHash sent in the request. Used to decrypt values in the Card object.
272
+ type: string
273
+ card:
274
+ $ref: '#/components/schemas/Card'
275
+
276
+ Card:
277
+ type: object
278
+ additionalProperties: false
279
+ required:
280
+ - last4
281
+ - cardNumber
282
+ properties:
283
+ last4:
284
+ type: string
285
+ pattern: '^\d+$'
286
+ description: Last four digits of the card number
287
+ minLength: 4
288
+ maxLength: 4
289
+ cardNumber:
290
+ type: string
291
+ description: Encrypted card number (PAN)
292
+
293
+ NewCardRequest:
294
+ type: object
295
+ required:
296
+ - dataEncryptionKeyId
297
+ - cardNumberLength
298
+ - newCard
299
+ additionalProperties: false
300
+ properties:
301
+ dataEncryptionKeyId:
302
+ $ref: '#/components/schemas/DataEncryptionKeyId'
303
+ cardNumberLength:
304
+ type: integer
305
+ description: Desired number of digits in the card number.
306
+ minimum: 15
307
+ maximum: 19
308
+ newCard:
309
+ $ref: '#/components/schemas/NewCard'
310
+
311
+ DataEncryptionKeyId:
312
+ type: string
313
+ description: |
314
+ Reference to encryption keys Payment Card Service will use to encrypt the Card object.
315
+ format: uuid
316
+
317
+ NewCard:
318
+ type: object
319
+ additionalProperties: false
320
+ required:
321
+ - serviceCode
322
+ - sequenceNumber
323
+ properties:
324
+ serviceCode:
325
+ type: string
326
+ pattern: '^\d+$'
327
+ description: |
328
+ Used to set general guidelines for how the card can be used, e.g. domestic only or international, online authorizations only, etc.
329
+ minLength: 3
330
+ maxLength: 3
331
+ sequenceNumber:
332
+ type: integer
333
+ description: Used to differentiate issuing the same card and expiration date on multiple plastics.
334
+ minimum: 0
335
+ maximum: 10
336
+
337
+ parameters:
338
+ tenantId:
339
+ in: path
340
+ name: tenantId
341
+ description: Specifies tenant of Ledger service
342
+ required: true
343
+ schema:
344
+ type: string
345
+
346
+ ifMatch:
347
+ in: header
348
+ name: If-Match
349
+ description: If-Match header
350
+ schema:
351
+ type: string
352
+
353
+ at:
354
+ in: query
355
+ name: at
356
+ description: Returns data createdOn before this time.
357
+ required: true
358
+ schema:
359
+ type: string
360
+ format: date-time
361
+
362
+ entryId:
363
+ in: path
364
+ name: entryId
365
+ description: Unique identifier for an entry.
366
+ required: true
367
+ schema:
368
+ type: string
369
+
370
+ createdOn:
371
+ in: header
372
+ name: Created-On
373
+ description: used to set resource creation date.
374
+ required: true
375
+ schema:
376
+ type: string
377
+ format: date-time
378
+
379
+ cardId:
380
+ name: cardId
381
+ in: path
382
+ description: An id unique to each card in uuid format
383
+ required: true
384
+ schema:
385
+ $ref: '#/components/schemas/CardId'
386
+
387
+ publicKeyHashQuery:
388
+ name: publicKeyHashQuery
389
+ in: query
390
+ description: UUID derived from a PEM formatted Public Key using @checkdigit/hash
391
+ required: false
392
+ schema:
393
+ $ref: '#/components/schemas/Hash'
394
+
395
+ requestBodies:
396
+ NewCardRequest:
397
+ required: true
398
+ content:
399
+ application/json:
400
+ schema:
401
+ $ref: '#/components/schemas/NewCardRequest'
402
+
403
+ responses:
404
+ Ping:
405
+ description: ping successful response.
406
+ content:
407
+ application/json:
408
+ schema:
409
+ $ref: '#/components/schemas/Ping'
410
+
411
+ ServerError:
412
+ description: Server Error response.
413
+ content:
414
+ application/json:
415
+ schema:
416
+ $ref: '#/components/schemas/Error'
417
+
418
+ NotFound:
419
+ description: Requested resource not found.
420
+
421
+ Conflict:
422
+ description: Conflict
423
+
424
+ PreConditionFailed:
425
+ description: Version tag mismatch
426
+
427
+ Created:
428
+ description: Created.
429
+ headers:
430
+ Created-On:
431
+ $ref: '#/components/headers/Created-On'
432
+ Updated-On:
433
+ $ref: '#/components/headers/Updated-On'
434
+
435
+ CardResponse:
436
+ description: Card retrieved
437
+ content:
438
+ application/json:
439
+ schema:
440
+ $ref: '#/components/schemas/CardResponse'
441
+ headers:
442
+ Last-Modified:
443
+ $ref: '#/components/headers/Last-Modified'
444
+ Created-On:
445
+ $ref: '#/components/headers/Created-On'
446
+ Updated-On:
447
+ $ref: '#/components/headers/Updated-On'
448
+ ETag:
449
+ $ref: '#/components/headers/ETag'
450
+
451
+ headers:
452
+ Created-On:
453
+ description: When the resource was created.
454
+ required: true
455
+ schema:
456
+ type: string
457
+ format: date-time
458
+
459
+ Updated-On:
460
+ description: The updated-on timestamp for the existing record.
461
+ required: true
462
+ schema:
463
+ type: string
464
+ format: date-time
465
+
466
+ Last-Modified:
467
+ description: When the resource was created (all resources are immutable).
468
+ required: true
469
+ schema:
470
+ type: string
471
+ format: date-time
472
+
473
+ ETag:
474
+ description: Version information for record
475
+ required: true
476
+ schema:
477
+ type: string
@@ -0,0 +1,78 @@
1
+ // athena/api-locator.ts
2
+
3
+ import fs from 'node:fs';
4
+
5
+ import debug from 'debug';
6
+
7
+ import {
8
+ type ApiSchemas,
9
+ generateSchemasForService,
10
+ } from '../openapi/generate-schema.ts';
11
+
12
+ const log = debug('eslint-athena-plugin:athena:api-locator');
13
+
14
+ const SERVICES_ROOT_FOLDER = 'src/services';
15
+ const LEGACY_TABLE_SUFFIX = '_logs';
16
+ // eslint-disable-next-line no-magic-numbers
17
+ const SCHEMA_MAX_AGE_MS = 60 * 60 * 1000; // 1 hour
18
+
19
+ export function locateApi(originalServiceName: string): ApiSchemas[] {
20
+ log('locating API for service', originalServiceName);
21
+
22
+ let serviceName = originalServiceName;
23
+ if (serviceName.endsWith(LEGACY_TABLE_SUFFIX)) {
24
+ log(
25
+ 'service table is a legacy table name, looking for API schemas after removing "_logs" suffix',
26
+ serviceName,
27
+ );
28
+ serviceName = serviceName.slice(0, -LEGACY_TABLE_SUFFIX.length);
29
+ }
30
+
31
+ const camelCaseServiceName = serviceName.replace(
32
+ /-(?<letter>[a-z])/gu,
33
+ (_, letter: string) => letter.toUpperCase(),
34
+ );
35
+
36
+ const allSchemaFilenames = fs.globSync(
37
+ `${SERVICES_ROOT_FOLDER}/${camelCaseServiceName}/*/swagger.schema.deref.json`,
38
+ );
39
+ log(
40
+ `${allSchemaFilenames.length.toString()} versions of API schemas located for service ${serviceName}`,
41
+ allSchemaFilenames,
42
+ );
43
+
44
+ const outputDir = `${SERVICES_ROOT_FOLDER}/${camelCaseServiceName}`;
45
+
46
+ if (allSchemaFilenames.length > 0) {
47
+ const hasStaleSchema = allSchemaFilenames.some(
48
+ (schemaFilename) =>
49
+ Date.now() - fs.statSync(schemaFilename).mtimeMs > SCHEMA_MAX_AGE_MS,
50
+ );
51
+ if (hasStaleSchema) {
52
+ log(
53
+ 'cached schema(s) are stale (older than 1 hour), regenerating for service',
54
+ serviceName,
55
+ );
56
+ const regenerated = generateSchemasForService(serviceName, outputDir);
57
+ if (regenerated.length > 0) {
58
+ return regenerated.map(({ schema }) => schema);
59
+ }
60
+ log(
61
+ 'regeneration failed, falling back to stale cached schemas for service',
62
+ serviceName,
63
+ );
64
+ }
65
+ return allSchemaFilenames.map(
66
+ (schemaFilename) =>
67
+ JSON.parse(fs.readFileSync(schemaFilename, 'utf-8')) as ApiSchemas,
68
+ );
69
+ }
70
+
71
+ log(
72
+ 'no pre-generated schemas found, attempting on-demand generation for service',
73
+ serviceName,
74
+ );
75
+ return generateSchemasForService(serviceName, outputDir).map(
76
+ ({ schema }) => schema,
77
+ );
78
+ }