@internetarchive/metadata-service 0.0.1-alpha.1

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 +168 -0
  63. package/dist/src/models/metadata.js +618 -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 +457 -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,457 @@
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 date(): DateField | undefined {
131
+ return this.rawMetadata?.date
132
+ ? new DateField(this.rawMetadata.date)
133
+ : undefined;
134
+ }
135
+
136
+ @Memoize() get description(): StringField | undefined {
137
+ return this.rawMetadata?.description
138
+ ? new StringField(this.rawMetadata.description)
139
+ : undefined;
140
+ }
141
+
142
+ /**
143
+ * All time download count
144
+ *
145
+ * @type {NumberField}
146
+ * @memberof Metadata
147
+ */
148
+ @Memoize() get downloads(): NumberField | undefined {
149
+ return this.rawMetadata?.downloads
150
+ ? new NumberField(this.rawMetadata.downloads)
151
+ : undefined;
152
+ }
153
+
154
+ /**
155
+ * The item duration in seconds
156
+ *
157
+ * @type {DurationField}
158
+ * @memberof Metadata
159
+ */
160
+ @Memoize() get duration(): DurationField | undefined {
161
+ return this.rawMetadata?.duration
162
+ ? new DurationField(this.rawMetadata.duration)
163
+ : undefined;
164
+ }
165
+
166
+ @Memoize() get 'external-identifier'(): StringField | undefined {
167
+ return this.rawMetadata?.['external-identifier']
168
+ ? new StringField(this.rawMetadata?.['external-identifier'])
169
+ : undefined;
170
+ }
171
+
172
+ /**
173
+ * The number of files in an item
174
+ *
175
+ * @type {NumberField}
176
+ * @memberof Metadata
177
+ */
178
+ @Memoize() get files_count(): NumberField | undefined {
179
+ return this.rawMetadata?.files_count
180
+ ? new NumberField(this.rawMetadata.files_count)
181
+ : undefined;
182
+ }
183
+
184
+ @Memoize() get indexdate(): DateField | undefined {
185
+ return this.rawMetadata?.indexdate
186
+ ? new DateField(this.rawMetadata.indexdate)
187
+ : undefined;
188
+ }
189
+
190
+ @Memoize() get isbn(): StringField | undefined {
191
+ return this.rawMetadata?.isbn
192
+ ? new StringField(this.rawMetadata.isbn)
193
+ : undefined;
194
+ }
195
+
196
+ @Memoize() get issue(): StringField | undefined {
197
+ return this.rawMetadata?.issue
198
+ ? new StringField(this.rawMetadata.issue)
199
+ : undefined;
200
+ }
201
+
202
+ /**
203
+ * For collections, the number of items in the collection
204
+ *
205
+ * @type {NumberField}
206
+ * @memberof Metadata
207
+ */
208
+ @Memoize() get item_count(): NumberField | undefined {
209
+ return this.rawMetadata?.item_count
210
+ ? new NumberField(this.rawMetadata.item_count)
211
+ : undefined;
212
+ }
213
+
214
+ /**
215
+ * The size of the item in bytes
216
+ *
217
+ * @type {NumberField}
218
+ * @memberof Metadata
219
+ */
220
+ @Memoize() get item_size(): ByteField | undefined {
221
+ return this.rawMetadata?.item_size
222
+ ? new ByteField(this.rawMetadata.item_size)
223
+ : undefined;
224
+ }
225
+
226
+ @Memoize() get language(): StringField | undefined {
227
+ return this.rawMetadata?.language
228
+ ? new StringField(this.rawMetadata.language)
229
+ : undefined;
230
+ }
231
+
232
+ @Memoize() get length(): DurationField | undefined {
233
+ return this.rawMetadata?.length
234
+ ? new DurationField(this.rawMetadata.length)
235
+ : undefined;
236
+ }
237
+
238
+ @Memoize() get lineage(): StringField | undefined {
239
+ return this.rawMetadata?.lineage
240
+ ? new StringField(this.rawMetadata.lineage)
241
+ : undefined;
242
+ }
243
+
244
+ /**
245
+ * The number of downloads in the last month
246
+ *
247
+ * @type {NumberField}
248
+ * @memberof Metadata
249
+ */
250
+ @Memoize() get month(): NumberField | undefined {
251
+ return this.rawMetadata?.month
252
+ ? new NumberField(this.rawMetadata.month)
253
+ : undefined;
254
+ }
255
+
256
+ @Memoize() get mediatype(): MediaTypeField | undefined {
257
+ return this.rawMetadata?.mediatype
258
+ ? new MediaTypeField(this.rawMetadata.mediatype)
259
+ : undefined;
260
+ }
261
+
262
+ @Memoize() get noindex(): BooleanField | undefined {
263
+ return this.rawMetadata?.noindex
264
+ ? new BooleanField(this.rawMetadata.noindex)
265
+ : undefined;
266
+ }
267
+
268
+ @Memoize() get notes(): StringField | undefined {
269
+ return this.rawMetadata?.notes
270
+ ? new StringField(this.rawMetadata.notes)
271
+ : undefined;
272
+ }
273
+
274
+ /**
275
+ * The number of users that have favorited the item
276
+ *
277
+ * @type {NumberField}
278
+ * @memberof Metadata
279
+ */
280
+ @Memoize() get num_favorites(): NumberField | undefined {
281
+ return this.rawMetadata?.num_favorites
282
+ ? new NumberField(this.rawMetadata.num_favorites)
283
+ : undefined;
284
+ }
285
+
286
+ @Memoize() get num_reviews(): NumberField | undefined {
287
+ return this.rawMetadata?.num_reviews
288
+ ? new NumberField(this.rawMetadata.num_reviews)
289
+ : undefined;
290
+ }
291
+
292
+ @Memoize() get openlibrary_edition(): StringField | undefined {
293
+ return this.rawMetadata?.openlibrary_edition
294
+ ? new StringField(this.rawMetadata.openlibrary_edition)
295
+ : undefined;
296
+ }
297
+
298
+ @Memoize() get openlibrary_work(): StringField | undefined {
299
+ return this.rawMetadata?.openlibrary_work
300
+ ? new StringField(this.rawMetadata.openlibrary_work)
301
+ : undefined;
302
+ }
303
+
304
+ @Memoize() get page_progression(): PageProgressionField | undefined {
305
+ return this.rawMetadata?.page_progression
306
+ ? new PageProgressionField(this.rawMetadata.page_progression)
307
+ : undefined;
308
+ }
309
+
310
+ @Memoize() get partner(): StringField | undefined {
311
+ return this.rawMetadata?.partner
312
+ ? new StringField(this.rawMetadata.partner)
313
+ : undefined;
314
+ }
315
+
316
+ @Memoize() get ppi(): NumberField | undefined {
317
+ return this.rawMetadata?.ppi
318
+ ? new NumberField(this.rawMetadata.ppi)
319
+ : undefined;
320
+ }
321
+
322
+ @Memoize() get publicdate(): DateField | undefined {
323
+ return this.rawMetadata?.publicdate
324
+ ? new DateField(this.rawMetadata.publicdate)
325
+ : undefined;
326
+ }
327
+
328
+ @Memoize() get publisher(): StringField | undefined {
329
+ return this.rawMetadata?.publisher
330
+ ? new StringField(this.rawMetadata.publisher)
331
+ : undefined;
332
+ }
333
+
334
+ @Memoize() get reviewdate(): DateField | undefined {
335
+ return this.rawMetadata?.reviewdate
336
+ ? new DateField(this.rawMetadata.reviewdate)
337
+ : undefined;
338
+ }
339
+
340
+ @Memoize() get runtime(): DurationField | undefined {
341
+ return this.rawMetadata?.runtime
342
+ ? new DurationField(this.rawMetadata.runtime)
343
+ : undefined;
344
+ }
345
+
346
+ @Memoize() get scanner(): StringField | undefined {
347
+ return this.rawMetadata?.scanner
348
+ ? new StringField(this.rawMetadata.scanner)
349
+ : undefined;
350
+ }
351
+
352
+ @Memoize() get source(): StringField | undefined {
353
+ return this.rawMetadata?.source
354
+ ? new StringField(this.rawMetadata.source)
355
+ : undefined;
356
+ }
357
+
358
+ @Memoize() get start_localtime(): DateField | undefined {
359
+ return this.rawMetadata?.start_localtime
360
+ ? new DateField(this.rawMetadata.start_localtime)
361
+ : undefined;
362
+ }
363
+
364
+ @Memoize() get start_time(): DateField | undefined {
365
+ return this.rawMetadata?.start_time
366
+ ? new DateField(this.rawMetadata.start_time)
367
+ : undefined;
368
+ }
369
+
370
+ @Memoize() get stop_time(): DateField | undefined {
371
+ return this.rawMetadata?.stop_time
372
+ ? new DateField(this.rawMetadata.stop_time)
373
+ : undefined;
374
+ }
375
+
376
+ @Memoize() get subject(): StringListField | undefined {
377
+ return this.rawMetadata?.subject
378
+ ? new StringListField(this.rawMetadata.subject)
379
+ : undefined;
380
+ }
381
+
382
+ @Memoize() get taper(): StringField | undefined {
383
+ return this.rawMetadata?.taper
384
+ ? new StringField(this.rawMetadata.taper)
385
+ : undefined;
386
+ }
387
+
388
+ @Memoize() get title(): StringField | undefined {
389
+ return this.rawMetadata?.title
390
+ ? new StringField(this.rawMetadata.title)
391
+ : undefined;
392
+ }
393
+
394
+ @Memoize() get transferer(): StringField | undefined {
395
+ return this.rawMetadata?.transferer
396
+ ? new StringField(this.rawMetadata.transferer)
397
+ : undefined;
398
+ }
399
+
400
+ @Memoize() get track(): NumberField | undefined {
401
+ return this.rawMetadata?.track
402
+ ? new NumberField(this.rawMetadata.track)
403
+ : undefined;
404
+ }
405
+
406
+ @Memoize() get type(): StringField | undefined {
407
+ return this.rawMetadata?.type
408
+ ? new StringField(this.rawMetadata.type)
409
+ : undefined;
410
+ }
411
+
412
+ @Memoize() get uploader(): StringField | undefined {
413
+ return this.rawMetadata?.uploader
414
+ ? new StringField(this.rawMetadata.uploader)
415
+ : undefined;
416
+ }
417
+
418
+ @Memoize() get utc_offset(): NumberField | undefined {
419
+ return this.rawMetadata?.utc_offset
420
+ ? new NumberField(this.rawMetadata.utc_offset)
421
+ : undefined;
422
+ }
423
+
424
+ @Memoize() get venue(): StringField | undefined {
425
+ return this.rawMetadata?.venue
426
+ ? new StringField(this.rawMetadata.venue)
427
+ : undefined;
428
+ }
429
+
430
+ @Memoize() get volume(): StringField | undefined {
431
+ return this.rawMetadata?.volume
432
+ ? new StringField(this.rawMetadata.volume)
433
+ : undefined;
434
+ }
435
+
436
+ /**
437
+ * The number of downloads in the last week
438
+ *
439
+ * @type {NumberField}
440
+ * @memberof Metadata
441
+ */
442
+ @Memoize() get week(): NumberField | undefined {
443
+ return this.rawMetadata?.week
444
+ ? new NumberField(this.rawMetadata.week)
445
+ : undefined;
446
+ }
447
+
448
+ @Memoize() get year(): DateField | undefined {
449
+ return this.rawMetadata?.year
450
+ ? new DateField(this.rawMetadata.year)
451
+ : undefined;
452
+ }
453
+
454
+ constructor(json: Record<string, any>) {
455
+ this.rawMetadata = json;
456
+ }
457
+ }
@@ -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
+ });