@internetarchive/metadata-service 0.0.0-bc6513-0

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 (125) hide show
  1. package/.editorconfig +29 -0
  2. package/.eslintrc.js +14 -0
  3. package/.github/workflows/ci.yml +26 -0
  4. package/LICENSE +661 -0
  5. package/README.md +64 -0
  6. package/demo/app-root.ts +80 -0
  7. package/demo/index.html +27 -0
  8. package/dist/demo/app-root.d.ts +11 -0
  9. package/dist/demo/app-root.js +71 -0
  10. package/dist/demo/app-root.js.map +1 -0
  11. package/dist/index.d.ts +18 -0
  12. package/dist/index.js +17 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/src/backend/default-metadata-backend.d.ts +27 -0
  15. package/dist/src/backend/default-metadata-backend.js +101 -0
  16. package/dist/src/backend/default-metadata-backend.js.map +1 -0
  17. package/dist/src/backend/metadata-backend-interface.d.ts +20 -0
  18. package/dist/src/backend/metadata-backend-interface.js +2 -0
  19. package/dist/src/backend/metadata-backend-interface.js.map +1 -0
  20. package/dist/src/metadata-service-error.d.ts +11 -0
  21. package/dist/src/metadata-service-error.js +18 -0
  22. package/dist/src/metadata-service-error.js.map +1 -0
  23. package/dist/src/metadata-service-interface.d.ts +44 -0
  24. package/dist/src/metadata-service-interface.js +2 -0
  25. package/dist/src/metadata-service-interface.js.map +1 -0
  26. package/dist/src/metadata-service.d.ts +18 -0
  27. package/dist/src/metadata-service.js +43 -0
  28. package/dist/src/metadata-service.js.map +1 -0
  29. package/dist/src/models/file.d.ts +29 -0
  30. package/dist/src/models/file.js +95 -0
  31. package/dist/src/models/file.js.map +1 -0
  32. package/dist/src/models/metadata-fields/field-types/boolean.d.ts +5 -0
  33. package/dist/src/models/metadata-fields/field-types/boolean.js +8 -0
  34. package/dist/src/models/metadata-fields/field-types/boolean.js.map +1 -0
  35. package/dist/src/models/metadata-fields/field-types/byte.d.ts +13 -0
  36. package/dist/src/models/metadata-fields/field-types/byte.js +16 -0
  37. package/dist/src/models/metadata-fields/field-types/byte.js.map +1 -0
  38. package/dist/src/models/metadata-fields/field-types/date.d.ts +5 -0
  39. package/dist/src/models/metadata-fields/field-types/date.js +8 -0
  40. package/dist/src/models/metadata-fields/field-types/date.js.map +1 -0
  41. package/dist/src/models/metadata-fields/field-types/duration.d.ts +14 -0
  42. package/dist/src/models/metadata-fields/field-types/duration.js +17 -0
  43. package/dist/src/models/metadata-fields/field-types/duration.js.map +1 -0
  44. package/dist/src/models/metadata-fields/field-types/list.d.ts +25 -0
  45. package/dist/src/models/metadata-fields/field-types/list.js +34 -0
  46. package/dist/src/models/metadata-fields/field-types/list.js.map +1 -0
  47. package/dist/src/models/metadata-fields/field-types/mediatype.d.ts +5 -0
  48. package/dist/src/models/metadata-fields/field-types/mediatype.js +8 -0
  49. package/dist/src/models/metadata-fields/field-types/mediatype.js.map +1 -0
  50. package/dist/src/models/metadata-fields/field-types/number.d.ts +5 -0
  51. package/dist/src/models/metadata-fields/field-types/number.js +8 -0
  52. package/dist/src/models/metadata-fields/field-types/number.js.map +1 -0
  53. package/dist/src/models/metadata-fields/field-types/page-progression.d.ts +5 -0
  54. package/dist/src/models/metadata-fields/field-types/page-progression.js +8 -0
  55. package/dist/src/models/metadata-fields/field-types/page-progression.js.map +1 -0
  56. package/dist/src/models/metadata-fields/field-types/string.d.ts +5 -0
  57. package/dist/src/models/metadata-fields/field-types/string.js +8 -0
  58. package/dist/src/models/metadata-fields/field-types/string.js.map +1 -0
  59. package/dist/src/models/metadata-fields/metadata-field.d.ts +70 -0
  60. package/dist/src/models/metadata-fields/metadata-field.js +67 -0
  61. package/dist/src/models/metadata-fields/metadata-field.js.map +1 -0
  62. package/dist/src/models/metadata.d.ts +169 -0
  63. package/dist/src/models/metadata.js +627 -0
  64. package/dist/src/models/metadata.js.map +1 -0
  65. package/dist/src/models/review.d.ts +10 -0
  66. package/dist/src/models/review.js +43 -0
  67. package/dist/src/models/review.js.map +1 -0
  68. package/dist/src/models/speech-music-asr-entry.d.ts +10 -0
  69. package/dist/src/models/speech-music-asr-entry.js +2 -0
  70. package/dist/src/models/speech-music-asr-entry.js.map +1 -0
  71. package/dist/src/responses/metadata-response.d.ts +28 -0
  72. package/dist/src/responses/metadata-response.js +31 -0
  73. package/dist/src/responses/metadata-response.js.map +1 -0
  74. package/dist/test/default-metadata-backend.test.d.ts +1 -0
  75. package/dist/test/default-metadata-backend.test.js +91 -0
  76. package/dist/test/default-metadata-backend.test.js.map +1 -0
  77. package/dist/test/metadata-service.test.d.ts +1 -0
  78. package/dist/test/metadata-service.test.js +99 -0
  79. package/dist/test/metadata-service.test.js.map +1 -0
  80. package/dist/test/mock-response-generator.d.ts +3 -0
  81. package/dist/test/mock-response-generator.js +62 -0
  82. package/dist/test/mock-response-generator.js.map +1 -0
  83. package/dist/test/models/file.test.d.ts +1 -0
  84. package/dist/test/models/file.test.js +9 -0
  85. package/dist/test/models/file.test.js.map +1 -0
  86. package/dist/test/models/metadata-fields/list-field.test.d.ts +1 -0
  87. package/dist/test/models/metadata-fields/list-field.test.js +52 -0
  88. package/dist/test/models/metadata-fields/list-field.test.js.map +1 -0
  89. package/dist/test/models/metadata-fields/metadata-field.test.d.ts +1 -0
  90. package/dist/test/models/metadata-fields/metadata-field.test.js +121 -0
  91. package/dist/test/models/metadata-fields/metadata-field.test.js.map +1 -0
  92. package/dist/test/models/metadata.test.d.ts +1 -0
  93. package/dist/test/models/metadata.test.js +10 -0
  94. package/dist/test/models/metadata.test.js.map +1 -0
  95. package/index.ts +32 -0
  96. package/package.json +77 -0
  97. package/src/backend/default-metadata-backend.ts +141 -0
  98. package/src/backend/metadata-backend-interface.ts +25 -0
  99. package/src/metadata-service-error.ts +21 -0
  100. package/src/metadata-service-interface.ts +51 -0
  101. package/src/metadata-service.ts +63 -0
  102. package/src/models/file.ts +105 -0
  103. package/src/models/metadata-fields/field-types/boolean.ts +8 -0
  104. package/src/models/metadata-fields/field-types/byte.ts +16 -0
  105. package/src/models/metadata-fields/field-types/date.ts +8 -0
  106. package/src/models/metadata-fields/field-types/duration.ts +17 -0
  107. package/src/models/metadata-fields/field-types/list.ts +44 -0
  108. package/src/models/metadata-fields/field-types/mediatype.ts +8 -0
  109. package/src/models/metadata-fields/field-types/number.ts +8 -0
  110. package/src/models/metadata-fields/field-types/page-progression.ts +14 -0
  111. package/src/models/metadata-fields/field-types/string.ts +8 -0
  112. package/src/models/metadata-fields/metadata-field.ts +108 -0
  113. package/src/models/metadata.ts +463 -0
  114. package/src/models/review.ts +41 -0
  115. package/src/models/speech-music-asr-entry.ts +10 -0
  116. package/src/responses/metadata-response.ts +61 -0
  117. package/test/default-metadata-backend.test.ts +101 -0
  118. package/test/metadata-service.test.ts +150 -0
  119. package/test/mock-response-generator.ts +62 -0
  120. package/test/models/file.test.ts +10 -0
  121. package/test/models/metadata-fields/list-field.test.ts +64 -0
  122. package/test/models/metadata-fields/metadata-field.test.ts +151 -0
  123. package/test/models/metadata.test.ts +11 -0
  124. package/tsconfig.json +22 -0
  125. package/web-test-runner.config.mjs +7 -0
