confluent-schema-registry 3.3.2

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 (173) hide show
  1. package/.dockerignore +2 -0
  2. package/.prettierrc.js +8 -0
  3. package/CHANGELOG.md +166 -0
  4. package/Dockerfile +10 -0
  5. package/LICENSE +21 -0
  6. package/README.md +44 -0
  7. package/bin/avdlToAVSC.sh +9 -0
  8. package/dist/@types.d.ts +93 -0
  9. package/dist/@types.js +10 -0
  10. package/dist/@types.js.map +1 -0
  11. package/dist/AvroHelper.d.ts +12 -0
  12. package/dist/AvroHelper.js +67 -0
  13. package/dist/AvroHelper.js.map +1 -0
  14. package/dist/JsonHelper.d.ts +7 -0
  15. package/dist/JsonHelper.js +20 -0
  16. package/dist/JsonHelper.js.map +1 -0
  17. package/dist/JsonSchema.d.ts +31 -0
  18. package/dist/JsonSchema.js +58 -0
  19. package/dist/JsonSchema.js.map +1 -0
  20. package/dist/ProtoHelper.d.ts +7 -0
  21. package/dist/ProtoHelper.js +23 -0
  22. package/dist/ProtoHelper.js.map +1 -0
  23. package/dist/ProtoSchema.d.ts +14 -0
  24. package/dist/ProtoSchema.js +66 -0
  25. package/dist/ProtoSchema.js.map +1 -0
  26. package/dist/SchemaRegistry.d.ts +48 -0
  27. package/dist/SchemaRegistry.js +250 -0
  28. package/dist/SchemaRegistry.js.map +1 -0
  29. package/dist/api/index.d.ts +43 -0
  30. package/dist/api/index.js +90 -0
  31. package/dist/api/index.js.map +1 -0
  32. package/dist/api/middleware/confluentEncoderMiddleware.d.ts +3 -0
  33. package/dist/api/middleware/confluentEncoderMiddleware.js +31 -0
  34. package/dist/api/middleware/confluentEncoderMiddleware.js.map +1 -0
  35. package/dist/api/middleware/errorMiddleware.d.ts +3 -0
  36. package/dist/api/middleware/errorMiddleware.js +20 -0
  37. package/dist/api/middleware/errorMiddleware.js.map +1 -0
  38. package/dist/api/middleware/userAgent.d.ts +3 -0
  39. package/dist/api/middleware/userAgent.js +18 -0
  40. package/dist/api/middleware/userAgent.js.map +1 -0
  41. package/dist/cache.d.ts +20 -0
  42. package/dist/cache.js +24 -0
  43. package/dist/cache.js.map +1 -0
  44. package/dist/constants.d.ts +11 -0
  45. package/dist/constants.js +15 -0
  46. package/dist/constants.js.map +1 -0
  47. package/dist/errors.d.ts +14 -0
  48. package/dist/errors.js +26 -0
  49. package/dist/errors.js.map +1 -0
  50. package/dist/index.d.ts +4 -0
  51. package/dist/index.js +13 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/schemaTypeResolver.d.ts +4 -0
  54. package/dist/schemaTypeResolver.js +80 -0
  55. package/dist/schemaTypeResolver.js.map +1 -0
  56. package/dist/utils/avdlToAVSC.d.ts +2 -0
  57. package/dist/utils/avdlToAVSC.js +85 -0
  58. package/dist/utils/avdlToAVSC.js.map +1 -0
  59. package/dist/utils/index.d.ts +2 -0
  60. package/dist/utils/index.js +9 -0
  61. package/dist/utils/index.js.map +1 -0
  62. package/dist/utils/readAVSC.d.ts +3 -0
  63. package/dist/utils/readAVSC.js +33 -0
  64. package/dist/utils/readAVSC.js.map +1 -0
  65. package/dist/wireDecoder.d.ts +7 -0
  66. package/dist/wireDecoder.js +8 -0
  67. package/dist/wireDecoder.js.map +1 -0
  68. package/dist/wireEncoder.d.ts +3 -0
  69. package/dist/wireEncoder.js +10 -0
  70. package/dist/wireEncoder.js.map +1 -0
  71. package/dockest-error.json +11 -0
  72. package/dockest.ts +30 -0
  73. package/jest.setup.ts +60 -0
  74. package/package.json +56 -0
  75. package/release/CHANGELOG.md +166 -0
  76. package/release/LICENSE +21 -0
  77. package/release/README.md +44 -0
  78. package/release/dist/@types.d.ts +93 -0
  79. package/release/dist/@types.js +10 -0
  80. package/release/dist/@types.js.map +1 -0
  81. package/release/dist/AvroHelper.d.ts +12 -0
  82. package/release/dist/AvroHelper.js +67 -0
  83. package/release/dist/AvroHelper.js.map +1 -0
  84. package/release/dist/JsonHelper.d.ts +7 -0
  85. package/release/dist/JsonHelper.js +20 -0
  86. package/release/dist/JsonHelper.js.map +1 -0
  87. package/release/dist/JsonSchema.d.ts +31 -0
  88. package/release/dist/JsonSchema.js +58 -0
  89. package/release/dist/JsonSchema.js.map +1 -0
  90. package/release/dist/ProtoHelper.d.ts +7 -0
  91. package/release/dist/ProtoHelper.js +23 -0
  92. package/release/dist/ProtoHelper.js.map +1 -0
  93. package/release/dist/ProtoSchema.d.ts +14 -0
  94. package/release/dist/ProtoSchema.js +66 -0
  95. package/release/dist/ProtoSchema.js.map +1 -0
  96. package/release/dist/SchemaRegistry.d.ts +48 -0
  97. package/release/dist/SchemaRegistry.js +250 -0
  98. package/release/dist/SchemaRegistry.js.map +1 -0
  99. package/release/dist/api/index.d.ts +43 -0
  100. package/release/dist/api/index.js +90 -0
  101. package/release/dist/api/index.js.map +1 -0
  102. package/release/dist/api/middleware/confluentEncoderMiddleware.d.ts +3 -0
  103. package/release/dist/api/middleware/confluentEncoderMiddleware.js +31 -0
  104. package/release/dist/api/middleware/confluentEncoderMiddleware.js.map +1 -0
  105. package/release/dist/api/middleware/errorMiddleware.d.ts +3 -0
  106. package/release/dist/api/middleware/errorMiddleware.js +20 -0
  107. package/release/dist/api/middleware/errorMiddleware.js.map +1 -0
  108. package/release/dist/api/middleware/userAgent.d.ts +3 -0
  109. package/release/dist/api/middleware/userAgent.js +18 -0
  110. package/release/dist/api/middleware/userAgent.js.map +1 -0
  111. package/release/dist/cache.d.ts +20 -0
  112. package/release/dist/cache.js +24 -0
  113. package/release/dist/cache.js.map +1 -0
  114. package/release/dist/constants.d.ts +11 -0
  115. package/release/dist/constants.js +15 -0
  116. package/release/dist/constants.js.map +1 -0
  117. package/release/dist/errors.d.ts +14 -0
  118. package/release/dist/errors.js +26 -0
  119. package/release/dist/errors.js.map +1 -0
  120. package/release/dist/index.d.ts +4 -0
  121. package/release/dist/index.js +13 -0
  122. package/release/dist/index.js.map +1 -0
  123. package/release/dist/schemaTypeResolver.d.ts +4 -0
  124. package/release/dist/schemaTypeResolver.js +80 -0
  125. package/release/dist/schemaTypeResolver.js.map +1 -0
  126. package/release/dist/utils/avdlToAVSC.d.ts +2 -0
  127. package/release/dist/utils/avdlToAVSC.js +85 -0
  128. package/release/dist/utils/avdlToAVSC.js.map +1 -0
  129. package/release/dist/utils/index.d.ts +2 -0
  130. package/release/dist/utils/index.js +9 -0
  131. package/release/dist/utils/index.js.map +1 -0
  132. package/release/dist/utils/readAVSC.d.ts +3 -0
  133. package/release/dist/utils/readAVSC.js +33 -0
  134. package/release/dist/utils/readAVSC.js.map +1 -0
  135. package/release/dist/wireDecoder.d.ts +7 -0
  136. package/release/dist/wireDecoder.js +8 -0
  137. package/release/dist/wireDecoder.js.map +1 -0
  138. package/release/dist/wireEncoder.d.ts +3 -0
  139. package/release/dist/wireEncoder.js +10 -0
  140. package/release/dist/wireEncoder.js.map +1 -0
  141. package/release/package.json +56 -0
  142. package/src/@types.ts +105 -0
  143. package/src/AvroHelper.ts +91 -0
  144. package/src/JsonHelper.ts +35 -0
  145. package/src/JsonSchema.ts +80 -0
  146. package/src/ProtoHelper.ts +38 -0
  147. package/src/ProtoSchema.ts +80 -0
  148. package/src/SchemaRegistry.avro.spec.ts +558 -0
  149. package/src/SchemaRegistry.json.spec.ts +364 -0
  150. package/src/SchemaRegistry.newApi.spec.ts +622 -0
  151. package/src/SchemaRegistry.protobuf.spec.ts +372 -0
  152. package/src/SchemaRegistry.spec.ts +252 -0
  153. package/src/SchemaRegistry.ts +387 -0
  154. package/src/api/index.spec.ts +23 -0
  155. package/src/api/index.ts +121 -0
  156. package/src/api/middleware/confluentEncoderMiddleware.ts +36 -0
  157. package/src/api/middleware/errorMiddleware.spec.ts +67 -0
  158. package/src/api/middleware/errorMiddleware.ts +37 -0
  159. package/src/api/middleware/userAgent.spec.ts +53 -0
  160. package/src/api/middleware/userAgent.ts +19 -0
  161. package/src/cache.ts +34 -0
  162. package/src/constants.ts +13 -0
  163. package/src/errors.ts +26 -0
  164. package/src/index.ts +4 -0
  165. package/src/schemaTypeResolver.ts +101 -0
  166. package/src/utils/avdlToAVSC.spec.ts +79 -0
  167. package/src/utils/avdlToAVSC.ts +106 -0
  168. package/src/utils/index.ts +2 -0
  169. package/src/utils/readAVSC.spec.ts +23 -0
  170. package/src/utils/readAVSC.ts +36 -0
  171. package/src/wireDecoder.ts +5 -0
  172. package/src/wireEncoder.ts +10 -0
  173. package/tsconfig.json +22 -0
