@jskit-ai/json-rest-api-core 0.1.29 → 0.1.31

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.
@@ -1,7 +1,7 @@
1
1
  export default Object.freeze({
2
2
  packageVersion: 1,
3
3
  packageId: "@jskit-ai/json-rest-api-core",
4
- version: "0.1.29",
4
+ version: "0.1.31",
5
5
  kind: "runtime",
6
6
  description: "Shared internal json-rest-api host runtime for JSKIT server packages.",
7
7
  dependsOn: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/json-rest-api-core",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -11,6 +11,6 @@
11
11
  "dependencies": {
12
12
  "hooked-api": "1.x.x",
13
13
  "json-rest-api": "1.x.x",
14
- "@jskit-ai/kernel": "0.1.84"
14
+ "@jskit-ai/kernel": "0.1.86"
15
15
  }
16
16
  }
@@ -1,6 +1,7 @@
1
1
  import { Api } from "hooked-api";
2
2
  import { AutoFilterPlugin, RestApiKnexPlugin, RestApiPlugin } from "json-rest-api";
3
3
  import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
4
+ import { resolveCrudResourceScopeName } from "@jskit-ai/kernel/shared/support/crudLookup";
4
5
 
5
6
  const INTERNAL_JSON_REST_API = "internal.json-rest-api";
6
7
 
@@ -79,6 +80,12 @@ function cloneJsonRestResourceValue(value, { writeSerializers = {} } = {}) {
79
80
  delete next.storage.writeSerializer;
80
81
  }
81
82
  }
83
+ if (
84
+ isPlainJsonRestObject(next.relation) &&
85
+ normalizeJsonRestText(next.relation.kind).toLowerCase() === "collection"
86
+ ) {
87
+ next.virtual = true;
88
+ }
82
89
 
83
90
  return next;
84
91
  }
@@ -144,6 +151,38 @@ function normalizeJsonRestList(value) {
144
151
  .filter(Boolean);
145
152
  }
146
153
 
154
+ function resolveJsonRestCollectionRelationships(resource = {}) {
155
+ const resourceSchema = normalizeJsonRestObject(resource?.schema);
156
+ const relationships = {};
157
+
158
+ for (const [fieldName, fieldDefinition] of Object.entries(resourceSchema)) {
159
+ const normalizedFieldDefinition = normalizeJsonRestObject(fieldDefinition);
160
+ const relation = normalizeJsonRestObject(normalizedFieldDefinition.relation);
161
+ if (normalizeJsonRestText(relation.kind).toLowerCase() !== "collection") {
162
+ continue;
163
+ }
164
+
165
+ const relationshipName = normalizeJsonRestText(relation.as || normalizedFieldDefinition.as, {
166
+ fallback: fieldName
167
+ });
168
+ const target = resolveCrudResourceScopeName(
169
+ relation.target || relation.targetResource || relation.namespace || relation.apiPath
170
+ );
171
+ const foreignKey = normalizeJsonRestText(relation.foreignKey);
172
+ if (!relationshipName || !target || !foreignKey) {
173
+ continue;
174
+ }
175
+
176
+ relationships[relationshipName] = {
177
+ type: "hasMany",
178
+ target,
179
+ foreignKey
180
+ };
181
+ }
182
+
183
+ return relationships;
184
+ }
185
+
147
186
  function buildJsonRestQueryParams(resourceType = "", query = {}, { include = undefined } = {}) {
148
187
  const normalizedResourceType = normalizeJsonRestText(resourceType);
149
188
  const source = normalizeJsonRestObject(query);
@@ -292,6 +331,23 @@ function createJsonRestResourceScopeOptions(resource = {}, { writeSerializers =
292
331
  const scopeOptions = cloneJsonRestResourceValue(resource, {
293
332
  writeSerializers: normalizeJsonRestObject(writeSerializers)
294
333
  });
334
+ const collectionRelationships = resolveJsonRestCollectionRelationships(scopeOptions);
335
+ if (Object.keys(collectionRelationships).length > 0) {
336
+ if (
337
+ scopeOptions.relationships !== undefined &&
338
+ scopeOptions.relationships !== null &&
339
+ !isPlainJsonRestObject(scopeOptions.relationships)
340
+ ) {
341
+ throw new TypeError(
342
+ "json-rest-api resource relationships must be an object when collection relations are derived."
343
+ );
344
+ }
345
+
346
+ scopeOptions.relationships = {
347
+ ...collectionRelationships,
348
+ ...normalizeJsonRestObject(scopeOptions.relationships)
349
+ };
350
+ }
295
351
 
296
352
  if (typeof normalizeId === "function") {
297
353
  scopeOptions.normalizeId = normalizeId;
@@ -290,6 +290,26 @@ test("createJsonRestResourceScopeOptions clones canonical resource metadata and
290
290
  required: false
291
291
  })
292
292
  })
293
+ }),
294
+ pets: Object.freeze({
295
+ type: "array",
296
+ relation: Object.freeze({
297
+ kind: "collection",
298
+ namespace: "pets",
299
+ foreignKey: "contactId"
300
+ }),
301
+ operations: Object.freeze({
302
+ output: Object.freeze({
303
+ required: false
304
+ })
305
+ })
306
+ })
307
+ }),
308
+ relationships: Object.freeze({
309
+ auditEvents: Object.freeze({
310
+ type: "hasMany",
311
+ target: "auditEvents",
312
+ foreignKey: "contactId"
293
313
  })
294
314
  }),
295
315
  operations: Object.freeze({
@@ -314,10 +334,23 @@ test("createJsonRestResourceScopeOptions clones canonical resource metadata and
314
334
  assert.equal(result.schema.createdAt.storage.serialize(null), null);
315
335
  assert.equal(result.schema.createdAt.storage.writeSerializer, undefined);
316
336
  assert.equal(result.schema.bookingSteps.virtual, true);
337
+ assert.equal(result.schema.pets.virtual, true);
317
338
  assert.equal(result.normalizeId, normalizeId);
318
339
  assert.equal(result.schema.name.maxLength, 190);
319
340
  assert.equal(result.schema.name.operations.output.required, true);
320
341
  assert.equal(result.operations.view.method, "GET");
342
+ assert.deepEqual(result.relationships, {
343
+ pets: {
344
+ type: "hasMany",
345
+ target: "pets",
346
+ foreignKey: "contactId"
347
+ },
348
+ auditEvents: {
349
+ type: "hasMany",
350
+ target: "auditEvents",
351
+ foreignKey: "contactId"
352
+ }
353
+ });
321
354
 
322
355
  result.schema.createdAt.indexed = true;
323
356
  assert.equal(source.schema.createdAt.indexed, undefined);