@@ -0,0 +1,463 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { Memoize } from 'typescript-memoize';
3
+ import { BooleanField } from './metadata-fields/field-types/boolean';
4
+ import { DateField } from './metadata-fields/field-types/date';
5
+ import { DurationField } from './metadata-fields/field-types/duration';
6
+ import { NumberField } from './metadata-fields/field-types/number';
7
+ import { StringField } from './metadata-fields/field-types/string';
8
+ import { PageProgressionField } from './metadata-fields/field-types/page-progression';
9
+ import { ByteField } from './metadata-fields/field-types/byte';
10
+ import { MediaTypeField } from './metadata-fields/field-types/mediatype';
11
+ import { StringListField } from './metadata-fields/field-types/list';
12
+
13
+ /**
14
+ * Metadata is an expansive model that describes an Item.
15
+ *
16
+ * The fields in here get casted to their respective field types. See `metadata-fields/field-type`.
17
+ *
18
+ * Add additional fields as needed.
19
+ *
20
+ * @export
21
+ * @class Metadata
22
+ */
23
+ export class Metadata {
24
+ /**
25
+ * This is the raw metadata reponse; useful for inspecting the raw data returned from the server.
26
+ *
27
+ * @type { string: any }
28
+ * @memberof Metadata
29
+ */
30
+ rawMetadata?: Record<string, any>;
31
+
32
+ /**
33
+ * The item identifier.
34
+ *
35
+ * _Note_ This is a plain string instead of a `MetadataField` since it's
36
+ * the primary key of the item.
37
+ *
38
+ * @type {string}
39
+ * @memberof Metadata
40
+ */
41
+ get identifier(): string | undefined {
42
+ return this.rawMetadata?.identifier;
43
+ }
44
+
45
+ @Memoize() get addeddate(): DateField | undefined {
46
+ return this.rawMetadata?.addeddate
47
+ ? new DateField(this.rawMetadata.addeddate)
48
+ : undefined;
49
+ }
50
+
51
+ @Memoize() get audio_codec(): StringField | undefined {
52
+ return this.rawMetadata?.audio_codec
53
+ ? new StringField(this.rawMetadata.audio_codec)
54
+ : undefined;
55
+ }
56
+
57
+ @Memoize() get audio_sample_rate(): NumberField | undefined {
58
+ return this.rawMetadata?.audio_sample_rate
59
+ ? new NumberField(this.rawMetadata.audio_sample_rate)
60
+ : undefined;
61
+ }
62
+
63
+ @Memoize() get avg_rating(): NumberField | undefined {
64
+ return this.rawMetadata?.avg_rating
65
+ ? new NumberField(this.rawMetadata.avg_rating)
66
+ : undefined;
67
+ }
68
+
69
+ /**
70
+ * All of the collections that an Item is in, including
71
+ * all of the side-loaded collections from the ListAPI
72
+ * and SimpleListsAPI like `fav-*`
73
+ *
74
+ * @type {StringField}
75
+ * @memberof Metadata
76
+ */
77
+ @Memoize() get collection(): StringField | undefined {
78
+ return this.rawMetadata?.collection
79
+ ? new StringField(this.rawMetadata.collection)
80
+ : undefined;
81
+ }
82
+
83
+ /**
84
+ * The "natural" collections for an item before augmentation
85
+ * by side-loaded collections like ListsAPI and SimpleLists
86
+ *
87
+ * The `collection` field above includes things like all of
88
+ * the `fav-*` collections, whereas this is only the collections
89
+ * that have been directly added in the hierarchy.
90
+ *
91
+ * @type {StringField}
92
+ * @memberof Metadata
93
+ */
94
+ @Memoize() get collections_raw(): StringField | undefined {
95
+ return this.rawMetadata?.collections_raw
96
+ ? new StringField(this.rawMetadata.collections_raw)
97
+ : undefined;
98
+ }
99
+
100
+ /**
101
+ * The size of a collection in bytes
102
+ *
103
+ * @type {ByteField}
104
+ * @memberof Metadata
105
+ */
106
+ @Memoize() get collection_size(): ByteField | undefined {
107
+ return this.rawMetadata?.collection_size
108
+ ? new ByteField(this.rawMetadata.collection_size)
109
+ : undefined;
110
+ }
111
+
112
+ @Memoize() get contributor(): StringField | undefined {
113
+ return this.rawMetadata?.contributor
114
+ ? new StringField(this.rawMetadata.contributor)
115
+ : undefined;
116
+ }
117
+
118
+ @Memoize() get coverage(): StringField | undefined {
119
+ return this.rawMetadata?.coverage
120
+ ? new StringField(this.rawMetadata.coverage)
121
+ : undefined;
122
+ }
123
+
124
+ @Memoize() get creator(): StringField | undefined {
125
+ return this.rawMetadata?.creator
126
+ ? new StringField(this.rawMetadata.creator)
127
+ : undefined;
128
+ }
129
+
130
+ @Memoize() get collection_layout(): StringField | undefined {
131
+ return this.rawMetadata?.collection_layout
132
+ ? new StringField(this.rawMetadata.collection_layout)
133
+ : undefined;
134
+ }
135
+
136
+ @Memoize() get date(): DateField | undefined {
137
+ return this.rawMetadata?.date
138
+ ? new DateField(this.rawMetadata.date)
139
+ : undefined;
140
+ }
141
+
142
+ @Memoize() get description(): StringField | undefined {
143
+ return this.rawMetadata?.description
144
+ ? new StringField(this.rawMetadata.description)
145
+ : undefined;
146
+ }
147
+
148
+ /**
149
+ * All time download count
150
+ *
151
+ * @type {NumberField}
152
+ * @memberof Metadata
153
+ */
154
+ @Memoize() get downloads(): NumberField | undefined {
155
+ return this.rawMetadata?.downloads
156
+ ? new NumberField(this.rawMetadata.downloads)
157
+ : undefined;
158
+ }
159
+
160
+ /**
161
+ * The item duration in seconds
162
+ *
163
+ * @type {DurationField}
164
+ * @memberof Metadata
165
+ */
166
+ @Memoize() get duration(): DurationField | undefined {
167
+ return this.rawMetadata?.duration
168
+ ? new DurationField(this.rawMetadata.duration)
169
+ : undefined;
170
+ }
171
+
172
+ @Memoize() get 'external-identifier'(): StringField | undefined {
173
+ return this.rawMetadata?.['external-identifier']
174
+ ? new StringField(this.rawMetadata?.['external-identifier'])
175
+ : undefined;
176
+ }
177
+
178
+ /**
179
+ * The number of files in an item
180
+ *
181
+ * @type {NumberField}
182
+ * @memberof Metadata
183
+ */
184
+ @Memoize() get files_count(): NumberField | undefined {
185
+ return this.rawMetadata?.files_count
186
+ ? new NumberField(this.rawMetadata.files_count)
187
+ : undefined;
188
+ }
189
+
190
+ @Memoize() get indexdate(): DateField | undefined {
191
+ return this.rawMetadata?.indexdate
192
+ ? new DateField(this.rawMetadata.indexdate)
193
+ : undefined;
194
+ }
195
+
196
+ @Memoize() get isbn(): StringField | undefined {
197
+ return this.rawMetadata?.isbn
198
+ ? new StringField(this.rawMetadata.isbn)
199
+ : undefined;
200
+ }
201
+
202
+ @Memoize() get issue(): StringField | undefined {
203
+ return this.rawMetadata?.issue
204
+ ? new StringField(this.rawMetadata.issue)
205
+ : undefined;
206
+ }
207
+
208
+ /**
209
+ * For collections, the number of items in the collection
210
+ *
211
+ * @type {NumberField}
212
+ * @memberof Metadata
213
+ */
214
+ @Memoize() get item_count(): NumberField | undefined {
215
+ return this.rawMetadata?.item_count
216
+ ? new NumberField(this.rawMetadata.item_count)
217
+ : undefined;
218
+ }
219
+
220
+ /**
221
+ * The size of the item in bytes
222
+ *
223
+ * @type {NumberField}
224
+ * @memberof Metadata
225
+ */
226
+ @Memoize() get item_size(): ByteField | undefined {
227
+ return this.rawMetadata?.item_size
228
+ ? new ByteField(this.rawMetadata.item_size)
229
+ : undefined;
230
+ }
231
+
232
+ @Memoize() get language(): StringField | undefined {
233
+ return this.rawMetadata?.language
234
+ ? new StringField(this.rawMetadata.language)
235
+ : undefined;
236
+ }
237
+
238
+ @Memoize() get length(): DurationField | undefined {
239
+ return this.rawMetadata?.length
240
+ ? new DurationField(this.rawMetadata.length)
241
+ : undefined;
242
+ }
243
+
244
+ @Memoize() get lineage(): StringField | undefined {
245
+ return this.rawMetadata?.lineage
246
+ ? new StringField(this.rawMetadata.lineage)
247
+ : undefined;
248
+ }
249
+
250
+ /**
251
+ * The number of downloads in the last month
252
+ *
253
+ * @type {NumberField}
254
+ * @memberof Metadata
255
+ */
256
+ @Memoize() get month(): NumberField | undefined {
257
+ return this.rawMetadata?.month
258
+ ? new NumberField(this.rawMetadata.month)
259
+ : undefined;
260
+ }
261
+
262
+ @Memoize() get mediatype(): MediaTypeField | undefined {
263
+ return this.rawMetadata?.mediatype
264
+ ? new MediaTypeField(this.rawMetadata.mediatype)
265
+ : undefined;
266
+ }
267
+
268
+ @Memoize() get noindex(): BooleanField | undefined {
269
+ return this.rawMetadata?.noindex
270
+ ? new BooleanField(this.rawMetadata.noindex)
271
+ : undefined;
272
+ }
273
+
274
+ @Memoize() get notes(): StringField | undefined {
275
+ return this.rawMetadata?.notes
276
+ ? new StringField(this.rawMetadata.notes)
277
+ : undefined;
278
+ }
279
+
280
+ /**
281
+ * The number of users that have favorited the item
282
+ *
283
+ * @type {NumberField}
284
+ * @memberof Metadata
285
+ */
286
+ @Memoize() get num_favorites(): NumberField | undefined {
287
+ return this.rawMetadata?.num_favorites
288
+ ? new NumberField(this.rawMetadata.num_favorites)
289
+ : undefined;
290
+ }
291
+
292
+ @Memoize() get num_reviews(): NumberField | undefined {
293
+ return this.rawMetadata?.num_reviews
294
+ ? new NumberField(this.rawMetadata.num_reviews)
295
+ : undefined;
296
+ }
297
+
298
+ @Memoize() get openlibrary_edition(): StringField | undefined {
299
+ return this.rawMetadata?.openlibrary_edition
300
+ ? new StringField(this.rawMetadata.openlibrary_edition)
301
+ : undefined;
302
+ }
303
+
304
+ @Memoize() get openlibrary_work(): StringField | undefined {
305
+ return this.rawMetadata?.openlibrary_work
306
+ ? new StringField(this.rawMetadata.openlibrary_work)
307
+ : undefined;
308
+ }
309
+
310
+ @Memoize() get page_progression(): PageProgressionField | undefined {
311
+ return this.rawMetadata?.page_progression
312
+ ? new PageProgressionField(this.rawMetadata.page_progression)
313
+ : undefined;
314
+ }
315
+
316
+ @Memoize() get partner(): StringField | undefined {
317
+ return this.rawMetadata?.partner
318
+ ? new StringField(this.rawMetadata.partner)
319
+ : undefined;
320
+ }
321
+
322
+ @Memoize() get ppi(): NumberField | undefined {
323
+ return this.rawMetadata?.ppi
324
+ ? new NumberField(this.rawMetadata.ppi)
325
+ : undefined;
326
+ }
327
+
328
+ @Memoize() get publicdate(): DateField | undefined {
329
+ return this.rawMetadata?.publicdate
330
+ ? new DateField(this.rawMetadata.publicdate)
331
+ : undefined;
332
+ }
333
+
334
+ @Memoize() get publisher(): StringField | undefined {
335
+ return this.rawMetadata?.publisher
336
+ ? new StringField(this.rawMetadata.publisher)
337
+ : undefined;
338
+ }
339
+
340
+ @Memoize() get reviewdate(): DateField | undefined {
341
+ return this.rawMetadata?.reviewdate
342
+ ? new DateField(this.rawMetadata.reviewdate)
343
+ : undefined;
344
+ }
345
+
346
+ @Memoize() get runtime(): DurationField | undefined {
347
+ return this.rawMetadata?.runtime
348
+ ? new DurationField(this.rawMetadata.runtime)
349
+ : undefined;
350
+ }
351
+
352
+ @Memoize() get scanner(): StringField | undefined {
353
+ return this.rawMetadata?.scanner
354
+ ? new StringField(this.rawMetadata.scanner)
355
+ : undefined;
356
+ }
357
+
358
+ @Memoize() get source(): StringField | undefined {
359
+ return this.rawMetadata?.source
360
+ ? new StringField(this.rawMetadata.source)
361
+ : undefined;
362
+ }
363
+
364
+ @Memoize() get start_localtime(): DateField | undefined {
365
+ return this.rawMetadata?.start_localtime
366
+ ? new DateField(this.rawMetadata.start_localtime)
367
+ : undefined;
368
+ }
369
+
370
+ @Memoize() get start_time(): DateField | undefined {
371
+ return this.rawMetadata?.start_time
372
+ ? new DateField(this.rawMetadata.start_time)
373
+ : undefined;
374
+ }
375
+
376
+ @Memoize() get stop_time(): DateField | undefined {
377
+ return this.rawMetadata?.stop_time
378
+ ? new DateField(this.rawMetadata.stop_time)
379
+ : undefined;
380
+ }
381
+
382
+ @Memoize() get subject(): StringListField | undefined {
383
+ return this.rawMetadata?.subject
384
+ ? new StringListField(this.rawMetadata.subject)
385
+ : undefined;
386
+ }
387
+
388
+ @Memoize() get taper(): StringField | undefined {
389
+ return this.rawMetadata?.taper
390
+ ? new StringField(this.rawMetadata.taper)
391
+ : undefined;
392
+ }
393
+
394
+ @Memoize() get title(): StringField | undefined {
395
+ return this.rawMetadata?.title
396
+ ? new StringField(this.rawMetadata.title)
397
+ : undefined;
398
+ }
399
+
400
+ @Memoize() get transferer(): StringField | undefined {
401
+ return this.rawMetadata?.transferer
402
+ ? new StringField(this.rawMetadata.transferer)
403
+ : undefined;
404
+ }
405
+
406
+ @Memoize() get track(): NumberField | undefined {
407
+ return this.rawMetadata?.track
408
+ ? new NumberField(this.rawMetadata.track)
409
+ : undefined;
410
+ }
411
+
412
+ @Memoize() get type(): StringField | undefined {
413
+ return this.rawMetadata?.type
414
+ ? new StringField(this.rawMetadata.type)
415
+ : undefined;
416
+ }
417
+
418
+ @Memoize() get uploader(): StringField | undefined {
419
+ return this.rawMetadata?.uploader
420
+ ? new StringField(this.rawMetadata.uploader)
421
+ : undefined;
422
+ }
423
+
424
+ @Memoize() get utc_offset(): NumberField | undefined {
425
+ return this.rawMetadata?.utc_offset
426
+ ? new NumberField(this.rawMetadata.utc_offset)
427
+ : undefined;
428
+ }
429
+
430
+ @Memoize() get venue(): StringField | undefined {
431
+ return this.rawMetadata?.venue
432
+ ? new StringField(this.rawMetadata.venue)
433
+ : undefined;
434
+ }
435
+
436
+ @Memoize() get volume(): StringField | undefined {
437
+ return this.rawMetadata?.volume
438
+ ? new StringField(this.rawMetadata.volume)
439
+ : undefined;
440
+ }
441
+
442
+ /**
443
+ * The number of downloads in the last week
444
+ *
445
+ * @type {NumberField}
446
+ * @memberof Metadata
447
+ */
448
+ @Memoize() get week(): NumberField | undefined {
449
+ return this.rawMetadata?.week
450
+ ? new NumberField(this.rawMetadata.week)
451
+ : undefined;
452
+ }
453
+
454
+ @Memoize() get year(): DateField | undefined {
455
+ return this.rawMetadata?.year
456
+ ? new DateField(this.rawMetadata.year)
457
+ : undefined;
458
+ }
459
+
460
+ constructor(json: Record<string, any>) {
461
+ this.rawMetadata = json;
462
+ }
463
+ }
@@ -0,0 +1,41 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { Memoize } from 'typescript-memoize';
3
+ import { DateParser, NumberParser } from '@internetarchive/field-parsers';
4
+
5
+ export class Review {
6
+ rawValue: Record<string, any>;
7
+
8
+ get reviewbody(): string | undefined {
9
+ return this.rawValue.reviewbody;
10
+ }
11
+
12
+ get reviewtitle(): string | undefined {
13
+ return this.rawValue.reviewtitle;
14
+ }
15
+
16
+ get reviewer(): string | undefined {
17
+ return this.rawValue.reviewer;
18
+ }
19
+
20
+ @Memoize() get reviewdate(): Date | undefined {
21
+ return this.rawValue.reviewdate
22
+ ? DateParser.shared.parseValue(this.rawValue.reviewdate)
23
+ : undefined;
24
+ }
25
+
26
+ @Memoize() get createdate(): Date | undefined {
27
+ return this.rawValue.createdate
28
+ ? DateParser.shared.parseValue(this.rawValue.createdate)
29
+ : undefined;
30
+ }
31
+
32
+ @Memoize() get stars(): number | undefined {
33
+ return this.rawValue.stars
34
+ ? NumberParser.shared.parseValue(this.rawValue.stars)
35
+ : undefined;
36
+ }
37
+
38
+ constructor(json: Record<string, any>) {
39
+ this.rawValue = json;
40
+ }
41
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * This is the format used for radio transcripts
3
+ */
4
+ export interface SpeechMusicASREntry {
5
+ end: number;
6
+ id: number;
7
+ is_music: boolean;
8
+ start: number;
9
+ text: string;
10
+ }
@@ -0,0 +1,61 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { File } from '../models/file';
3
+ import { Metadata } from '../models/metadata';
4
+ import { Review } from '../models/review';
5
+ import { SpeechMusicASREntry } from '../models/speech-music-asr-entry';
6
+
7
+ /**
8
+ * The main top-level reponse when fetching Metadata
9
+ *
10
+ * @export
11
+ * @class MetadataResponse
12
+ */
13
+ export class MetadataResponse {
14
+ rawResponse: any;
15
+
16
+ created: number;
17
+
18
+ d1: string;
19
+
20
+ d2: string;
21
+
22
+ dir: string;
23
+
24
+ files: File[];
25
+
26
+ files_count: number;
27
+
28
+ item_last_updated: number;
29
+
30
+ item_size: number;
31
+
32
+ metadata: Metadata;
33
+
34
+ server: string;
35
+
36
+ uniq: number;
37
+
38
+ workable_servers: string[];
39
+
40
+ speech_vs_music_asr?: SpeechMusicASREntry[];
41
+
42
+ reviews?: Review[];
43
+
44
+ constructor(json: Record<string, any>) {
45
+ this.rawResponse = json;
46
+ this.created = json.created;
47
+ this.d1 = json.d1;
48
+ this.d2 = json.d2;
49
+ this.dir = json.dir;
50
+ this.files = json.files?.map((file: any) => new File(file));
51
+ this.files_count = json.files_count;
52
+ this.item_last_updated = json.item_last_updated;
53
+ this.item_size = json.item_size;
54
+ this.metadata = new Metadata(json.metadata);
55
+ this.server = json.server;
56
+ this.uniq = json.uniq;
57
+ this.workable_servers = json.workable_servers;
58
+ this.speech_vs_music_asr = json.speech_vs_music_asr;
59
+ this.reviews = json.reviews?.map((entry: any) => new Review(entry));
60
+ }
61
+ }
@@ -0,0 +1,101 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ import { expect } from '@open-wc/testing';
4
+ import { DefaultMetadataBackend } from '../src/backend/default-metadata-backend';
5
+ import { MetadataServiceErrorType } from '../src/metadata-service-error';
6
+
7
+ describe('DefaultMetadataBackend', () => {
8
+ it('can fetch metadata', async () => {
9
+ const fetchBackup = window.fetch;
10
+ window.fetch = (): Promise<Response> => {
11
+ return new Promise(resolve => {
12
+ const response = new Response('{ "foo": "bar" }');
13
+ resolve(response);
14
+ });
15
+ };
16
+
17
+ const backend = new DefaultMetadataBackend();
18
+ const result = await backend.fetchMetadata('foo');
19
+ expect(result.success?.foo).to.equal('bar');
20
+ window.fetch = fetchBackup;
21
+ });
22
+
23
+ it('returns a networkError if theres a problem fetching using String type', async () => {
24
+ const fetchBackup = window.fetch;
25
+ window.fetch = (): Promise<Response> => {
26
+ throw 'network error';
27
+ };
28
+
29
+ const backend = new DefaultMetadataBackend();
30
+ const result = await backend.fetchMetadata('foo');
31
+ expect(result.error?.type).to.equal(MetadataServiceErrorType.networkError);
32
+ expect(result.error?.message).to.equal('network error');
33
+ window.fetch = fetchBackup;
34
+ });
35
+
36
+ it('returns a networkError if theres a problem fetching using Error type', async () => {
37
+ const fetchBackup = window.fetch;
38
+ window.fetch = (): Promise<Response> => {
39
+ throw new Error('network error');
40
+ };
41
+
42
+ const backend = new DefaultMetadataBackend();
43
+ const result = await backend.fetchMetadata('foo');
44
+ expect(result.error?.type).to.equal(MetadataServiceErrorType.networkError);
45
+ expect(result.error?.message).to.equal('network error');
46
+ window.fetch = fetchBackup;
47
+ });
48
+
49
+ it('returns a decodingError if theres a problem decoding the json', async () => {
50
+ const fetchBackup = window.fetch;
51
+ window.fetch = (): Promise<Response> => {
52
+ const response = new Response('boop');
53
+ return new Promise(resolve => resolve(response));
54
+ };
55
+
56
+ const backend = new DefaultMetadataBackend();
57
+ const result = await backend.fetchMetadata('foo');
58
+ expect(result.error?.type).to.equal(MetadataServiceErrorType.decodingError);
59
+ window.fetch = fetchBackup;
60
+ });
61
+
62
+ it('appends the scope if provided', async () => {
63
+ const fetchBackup = window.fetch;
64
+ let urlCalled = '';
65
+ window.fetch = (url: RequestInfo): Promise<Response> => {
66
+ urlCalled = url.toString();
67
+ const response = new Response('boop');
68
+ return new Promise(resolve => resolve(response));
69
+ };
70
+
71
+ const backend = new DefaultMetadataBackend({
72
+ scope: 'foo',
73
+ });
74
+ const result = await backend.fetchMetadata('foo');
75
+ expect(urlCalled.includes('scope=foo')).to.be.true;
76
+ window.fetch = fetchBackup;
77
+ });
78
+
79
+ it('does not credentials for metadata endpoint', async () => {
80
+ const fetchBackup = window.fetch;
81
+ let urlCalled: RequestInfo;
82
+ let urlConfig: RequestInit | undefined;
83
+ window.fetch = (
84
+ url: RequestInfo,
85
+ config?: RequestInit
86
+ ): Promise<Response> => {
87
+ urlCalled = url;
88
+ urlConfig = config;
89
+ const response = new Response('boop');
90
+ return new Promise(resolve => resolve(response));
91
+ };
92
+
93
+ const backend = new DefaultMetadataBackend({
94
+ scope: 'foo',
95
+ includeCredentials: true,
96
+ });
97
+ await backend.fetchMetadata('foo');
98
+ expect(urlConfig?.credentials).to.equal('omit');
99
+ window.fetch = fetchBackup;
100
+ });
101
+ });