@@ -0,0 +1,364 @@
1
+ import SchemaRegistry, { RegisteredSchema } from './SchemaRegistry'
2
+ import API from './api'
3
+ import { JsonConfluentSchema, SchemaType } from './@types'
4
+
5
+ const REGISTRY_HOST = 'http://localhost:8982'
6
+ const schemaRegistryAPIClientArgs = { host: REGISTRY_HOST }
7
+ const schemaRegistryArgs = { host: REGISTRY_HOST }
8
+
9
+ const TestSchemas = {
10
+ ThirdLevelSchema: {
11
+ type: SchemaType.JSON,
12
+ schema: `
13
+ {
14
+ "$id": "https://example.com/schemas/ThirdLevel",
15
+ "type": "object",
16
+ "properties": {
17
+ "id3": { "type": "number" }
18
+ }
19
+ }
20
+ `,
21
+ } as JsonConfluentSchema,
22
+
23
+ SecondLevelASchema: {
24
+ type: SchemaType.JSON,
25
+ schema: `
26
+ {
27
+ "$id": "https://example.com/schemas/SecondLevelA",
28
+ "type": "object",
29
+ "properties": {
30
+ "id2a": { "type": "number" },
31
+ "level2a": { "$ref": "https://example.com/schemas/ThirdLevel" }
32
+ }
33
+ }
34
+ `,
35
+ references: [
36
+ {
37
+ name: 'https://example.com/schemas/ThirdLevel',
38
+ subject: 'JSON:ThirdLevel',
39
+ version: undefined,
40
+ },
41
+ ],
42
+ } as JsonConfluentSchema,
43
+
44
+ SecondLevelBSchema: {
45
+ type: SchemaType.JSON,
46
+ schema: `
47
+ {
48
+ "$id": "https://example.com/schemas/SecondLevelB",
49
+ "type": "object",
50
+ "properties": {
51
+ "id2b": { "type": "number" },
52
+ "level2b": { "$ref": "https://example.com/schemas/ThirdLevel" }
53
+ }
54
+ }
55
+ `,
56
+ references: [
57
+ {
58
+ name: 'https://example.com/schemas/ThirdLevel',
59
+ subject: 'JSON:ThirdLevel',
60
+ version: undefined,
61
+ },
62
+ ],
63
+ } as JsonConfluentSchema,
64
+
65
+ FirstLevelSchema: {
66
+ type: SchemaType.JSON,
67
+ schema: `
68
+ {
69
+ "$id": "https://example.com/schemas/FirstLevel",
70
+ "type": "object",
71
+ "properties": {
72
+ "id1": { "type": "number" },
73
+ "level1a": { "$ref": "https://example.com/schemas/SecondLevelA" },
74
+ "level1b": { "$ref": "https://example.com/schemas/SecondLevelB" }
75
+ }
76
+ }
77
+ `,
78
+ references: [
79
+ {
80
+ name: 'https://example.com/schemas/SecondLevelA',
81
+ subject: 'JSON:SecondLevelA',
82
+ version: undefined,
83
+ },
84
+ {
85
+ name: 'https://example.com/schemas/SecondLevelB',
86
+ subject: 'JSON:SecondLevelB',
87
+ version: undefined,
88
+ },
89
+ ],
90
+ } as JsonConfluentSchema,
91
+ }
92
+
93
+ function apiResponse(result) {
94
+ return JSON.parse(result.responseData)
95
+ }
96
+
97
+ describe('SchemaRegistry', () => {
98
+ let schemaRegistry: SchemaRegistry
99
+ let registeredSchema: RegisteredSchema
100
+ let api
101
+
102
+ beforeEach(async () => {
103
+ api = API(schemaRegistryAPIClientArgs)
104
+ schemaRegistry = new SchemaRegistry(schemaRegistryArgs)
105
+ })
106
+
107
+ describe('when register', () => {
108
+ describe('when no reference', () => {
109
+ beforeEach(async () => {
110
+ registeredSchema = await schemaRegistry.register(TestSchemas.ThirdLevelSchema, {
111
+ subject: 'JSON:ThirdLevel',
112
+ })
113
+ })
114
+ it('should return schema id', async () => {
115
+ expect(registeredSchema.id).toEqual(expect.any(Number))
116
+ })
117
+
118
+ it('should be able to encode/decode', async () => {
119
+ const obj = { id3: 3 }
120
+
121
+ const buffer = await schemaRegistry.encode(registeredSchema.id, obj)
122
+ const resultObj = await schemaRegistry.decode(buffer)
123
+
124
+ expect(resultObj).toEqual(obj)
125
+ })
126
+ })
127
+
128
+ describe('with reference', () => {
129
+ let schemaId
130
+ let referenceSchema
131
+
132
+ beforeEach(async () => {
133
+ await schemaRegistry.register(TestSchemas.ThirdLevelSchema, {
134
+ subject: 'JSON:ThirdLevel',
135
+ })
136
+
137
+ const latest = apiResponse(await api.Subject.latestVersion({ subject: 'JSON:ThirdLevel' }))
138
+ TestSchemas.SecondLevelASchema.references[0].version = latest.version
139
+ registeredSchema = await schemaRegistry.register(TestSchemas.SecondLevelASchema, {
140
+ subject: 'JSON:SecondLevelA',
141
+ })
142
+ schemaId = registeredSchema.id
143
+
144
+ const schemaRaw = apiResponse(await api.Schema.find({ id: schemaId }))
145
+ referenceSchema = schemaRaw.references[0].subject
146
+ })
147
+
148
+ it('should return schema id', async () => {
149
+ expect(schemaId).toEqual(expect.any(Number))
150
+ })
151
+
152
+ it('should create a schema with reference', async () => {
153
+ expect(referenceSchema).toEqual('JSON:ThirdLevel')
154
+ })
155
+
156
+ it('should be able to encode/decode', async () => {
157
+ const obj = { id2a: 2, level2a: { id3: 3 } }
158
+
159
+ const buffer = await schemaRegistry.encode(registeredSchema.id, obj)
160
+ const resultObj = await schemaRegistry.decode(buffer)
161
+
162
+ expect(resultObj).toEqual(obj)
163
+ })
164
+ })
165
+
166
+ describe('with multiple reference', () => {
167
+ beforeEach(async () => {
168
+ let latest
169
+
170
+ await schemaRegistry.register(TestSchemas.ThirdLevelSchema, {
171
+ subject: 'JSON:ThirdLevel',
172
+ })
173
+
174
+ latest = apiResponse(await api.Subject.latestVersion({ subject: 'JSON:ThirdLevel' }))
175
+ TestSchemas.SecondLevelASchema.references[0].version = latest.version
176
+ registeredSchema = await schemaRegistry.register(TestSchemas.SecondLevelASchema, {
177
+ subject: 'JSON:SecondLevelA',
178
+ })
179
+
180
+ latest = apiResponse(await api.Subject.latestVersion({ subject: 'JSON:ThirdLevel' }))
181
+ TestSchemas.SecondLevelBSchema.references[0].version = latest.version
182
+ registeredSchema = await schemaRegistry.register(TestSchemas.SecondLevelBSchema, {
183
+ subject: 'JSON:SecondLevelB',
184
+ })
185
+
186
+ latest = apiResponse(await api.Subject.latestVersion({ subject: 'JSON:SecondLevelA' }))
187
+ TestSchemas.FirstLevelSchema.references[0].version = latest.version
188
+ latest = apiResponse(await api.Subject.latestVersion({ subject: 'JSON:SecondLevelB' }))
189
+ TestSchemas.FirstLevelSchema.references[1].version = latest.version
190
+ registeredSchema = await schemaRegistry.register(TestSchemas.FirstLevelSchema, {
191
+ subject: 'JSON:FirstLevel',
192
+ })
193
+ })
194
+
195
+ it('should be able to encode/decode', async () => {
196
+ const obj = {
197
+ id1: 1,
198
+ level1a: { id2a: 2, level2a: { id3: 3 } },
199
+ level1b: { id2b: 4, level2b: { id3: 5 } },
200
+ }
201
+
202
+ const buffer = await schemaRegistry.encode(registeredSchema.id, obj)
203
+ const resultObj = await schemaRegistry.decode(buffer)
204
+
205
+ expect(resultObj).toEqual(obj)
206
+ })
207
+
208
+ it('should be able to encode/decode independent', async () => {
209
+ const obj = {
210
+ id1: 1,
211
+ level1a: { id2a: 2, level2a: { id3: 3 } },
212
+ level1b: { id2b: 4, level2b: { id3: 5 } },
213
+ }
214
+
215
+ schemaRegistry = new SchemaRegistry(schemaRegistryArgs)
216
+ const buffer = await schemaRegistry.encode(registeredSchema.id, obj)
217
+
218
+ schemaRegistry = new SchemaRegistry(schemaRegistryArgs)
219
+ const resultObj = await schemaRegistry.decode(buffer)
220
+
221
+ expect(resultObj).toEqual(obj)
222
+ })
223
+ })
224
+ })
225
+
226
+ describe('_getSchema', () => {
227
+ let schema
228
+
229
+ describe('no references', () => {
230
+ beforeEach(async () => {
231
+ registeredSchema = await schemaRegistry.register(TestSchemas.ThirdLevelSchema, {
232
+ subject: 'JSON:ThirdLevel',
233
+ })
234
+ ;({ schema } = await schemaRegistry['_getSchema'](registeredSchema.id))
235
+ })
236
+
237
+ it('should be able to encode/decode', async () => {
238
+ const obj = { id3: 3 }
239
+
240
+ const buffer = await schema.toBuffer(obj)
241
+ const resultObj = await schema.fromBuffer(buffer)
242
+
243
+ expect(resultObj).toEqual(obj)
244
+ })
245
+ })
246
+
247
+ describe('with references', () => {
248
+ beforeEach(async () => {
249
+ await schemaRegistry.register(TestSchemas.ThirdLevelSchema, { subject: 'JSON:ThirdLevel' })
250
+
251
+ const latest = apiResponse(await api.Subject.latestVersion({ subject: 'JSON:ThirdLevel' }))
252
+ TestSchemas.SecondLevelASchema.references[0].version = latest.version
253
+ registeredSchema = await schemaRegistry.register(TestSchemas.SecondLevelASchema, {
254
+ subject: 'JSON:SecondLevelA',
255
+ })
256
+ ;({ schema } = await schemaRegistry['_getSchema'](registeredSchema.id))
257
+ })
258
+
259
+ it('should be able to encode/decode', async () => {
260
+ const obj = { id2a: 2, level2a: { id3: 3 } }
261
+
262
+ const buffer = await schema.toBuffer(obj)
263
+ const resultObj = await schema.fromBuffer(buffer)
264
+
265
+ expect(resultObj).toEqual(obj)
266
+ })
267
+ })
268
+
269
+ describe('with multi references', () => {
270
+ beforeEach(async () => {
271
+ let latest
272
+
273
+ await schemaRegistry.register(TestSchemas.ThirdLevelSchema, {
274
+ subject: 'JSON:ThirdLevel',
275
+ })
276
+
277
+ latest = apiResponse(await api.Subject.latestVersion({ subject: 'JSON:ThirdLevel' }))
278
+ TestSchemas.SecondLevelASchema.references[0].version = latest.version
279
+ registeredSchema = await schemaRegistry.register(TestSchemas.SecondLevelASchema, {
280
+ subject: 'JSON:SecondLevelA',
281
+ })
282
+
283
+ latest = apiResponse(await api.Subject.latestVersion({ subject: 'JSON:ThirdLevel' }))
284
+ TestSchemas.SecondLevelBSchema.references[0].version = latest.version
285
+ registeredSchema = await schemaRegistry.register(TestSchemas.SecondLevelBSchema, {
286
+ subject: 'JSON:SecondLevelB',
287
+ })
288
+
289
+ latest = apiResponse(await api.Subject.latestVersion({ subject: 'JSON:SecondLevelA' }))
290
+ TestSchemas.FirstLevelSchema.references[0].version = latest.version
291
+ latest = apiResponse(await api.Subject.latestVersion({ subject: 'JSON:SecondLevelB' }))
292
+ TestSchemas.FirstLevelSchema.references[1].version = latest.version
293
+ registeredSchema = await schemaRegistry.register(TestSchemas.FirstLevelSchema, {
294
+ subject: 'JSON:FirstLevel',
295
+ })
296
+ ;({ schema } = await schemaRegistry['_getSchema'](registeredSchema.id))
297
+ })
298
+
299
+ it('should be able to encode/decode', async () => {
300
+ const obj = {
301
+ id1: 1,
302
+ level1a: { id2a: 2, level2a: { id3: 3 } },
303
+ level1b: { id2b: 4, level2b: { id3: 5 } },
304
+ }
305
+
306
+ const buffer = await schema.toBuffer(obj)
307
+ const resultObj = await schema.fromBuffer(buffer)
308
+
309
+ expect(resultObj).toEqual(obj)
310
+ })
311
+ })
312
+ })
313
+
314
+ describe('when document example', () => {
315
+ it('should encode/decode', async () => {
316
+ const schemaA = {
317
+ $id: 'https://example.com/schemas/A',
318
+ type: 'object',
319
+ properties: {
320
+ id: { type: 'number' },
321
+ b: { $ref: 'https://example.com/schemas/B' },
322
+ },
323
+ }
324
+
325
+ const schemaB = {
326
+ $id: 'https://example.com/schemas/B',
327
+ type: 'object',
328
+ properties: {
329
+ id: { type: 'number' },
330
+ },
331
+ }
332
+
333
+ await schemaRegistry.register(
334
+ { type: SchemaType.JSON, schema: JSON.stringify(schemaB) },
335
+ { subject: 'JSON:B' },
336
+ )
337
+
338
+ const response = await schemaRegistry.api.Subject.latestVersion({ subject: 'JSON:B' })
339
+ const { version } = JSON.parse(response.responseData)
340
+
341
+ const { id } = await schemaRegistry.register(
342
+ {
343
+ type: SchemaType.JSON,
344
+ schema: JSON.stringify(schemaA),
345
+ references: [
346
+ {
347
+ name: 'https://example.com/schemas/B',
348
+ subject: 'JSON:B',
349
+ version,
350
+ },
351
+ ],
352
+ },
353
+ { subject: 'JSON:A' },
354
+ )
355
+
356
+ const obj = { id: 1, b: { id: 2 } }
357
+
358
+ const buffer = await schemaRegistry.encode(id, obj)
359
+ const decodedObj = await schemaRegistry.decode(buffer)
360
+
361
+ expect(decodedObj).toEqual(obj)
362
+ })
363
+ })
364
+ })