json-rest-api 1.0.13 → 1.0.15

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 (118) hide show
  1. package/connectors_ideas/fastify.js +66 -68
  2. package/connectors_ideas/koa-plugin.js +50 -51
  3. package/eslint.config.js +13 -0
  4. package/examples/url-override-example.js +63 -63
  5. package/index.js +13 -13
  6. package/lib/enhanced-logger.js +51 -51
  7. package/lib/error-formatter.js +61 -61
  8. package/lib/rest-api-errors.js +28 -28
  9. package/package.json +9 -5
  10. package/plugins/core/bulk-operations-plugin.js +142 -145
  11. package/plugins/core/connectors/express-plugin.js +210 -217
  12. package/plugins/core/connectors/lib/busboy-detector.js +84 -84
  13. package/plugins/core/connectors/lib/formidable-detector.js +50 -50
  14. package/plugins/core/connectors/lib/request-helpers.js +74 -74
  15. package/plugins/core/file-handling-plugin.js +125 -125
  16. package/plugins/core/lib/anyapi/anyapi-registry.js +254 -254
  17. package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +287 -287
  18. package/plugins/core/lib/anyapi/schema-utils.js +101 -101
  19. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +41 -41
  20. package/plugins/core/lib/dbTablesOperations.js +134 -135
  21. package/plugins/core/lib/fractional-positioning.js +74 -76
  22. package/plugins/core/lib/querying/knex-cross-table-search.js +320 -320
  23. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +144 -144
  24. package/plugins/core/lib/querying/knex-pagination-helpers.js +164 -164
  25. package/plugins/core/lib/querying/knex-process-includes.js +50 -50
  26. package/plugins/core/lib/querying/knex-query-helpers-base.js +31 -31
  27. package/plugins/core/lib/querying/knex-query-helpers.js +451 -452
  28. package/plugins/core/lib/querying/knex-relationship-includes.js +1160 -1170
  29. package/plugins/core/lib/querying/knex-window-queries.js +89 -89
  30. package/plugins/core/lib/querying/url-helpers.js +35 -35
  31. package/plugins/core/lib/querying-writing/compile-schemas.js +97 -98
  32. package/plugins/core/lib/querying-writing/connectors-query-parser.js +31 -31
  33. package/plugins/core/lib/querying-writing/database-capabilities.js +94 -94
  34. package/plugins/core/lib/querying-writing/database-value-normalizers.js +76 -76
  35. package/plugins/core/lib/querying-writing/default-data-helpers.js +27 -27
  36. package/plugins/core/lib/querying-writing/field-utils.js +44 -42
  37. package/plugins/core/lib/querying-writing/knex-constants.js +15 -16
  38. package/plugins/core/lib/querying-writing/knex-field-helpers.js +117 -115
  39. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +41 -41
  40. package/plugins/core/lib/querying-writing/payload-validators.js +275 -275
  41. package/plugins/core/lib/querying-writing/schema-helpers.js +129 -129
  42. package/plugins/core/lib/querying-writing/scope-validations.js +143 -146
  43. package/plugins/core/lib/querying-writing/simplified-helpers.js +133 -129
  44. package/plugins/core/lib/storage/storage-adapter.js +68 -68
  45. package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +31 -31
  46. package/plugins/core/lib/writing/many-to-many-manipulations.js +81 -81
  47. package/plugins/core/lib/writing/relationship-processor.js +58 -58
  48. package/plugins/core/rest-api-access.js +8 -8
  49. package/plugins/core/rest-api-anyapi-knex-plugin.js +920 -920
  50. package/plugins/core/rest-api-cors-plugin.js +170 -171
  51. package/plugins/core/rest-api-knex-plugin.js +482 -493
  52. package/plugins/core/rest-api-multihome-plugin.js +247 -251
  53. package/plugins/core/rest-api-plugin-hooks/compile-resource-schemas.js +5 -5
  54. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +20 -21
  55. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +44 -44
  56. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +23 -23
  57. package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +15 -15
  58. package/plugins/core/rest-api-plugin-methods/add-route.js +9 -9
  59. package/plugins/core/rest-api-plugin-methods/check-permissions.js +5 -6
  60. package/plugins/core/rest-api-plugin-methods/common.js +295 -297
  61. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +35 -37
  62. package/plugins/core/rest-api-plugin-methods/delete.js +57 -59
  63. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +172 -171
  64. package/plugins/core/rest-api-plugin-methods/get-related.js +96 -96
  65. package/plugins/core/rest-api-plugin-methods/get-relationship.js +16 -18
  66. package/plugins/core/rest-api-plugin-methods/get.js +86 -86
  67. package/plugins/core/rest-api-plugin-methods/patch-relationship.js +16 -17
  68. package/plugins/core/rest-api-plugin-methods/patch.js +63 -64
  69. package/plugins/core/rest-api-plugin-methods/post-relationship.js +54 -55
  70. package/plugins/core/rest-api-plugin-methods/post.js +61 -62
  71. package/plugins/core/rest-api-plugin-methods/put.js +86 -87
  72. package/plugins/core/rest-api-plugin-methods/query.js +88 -88
  73. package/plugins/core/rest-api-plugin-methods/release.js +5 -6
  74. package/plugins/core/rest-api-plugin.js +76 -78
  75. package/plugins/core/rest-api-positioning-plugin.js +251 -252
  76. package/plugins/core/socketio-plugin.js +240 -236
  77. package/plugins/storage/local-storage.js +103 -103
  78. package/plugins/storage/s3-storage.js +41 -42
  79. package/quickTest.js +37 -37
  80. package/scripts/debug-anyapi-review-filter.mjs +18 -18
  81. package/scripts/inspect-anyapi-descriptor-country.mjs +10 -10
  82. package/scripts/inspect-anyapi-descriptor.mjs +10 -10
  83. package/scripts/run-docs.js +112 -113
  84. package/test-debug.js +28 -28
  85. package/tests/TEST_TEMPLATE.test.js +51 -51
  86. package/tests/anyapi-basic.test.js +42 -42
  87. package/tests/anyapi-cursor-pagination.test.js +48 -48
  88. package/tests/bulk-operations.test.js +208 -208
  89. package/tests/computed-fields.test.js +204 -204
  90. package/tests/cors.test.js +412 -413
  91. package/tests/curl.test.js +275 -276
  92. package/tests/custom-idproperty-relationships.test.js +266 -266
  93. package/tests/field-getters.test.js +132 -132
  94. package/tests/field-setters.test.js +110 -110
  95. package/tests/fixtures/api-configs.js +699 -699
  96. package/tests/helpers/storage-mode.js +32 -32
  97. package/tests/helpers/test-utils.js +76 -76
  98. package/tests/include-depth-validation.test.js +76 -76
  99. package/tests/includes.test.js +145 -145
  100. package/tests/multihome.test.js +175 -175
  101. package/tests/nested-includes.test.js +255 -255
  102. package/tests/pagination-cursor-multifield.test.js +164 -164
  103. package/tests/pagination-enhanced.test.js +225 -225
  104. package/tests/polymorphic-relationships.test.js +96 -96
  105. package/tests/polymorphic-simple.test.js +44 -44
  106. package/tests/positioning-advanced.test.js +226 -226
  107. package/tests/positioning-extended.test.js +399 -399
  108. package/tests/positioning-multifilter.test.js +204 -204
  109. package/tests/positioning.test.js +266 -266
  110. package/tests/queries.test.js +184 -184
  111. package/tests/query-limits.test.js +154 -155
  112. package/tests/relationship-endpoints.test.js +127 -127
  113. package/tests/relationships.test.js +178 -179
  114. package/tests/rest-api-access.test.js +86 -86
  115. package/tests/rest-api.test.js +103 -103
  116. package/tests/searchschema-merge.test.js +93 -93
  117. package/tests/socketio.test.js +326 -326
  118. package/tests/virtual-fields.test.js +164 -164
@@ -1,7 +1,7 @@
1
- import { describe, it, before, beforeEach, after } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import knexLib from 'knex';
4
- import { createCustomIdPropertyApi } from './fixtures/api-configs.js';
1
+ import { describe, it, before, beforeEach, after } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import knexLib from 'knex'
4
+ import { createCustomIdPropertyApi } from './fixtures/api-configs.js'
5
5
  import {
6
6
  validateJsonApiStructure,
7
7
  resourceIdentifier,
@@ -12,8 +12,8 @@ import {
12
12
  createToManyRelationship,
13
13
  assertResourceAttributes,
14
14
  assertResourceRelationship
15
- } from './helpers/test-utils.js';
16
- import { storageMode } from './helpers/storage-mode.js';
15
+ } from './helpers/test-utils.js'
16
+ import { storageMode } from './helpers/storage-mode.js'
17
17
 
18
18
  // Create Knex instance for tests
19
19
  const knex = knexLib({
@@ -22,59 +22,59 @@ const knex = knexLib({
22
22
  filename: ':memory:'
23
23
  },
24
24
  useNullAsDefault: true
25
- });
25
+ })
26
26
 
27
27
  // API instance that persists across tests
28
- let api;
28
+ let api
29
29
 
30
30
  describe('Custom idProperty Relationship Operations', () => {
31
31
  before(async () => {
32
32
  // Initialize API with custom idProperty for all resources
33
- api = await createCustomIdPropertyApi(knex);
34
- });
33
+ api = await createCustomIdPropertyApi(knex)
34
+ })
35
35
 
36
36
  after(async () => {
37
- await knex.destroy();
38
- });
37
+ await knex.destroy()
38
+ })
39
39
 
40
40
  describe('Basic CRUD with custom idProperty', () => {
41
41
  beforeEach(async () => {
42
42
  await cleanTables(knex, [
43
- 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
43
+ 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
44
44
  'custom_id_books', 'custom_id_book_authors', 'custom_id_reviews'
45
- ]);
46
- });
45
+ ])
46
+ })
47
47
 
48
48
  it('should create and retrieve resources with custom ID fields', async () => {
49
49
  // Create country
50
50
  const countryDoc = createJsonApiDocument('countries', {
51
51
  name: 'United States',
52
52
  code: 'US'
53
- });
53
+ })
54
54
 
55
55
  const countryResult = await api.resources.countries.post({
56
56
  inputRecord: countryDoc
57
- });
57
+ })
58
58
 
59
59
  // Verify response has 'id' not 'country_id'
60
- assert(countryResult.data.id, 'Should have id field');
61
- assert(!countryResult.data.attributes.country_id, 'Should not expose country_id in attributes');
60
+ assert(countryResult.data.id, 'Should have id field')
61
+ assert(!countryResult.data.attributes.country_id, 'Should not expose country_id in attributes')
62
62
 
63
63
  // Verify database has correct column when using legacy storage
64
64
  if (!storageMode.isAnyApi()) {
65
- const dbCountry = await knex('custom_id_countries').first();
66
- assert(dbCountry.country_id, 'Database should have country_id column');
67
- assert.equal(String(dbCountry.country_id), countryResult.data.id);
65
+ const dbCountry = await knex('custom_id_countries').first()
66
+ assert(dbCountry.country_id, 'Database should have country_id column')
67
+ assert.equal(String(dbCountry.country_id), countryResult.data.id)
68
68
  }
69
69
 
70
70
  // Test GET
71
71
  const getResult = await api.resources.countries.get({
72
72
  id: countryResult.data.id
73
- });
73
+ })
74
74
 
75
- assert.equal(getResult.data.id, countryResult.data.id);
76
- assert.equal(getResult.data.attributes.name, 'United States');
77
- });
75
+ assert.equal(getResult.data.id, countryResult.data.id)
76
+ assert.equal(getResult.data.attributes.name, 'United States')
77
+ })
78
78
 
79
79
  it('should handle PATCH updates returning full record', async () => {
80
80
  const country = await api.resources.countries.post({
@@ -82,7 +82,7 @@ describe('Custom idProperty Relationship Operations', () => {
82
82
  name: 'United States',
83
83
  code: 'US'
84
84
  })
85
- });
85
+ })
86
86
 
87
87
  const patchResult = await api.resources.countries.patch({
88
88
  inputRecord: {
@@ -94,33 +94,33 @@ describe('Custom idProperty Relationship Operations', () => {
94
94
  }
95
95
  }
96
96
  }
97
- });
97
+ })
98
98
 
99
99
  // With returnRecordApi.patch = 'full', we should get the complete record
100
- validateJsonApiStructure(patchResult, false);
101
- assert.equal(patchResult.data.attributes.name, 'USA');
102
- assert.equal(patchResult.data.attributes.code, 'US'); // Original value preserved
103
- });
104
- });
100
+ validateJsonApiStructure(patchResult, false)
101
+ assert.equal(patchResult.data.attributes.name, 'USA')
102
+ assert.equal(patchResult.data.attributes.code, 'US') // Original value preserved
103
+ })
104
+ })
105
105
 
106
106
  describe('One-to-One (belongsTo) Relationships with custom idProperty', () => {
107
107
  beforeEach(async () => {
108
108
  await cleanTables(knex, [
109
- 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
109
+ 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
110
110
  'custom_id_books', 'custom_id_book_authors', 'custom_id_reviews'
111
- ]);
112
- });
111
+ ])
112
+ })
113
113
 
114
114
  it('should create resource with belongsTo relationship using custom IDs', async () => {
115
115
  // Create a country first
116
116
  const countryDoc = createJsonApiDocument('countries', {
117
117
  name: 'United States',
118
118
  code: 'US'
119
- });
119
+ })
120
120
 
121
121
  const countryResult = await api.resources.countries.post({
122
122
  inputRecord: countryDoc
123
- });
123
+ })
124
124
 
125
125
  // Create publisher with country relationship
126
126
  const publisherDoc = createJsonApiDocument('publishers',
@@ -130,22 +130,22 @@ describe('Custom idProperty Relationship Operations', () => {
130
130
  {
131
131
  country: createRelationship(resourceIdentifier('countries', countryResult.data.id))
132
132
  }
133
- );
133
+ )
134
134
 
135
135
  const publisherResult = await api.resources.publishers.post({
136
136
  inputRecord: publisherDoc
137
- });
137
+ })
138
138
 
139
139
  // Verify response
140
- validateJsonApiStructure(publisherResult, false);
141
- assert.equal(publisherResult.data.type, 'publishers');
140
+ validateJsonApiStructure(publisherResult, false)
141
+ assert.equal(publisherResult.data.type, 'publishers')
142
142
  assertResourceRelationship(publisherResult.data, 'country',
143
- resourceIdentifier('countries', countryResult.data.id));
143
+ resourceIdentifier('countries', countryResult.data.id))
144
144
 
145
145
  // Verify database foreign key uses country_id in legacy mode
146
146
  if (!storageMode.isAnyApi()) {
147
- const dbPublisher = await knex('custom_id_publishers').first();
148
- assert.equal(String(dbPublisher.country_id), countryResult.data.id);
147
+ const dbPublisher = await knex('custom_id_publishers').first()
148
+ assert.equal(String(dbPublisher.country_id), countryResult.data.id)
149
149
  }
150
150
 
151
151
  // Verify through GET with include
@@ -154,24 +154,24 @@ describe('Custom idProperty Relationship Operations', () => {
154
154
  queryParams: {
155
155
  include: ['country']
156
156
  }
157
- });
157
+ })
158
158
 
159
- validateJsonApiStructure(getResult, false);
160
- assert(getResult.included, 'Should have included data');
161
- assert.equal(getResult.included.length, 1);
162
- assert.equal(getResult.included[0].type, 'countries');
163
- assert.equal(getResult.included[0].attributes.name, 'United States');
164
- });
159
+ validateJsonApiStructure(getResult, false)
160
+ assert(getResult.included, 'Should have included data')
161
+ assert.equal(getResult.included.length, 1)
162
+ assert.equal(getResult.included[0].type, 'countries')
163
+ assert.equal(getResult.included[0].attributes.name, 'United States')
164
+ })
165
165
 
166
166
  it('should update belongsTo relationship via PATCH', async () => {
167
167
  // Create two countries
168
168
  const country1 = await api.resources.countries.post({
169
169
  inputRecord: createJsonApiDocument('countries', { name: 'USA', code: 'US' })
170
- });
170
+ })
171
171
 
172
172
  const country2 = await api.resources.countries.post({
173
173
  inputRecord: createJsonApiDocument('countries', { name: 'UK', code: 'GB' })
174
- });
174
+ })
175
175
 
176
176
  // Create publisher with first country
177
177
  const publisher = await api.resources.publishers.post({
@@ -179,7 +179,7 @@ describe('Custom idProperty Relationship Operations', () => {
179
179
  { name: 'Test Publisher' },
180
180
  { country: createRelationship(resourceIdentifier('countries', country1.data.id)) }
181
181
  )
182
- });
182
+ })
183
183
 
184
184
  // Update to second country
185
185
  const patchDoc = {
@@ -190,65 +190,65 @@ describe('Custom idProperty Relationship Operations', () => {
190
190
  country: createRelationship(resourceIdentifier('countries', country2.data.id))
191
191
  }
192
192
  }
193
- };
193
+ }
194
194
 
195
195
  const patchResult = await api.resources.publishers.patch({
196
196
  inputRecord: patchDoc
197
- });
197
+ })
198
198
 
199
199
  // Verify we get full record back
200
- assert.equal(patchResult.data.attributes.name, 'Test Publisher');
200
+ assert.equal(patchResult.data.attributes.name, 'Test Publisher')
201
201
  assertResourceRelationship(patchResult.data, 'country',
202
- resourceIdentifier('countries', country2.data.id));
202
+ resourceIdentifier('countries', country2.data.id))
203
203
 
204
204
  // Verify update via GET with include
205
205
  const updated = await api.resources.publishers.get({
206
206
  id: publisher.data.id,
207
207
  queryParams: { include: ['country'] }
208
- });
208
+ })
209
209
 
210
- assert.equal(updated.included[0].id, country2.data.id);
211
- assert.equal(updated.included[0].attributes.name, 'UK');
212
- });
213
- });
210
+ assert.equal(updated.included[0].id, country2.data.id)
211
+ assert.equal(updated.included[0].attributes.name, 'UK')
212
+ })
213
+ })
214
214
 
215
215
  describe('Many-to-Many Relationships with custom idProperty', () => {
216
- let countryId;
217
- let publisherId;
218
- let author1Id;
219
- let author2Id;
220
- let bookId;
216
+ let countryId
217
+ let publisherId
218
+ let author1Id
219
+ let author2Id
220
+ let bookId
221
221
 
222
222
  beforeEach(async () => {
223
223
  await cleanTables(knex, [
224
- 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
224
+ 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
225
225
  'custom_id_books', 'custom_id_book_authors', 'custom_id_reviews'
226
- ]);
226
+ ])
227
227
 
228
228
  // Setup test data
229
229
  const country = await api.resources.countries.post({
230
230
  inputRecord: createJsonApiDocument('countries', { name: 'USA', code: 'US' })
231
- });
232
- countryId = country.data.id;
231
+ })
232
+ countryId = country.data.id
233
233
 
234
234
  const publisher = await api.resources.publishers.post({
235
235
  inputRecord: createJsonApiDocument('publishers',
236
236
  { name: 'Test Publisher' },
237
237
  { country: createRelationship(resourceIdentifier('countries', countryId)) }
238
238
  )
239
- });
240
- publisherId = publisher.data.id;
239
+ })
240
+ publisherId = publisher.data.id
241
241
 
242
242
  const author1 = await api.resources.authors.post({
243
243
  inputRecord: createJsonApiDocument('authors', { name: 'Author One' })
244
- });
245
- author1Id = author1.data.id;
244
+ })
245
+ author1Id = author1.data.id
246
246
 
247
247
  const author2 = await api.resources.authors.post({
248
248
  inputRecord: createJsonApiDocument('authors', { name: 'Author Two' })
249
- });
250
- author2Id = author2.data.id;
251
- });
249
+ })
250
+ author2Id = author2.data.id
251
+ })
252
252
 
253
253
  it('should create many-to-many relationships with custom IDs', async () => {
254
254
  // Create book with multiple authors
@@ -262,40 +262,40 @@ describe('Custom idProperty Relationship Operations', () => {
262
262
  resourceIdentifier('authors', author2Id)
263
263
  ])
264
264
  }
265
- );
265
+ )
266
266
 
267
267
  const bookResult = await api.resources.books.post({
268
268
  inputRecord: bookDoc
269
- });
269
+ })
270
270
 
271
- bookId = bookResult.data.id;
271
+ bookId = bookResult.data.id
272
272
 
273
273
  // Verify pivot table has correct foreign keys
274
274
  if (!storageMode.isAnyApi()) {
275
- const pivotRecords = await knex('custom_id_book_authors').select('*');
276
- assert.equal(pivotRecords.length, 2);
275
+ const pivotRecords = await knex('custom_id_book_authors').select('*')
276
+ assert.equal(pivotRecords.length, 2)
277
277
 
278
- const bookIds = pivotRecords.map((r) => r.book_id);
279
- const authorIds = pivotRecords.map((r) => r.author_id).sort();
278
+ const bookIds = pivotRecords.map((r) => r.book_id)
279
+ const authorIds = pivotRecords.map((r) => r.author_id).sort()
280
280
 
281
- assert(bookIds.every((id) => String(id) === bookId));
282
- assert.deepEqual(authorIds.map(String).sort(), [author1Id, author2Id].sort());
281
+ assert(bookIds.every((id) => String(id) === bookId))
282
+ assert.deepEqual(authorIds.map(String).sort(), [author1Id, author2Id].sort())
283
283
  } else {
284
- const pivotCount = await countRecords(knex, 'custom_id_book_authors');
285
- assert.equal(pivotCount, 2);
284
+ const pivotCount = await countRecords(knex, 'custom_id_book_authors')
285
+ assert.equal(pivotCount, 2)
286
286
  }
287
287
 
288
288
  // Verify through GET with include
289
289
  const getResult = await api.resources.books.get({
290
290
  id: bookId,
291
291
  queryParams: { include: ['authors'] }
292
- });
292
+ })
293
293
 
294
- assert(getResult.included);
295
- assert.equal(getResult.included.length, 2);
296
- const includedNames = getResult.included.map(a => a.attributes.name).sort();
297
- assert.deepEqual(includedNames, ['Author One', 'Author Two']);
298
- });
294
+ assert(getResult.included)
295
+ assert.equal(getResult.included.length, 2)
296
+ const includedNames = getResult.included.map(a => a.attributes.name).sort()
297
+ assert.deepEqual(includedNames, ['Author One', 'Author Two'])
298
+ })
299
299
 
300
300
  it('should handle PATCH updates to many-to-many relationships', async () => {
301
301
  // Create book with one author
@@ -307,7 +307,7 @@ describe('Custom idProperty Relationship Operations', () => {
307
307
  authors: createToManyRelationship([resourceIdentifier('authors', author1Id)])
308
308
  }
309
309
  )
310
- });
310
+ })
311
311
 
312
312
  // Update to have both authors
313
313
  const patchDoc = {
@@ -321,65 +321,65 @@ describe('Custom idProperty Relationship Operations', () => {
321
321
  ])
322
322
  }
323
323
  }
324
- };
324
+ }
325
325
 
326
326
  const patchResult = await api.resources.books.patch({
327
327
  inputRecord: patchDoc,
328
328
  queryParams: {
329
329
  include: ['authors']
330
330
  }
331
- });
331
+ })
332
332
 
333
333
  // Verify full record returned
334
- assert.equal(patchResult.data.attributes.title, 'Test Book');
334
+ assert.equal(patchResult.data.attributes.title, 'Test Book')
335
335
  // With include, we should get the authors relationship data
336
336
  assertResourceRelationship(patchResult.data, 'authors',
337
- [resourceIdentifier('authors', author1Id), resourceIdentifier('authors', author2Id)]);
337
+ [resourceIdentifier('authors', author1Id), resourceIdentifier('authors', author2Id)])
338
338
 
339
339
  // Verify update
340
340
  const updated = await api.resources.books.get({
341
341
  id: book.data.id,
342
342
  queryParams: { include: ['authors'] }
343
- });
343
+ })
344
344
 
345
- assert.equal(updated.included.length, 2);
346
- });
347
- });
345
+ assert.equal(updated.included.length, 2)
346
+ })
347
+ })
348
348
 
349
349
  describe('Polymorphic Relationships with custom idProperty', () => {
350
- let bookId;
351
- let authorId;
352
- let publisherId;
350
+ let bookId
351
+ let authorId
352
+ let publisherId
353
353
 
354
354
  beforeEach(async () => {
355
355
  await cleanTables(knex, [
356
- 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
356
+ 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
357
357
  'custom_id_books', 'custom_id_book_authors', 'custom_id_reviews'
358
- ]);
358
+ ])
359
359
 
360
360
  // Create test resources
361
361
  const country = await api.resources.countries.post({
362
362
  inputRecord: createJsonApiDocument('countries', { name: 'USA', code: 'US' })
363
- });
363
+ })
364
364
 
365
365
  const book = await api.resources.books.post({
366
366
  inputRecord: createJsonApiDocument('books',
367
367
  { title: 'Test Book' },
368
368
  { country: createRelationship(resourceIdentifier('countries', country.data.id)) }
369
369
  )
370
- });
371
- bookId = book.data.id;
370
+ })
371
+ bookId = book.data.id
372
372
 
373
373
  const author = await api.resources.authors.post({
374
374
  inputRecord: createJsonApiDocument('authors', { name: 'Test Author' })
375
- });
376
- authorId = author.data.id;
375
+ })
376
+ authorId = author.data.id
377
377
 
378
378
  const publisher = await api.resources.publishers.post({
379
379
  inputRecord: createJsonApiDocument('publishers', { name: 'Test Publisher' })
380
- });
381
- publisherId = publisher.data.id;
382
- });
380
+ })
381
+ publisherId = publisher.data.id
382
+ })
383
383
 
384
384
  it('should create polymorphic review for book with custom IDs', async () => {
385
385
  const review = await api.resources.reviews.post({
@@ -391,19 +391,19 @@ describe('Custom idProperty Relationship Operations', () => {
391
391
  reviewable_type: 'books',
392
392
  reviewable_id: bookId
393
393
  })
394
- });
394
+ })
395
395
 
396
- validateJsonApiStructure(review);
397
- assert.equal(review.data.attributes.reviewable_type, 'books');
398
- assert.equal(String(review.data.attributes.reviewable_id), bookId);
396
+ validateJsonApiStructure(review)
397
+ assert.equal(review.data.attributes.reviewable_type, 'books')
398
+ assert.equal(String(review.data.attributes.reviewable_id), bookId)
399
399
 
400
400
  if (!storageMode.isAnyApi()) {
401
- const dbReview = await knex('custom_id_reviews').first();
402
- assert.equal(dbReview.reviewable_type, 'books');
403
- assert.equal(String(dbReview.reviewable_id), bookId);
404
- assert(dbReview.review_id, 'Should have custom review_id column');
401
+ const dbReview = await knex('custom_id_reviews').first()
402
+ assert.equal(dbReview.reviewable_type, 'books')
403
+ assert.equal(String(dbReview.reviewable_id), bookId)
404
+ assert(dbReview.review_id, 'Should have custom review_id column')
405
405
  }
406
- });
406
+ })
407
407
 
408
408
  it('should create polymorphic review for author with custom IDs', async () => {
409
409
  const review = await api.resources.reviews.post({
@@ -415,11 +415,11 @@ describe('Custom idProperty Relationship Operations', () => {
415
415
  reviewable_type: 'authors',
416
416
  reviewable_id: authorId
417
417
  })
418
- });
418
+ })
419
419
 
420
- assert.equal(review.data.attributes.reviewable_type, 'authors');
421
- assert.equal(String(review.data.attributes.reviewable_id), authorId);
422
- });
420
+ assert.equal(review.data.attributes.reviewable_type, 'authors')
421
+ assert.equal(String(review.data.attributes.reviewable_id), authorId)
422
+ })
423
423
 
424
424
  it('should query polymorphic reviews by type', async () => {
425
425
  // Create reviews for different types
@@ -432,7 +432,7 @@ describe('Custom idProperty Relationship Operations', () => {
432
432
  reviewable_type: 'books',
433
433
  reviewable_id: bookId
434
434
  })
435
- });
435
+ })
436
436
 
437
437
  await api.resources.reviews.post({
438
438
  inputRecord: createJsonApiDocument('reviews', {
@@ -443,43 +443,43 @@ describe('Custom idProperty Relationship Operations', () => {
443
443
  reviewable_type: 'authors',
444
444
  reviewable_id: authorId
445
445
  })
446
- });
446
+ })
447
447
 
448
448
  // Query only book reviews
449
449
  const bookReviews = await api.resources.reviews.query({
450
450
  queryParams: {
451
451
  filters: { reviewable_type: 'books' }
452
452
  }
453
- });
454
- assert.equal(bookReviews.data.length, 1, `Expected 1 book review, got ${bookReviews.data.length}`);
455
- assert.equal(bookReviews.data[0].attributes.reviewable_type, 'books');
456
- });
457
- });
453
+ })
454
+ assert.equal(bookReviews.data.length, 1, `Expected 1 book review, got ${bookReviews.data.length}`)
455
+ assert.equal(bookReviews.data[0].attributes.reviewable_type, 'books')
456
+ })
457
+ })
458
458
 
459
459
  describe('Nested Includes with custom idProperty', () => {
460
460
  beforeEach(async () => {
461
461
  await cleanTables(knex, [
462
- 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
462
+ 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
463
463
  'custom_id_books', 'custom_id_book_authors', 'custom_id_reviews'
464
- ]);
465
- });
464
+ ])
465
+ })
466
466
 
467
467
  it('should handle nested includes through custom ID relationships', async () => {
468
468
  // Create hierarchy: Country -> Publisher -> Book -> Authors
469
469
  const country = await api.resources.countries.post({
470
470
  inputRecord: createJsonApiDocument('countries', { name: 'USA', code: 'US' })
471
- });
471
+ })
472
472
 
473
473
  const publisher = await api.resources.publishers.post({
474
474
  inputRecord: createJsonApiDocument('publishers',
475
475
  { name: 'Test Publisher' },
476
476
  { country: createRelationship(resourceIdentifier('countries', country.data.id)) }
477
477
  )
478
- });
478
+ })
479
479
 
480
480
  const author = await api.resources.authors.post({
481
481
  inputRecord: createJsonApiDocument('authors', { name: 'Test Author' })
482
- });
482
+ })
483
483
 
484
484
  const book = await api.resources.books.post({
485
485
  inputRecord: createJsonApiDocument('books',
@@ -490,50 +490,50 @@ describe('Custom idProperty Relationship Operations', () => {
490
490
  authors: createToManyRelationship([resourceIdentifier('authors', author.data.id)])
491
491
  }
492
492
  )
493
- });
493
+ })
494
494
 
495
495
  // Query with nested includes
496
496
  const result = await api.resources.books.query({
497
497
  queryParams: {
498
498
  include: ['publisher.country', 'authors']
499
499
  }
500
- });
500
+ })
501
+
502
+ validateJsonApiStructure(result, true)
503
+ assert(result.included)
501
504
 
502
- validateJsonApiStructure(result, true);
503
- assert(result.included);
504
-
505
505
  // Find each type in included
506
- const includedPublisher = result.included.find(r => r.type === 'publishers');
507
- const includedCountry = result.included.find(r => r.type === 'countries');
508
- const includedAuthor = result.included.find(r => r.type === 'authors');
509
-
510
- assert(includedPublisher, 'Should include publisher');
511
- assert(includedCountry, 'Should include country');
512
- assert(includedAuthor, 'Should include author');
513
-
506
+ const includedPublisher = result.included.find(r => r.type === 'publishers')
507
+ const includedCountry = result.included.find(r => r.type === 'countries')
508
+ const includedAuthor = result.included.find(r => r.type === 'authors')
509
+
510
+ assert(includedPublisher, 'Should include publisher')
511
+ assert(includedCountry, 'Should include country')
512
+ assert(includedAuthor, 'Should include author')
513
+
514
514
  // Verify nested relationship
515
- assert.equal(includedPublisher.relationships.country.data.id, country.data.id);
516
- });
515
+ assert.equal(includedPublisher.relationships.country.data.id, country.data.id)
516
+ })
517
517
 
518
518
  it('should handle 3+ levels of nested includes', async () => {
519
519
  // Create deeper hierarchy
520
520
  const country = await api.resources.countries.post({
521
521
  inputRecord: createJsonApiDocument('countries', { name: 'USA', code: 'US' })
522
- });
522
+ })
523
523
 
524
524
  const author = await api.resources.authors.post({
525
- inputRecord: createJsonApiDocument('authors',
525
+ inputRecord: createJsonApiDocument('authors',
526
526
  { name: 'Test Author' },
527
527
  { country: createRelationship(resourceIdentifier('countries', country.data.id)) }
528
528
  )
529
- });
529
+ })
530
530
 
531
531
  const publisher = await api.resources.publishers.post({
532
532
  inputRecord: createJsonApiDocument('publishers',
533
533
  { name: 'Test Publisher' },
534
534
  { country: createRelationship(resourceIdentifier('countries', country.data.id)) }
535
535
  )
536
- });
536
+ })
537
537
 
538
538
  const book = await api.resources.books.post({
539
539
  inputRecord: createJsonApiDocument('books',
@@ -544,52 +544,52 @@ describe('Custom idProperty Relationship Operations', () => {
544
544
  authors: createToManyRelationship([resourceIdentifier('authors', author.data.id)])
545
545
  }
546
546
  )
547
- });
547
+ })
548
548
 
549
549
  // Query with 3 levels: books -> authors -> country
550
550
  const result = await api.resources.books.query({
551
551
  queryParams: {
552
552
  include: ['authors.country', 'publisher.country']
553
553
  }
554
- });
555
-
556
- validateJsonApiStructure(result, true);
557
- assert(result.included);
558
-
559
- const types = result.included.map(r => r.type);
560
- assert(types.includes('authors'), 'Should include authors');
561
- assert(types.includes('publishers'), 'Should include publishers');
562
- assert(types.includes('countries'), 'Should include countries');
563
- });
564
- });
554
+ })
555
+
556
+ validateJsonApiStructure(result, true)
557
+ assert(result.included)
558
+
559
+ const types = result.included.map(r => r.type)
560
+ assert(types.includes('authors'), 'Should include authors')
561
+ assert(types.includes('publishers'), 'Should include publishers')
562
+ assert(types.includes('countries'), 'Should include countries')
563
+ })
564
+ })
565
565
 
566
566
  describe('Include with Sparse Fieldsets and custom idProperty', () => {
567
- let testData = {};
567
+ const testData = {}
568
568
 
569
569
  beforeEach(async () => {
570
570
  await cleanTables(knex, [
571
- 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
571
+ 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
572
572
  'custom_id_books', 'custom_id_book_authors', 'custom_id_reviews'
573
- ]);
573
+ ])
574
574
 
575
575
  // Create test data
576
576
  const country = await api.resources.countries.post({
577
577
  inputRecord: createJsonApiDocument('countries', { name: 'Test Country', code: 'TC' })
578
- });
579
- testData.country = country.data;
578
+ })
579
+ testData.country = country.data
580
580
 
581
581
  const publisher = await api.resources.publishers.post({
582
582
  inputRecord: createJsonApiDocument('publishers',
583
583
  { name: 'Test Publisher' },
584
584
  { country: createRelationship(resourceIdentifier('countries', testData.country.id)) }
585
585
  )
586
- });
587
- testData.publisher = publisher.data;
586
+ })
587
+ testData.publisher = publisher.data
588
588
 
589
589
  const author = await api.resources.authors.post({
590
590
  inputRecord: createJsonApiDocument('authors', { name: 'Test Author', biography: 'A great author' })
591
- });
592
- testData.author = author.data;
591
+ })
592
+ testData.author = author.data
593
593
 
594
594
  const book = await api.resources.books.post({
595
595
  inputRecord: createJsonApiDocument('books',
@@ -600,9 +600,9 @@ describe('Custom idProperty Relationship Operations', () => {
600
600
  authors: createToManyRelationship([resourceIdentifier('authors', testData.author.id)])
601
601
  }
602
602
  )
603
- });
604
- testData.book = book.data;
605
- });
603
+ })
604
+ testData.book = book.data
605
+ })
606
606
 
607
607
  it('should combine sparse fieldsets with includes using custom IDs', async () => {
608
608
  const result = await api.resources.books.get({
@@ -615,52 +615,52 @@ describe('Custom idProperty Relationship Operations', () => {
615
615
  authors: 'name' // Exclude biography
616
616
  }
617
617
  }
618
- });
618
+ })
619
619
 
620
620
  // Check main resource has only requested fields
621
- assert(result.data.attributes.title);
622
- assert(!result.data.attributes.isbn);
623
- assert(!result.data.attributes.pages);
621
+ assert(result.data.attributes.title)
622
+ assert(!result.data.attributes.isbn)
623
+ assert(!result.data.attributes.pages)
624
624
 
625
625
  // Check included resources have only requested fields
626
- const publisher = result.included.find(r => r.type === 'publishers');
627
- assert(publisher.attributes.name);
628
- assert(publisher.id); // ID is always included
626
+ const publisher = result.included.find(r => r.type === 'publishers')
627
+ assert(publisher.attributes.name)
628
+ assert(publisher.id) // ID is always included
629
629
 
630
- const author = result.included.find(r => r.type === 'authors');
631
- assert(author.attributes.name);
632
- assert(!author.attributes.biography);
633
- assert(author.id);
634
- });
635
- });
630
+ const author = result.included.find(r => r.type === 'authors')
631
+ assert(author.attributes.name)
632
+ assert(!author.attributes.biography)
633
+ assert(author.id)
634
+ })
635
+ })
636
636
 
637
637
  describe('Include Depth Validation with custom idProperty', () => {
638
638
  beforeEach(async () => {
639
639
  await cleanTables(knex, [
640
- 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
640
+ 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
641
641
  'custom_id_books', 'custom_id_book_authors', 'custom_id_reviews'
642
- ]);
643
- });
642
+ ])
643
+ })
644
644
 
645
645
  it('should enforce include depth limits', async () => {
646
646
  // Create deep hierarchy
647
647
  const country = await api.resources.countries.post({
648
648
  inputRecord: createJsonApiDocument('countries', { name: 'USA', code: 'US' })
649
- });
649
+ })
650
650
 
651
651
  const publisher = await api.resources.publishers.post({
652
652
  inputRecord: createJsonApiDocument('publishers',
653
653
  { name: 'Test Publisher' },
654
654
  { country: createRelationship(resourceIdentifier('countries', country.data.id)) }
655
655
  )
656
- });
656
+ })
657
657
 
658
658
  const author = await api.resources.authors.post({
659
659
  inputRecord: createJsonApiDocument('authors',
660
660
  { name: 'Test Author' },
661
661
  { country: createRelationship(resourceIdentifier('countries', country.data.id)) }
662
662
  )
663
- });
663
+ })
664
664
 
665
665
  const book = await api.resources.books.post({
666
666
  inputRecord: createJsonApiDocument('books',
@@ -671,7 +671,7 @@ describe('Custom idProperty Relationship Operations', () => {
671
671
  authors: createToManyRelationship([resourceIdentifier('authors', author.data.id)])
672
672
  }
673
673
  )
674
- });
674
+ })
675
675
 
676
676
  // Test depth limit (default is 3)
677
677
  // This should work: books -> authors -> country (depth 2)
@@ -679,8 +679,8 @@ describe('Custom idProperty Relationship Operations', () => {
679
679
  queryParams: {
680
680
  include: ['authors.country']
681
681
  }
682
- });
683
- assert(validDepth.data);
682
+ })
683
+ assert(validDepth.data)
684
684
 
685
685
  // This should fail: trying to go too deep (if we had deeper relationships)
686
686
  // Since our test data doesn't have 4+ levels, we test that 3 levels work
@@ -688,28 +688,28 @@ describe('Custom idProperty Relationship Operations', () => {
688
688
  queryParams: {
689
689
  include: ['publisher.country', 'authors.country']
690
690
  }
691
- });
692
- assert(maxDepth.data);
693
- });
694
- });
691
+ })
692
+ assert(maxDepth.data)
693
+ })
694
+ })
695
695
 
696
696
  describe('Complex Scenarios with custom idProperty', () => {
697
697
  beforeEach(async () => {
698
698
  await cleanTables(knex, [
699
- 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
699
+ 'custom_id_countries', 'custom_id_publishers', 'custom_id_authors',
700
700
  'custom_id_books', 'custom_id_book_authors', 'custom_id_reviews'
701
- ]);
702
- });
701
+ ])
702
+ })
703
703
 
704
704
  it('should handle complex queries with filters, includes, and custom IDs', async () => {
705
705
  // Create multiple countries
706
706
  const usa = await api.resources.countries.post({
707
707
  inputRecord: createJsonApiDocument('countries', { name: 'USA', code: 'US' })
708
- });
708
+ })
709
709
 
710
710
  const uk = await api.resources.countries.post({
711
711
  inputRecord: createJsonApiDocument('countries', { name: 'UK', code: 'GB' })
712
- });
712
+ })
713
713
 
714
714
  // Create publishers in different countries
715
715
  const usPublisher = await api.resources.publishers.post({
@@ -717,23 +717,23 @@ describe('Custom idProperty Relationship Operations', () => {
717
717
  { name: 'US Publisher' },
718
718
  { country: createRelationship(resourceIdentifier('countries', usa.data.id)) }
719
719
  )
720
- });
720
+ })
721
721
 
722
722
  const ukPublisher = await api.resources.publishers.post({
723
723
  inputRecord: createJsonApiDocument('publishers',
724
724
  { name: 'UK Publisher' },
725
725
  { country: createRelationship(resourceIdentifier('countries', uk.data.id)) }
726
726
  )
727
- });
727
+ })
728
728
 
729
729
  // Create authors
730
730
  const author1 = await api.resources.authors.post({
731
731
  inputRecord: createJsonApiDocument('authors', { name: 'Author One' })
732
- });
732
+ })
733
733
 
734
734
  const author2 = await api.resources.authors.post({
735
735
  inputRecord: createJsonApiDocument('authors', { name: 'Author Two' })
736
- });
736
+ })
737
737
 
738
738
  // Create books
739
739
  await api.resources.books.post({
@@ -745,7 +745,7 @@ describe('Custom idProperty Relationship Operations', () => {
745
745
  authors: createToManyRelationship([resourceIdentifier('authors', author1.data.id)])
746
746
  }
747
747
  )
748
- });
748
+ })
749
749
 
750
750
  await api.resources.books.post({
751
751
  inputRecord: createJsonApiDocument('books',
@@ -759,7 +759,7 @@ describe('Custom idProperty Relationship Operations', () => {
759
759
  ])
760
760
  }
761
761
  )
762
- });
762
+ })
763
763
 
764
764
  // Complex query: US books with their publishers and authors
765
765
  const result = await api.resources.books.query({
@@ -768,30 +768,30 @@ describe('Custom idProperty Relationship Operations', () => {
768
768
  include: ['publisher.country', 'authors'],
769
769
  sort: ['title']
770
770
  }
771
- });
771
+ })
772
+
773
+ assert.equal(result.data.length, 1)
774
+ assert.equal(result.data[0].attributes.title, 'US Book 1')
772
775
 
773
- assert.equal(result.data.length, 1);
774
- assert.equal(result.data[0].attributes.title, 'US Book 1');
775
-
776
776
  // Verify includes
777
- const includedTypes = result.included.map(r => r.type);
778
- assert(includedTypes.includes('publishers'));
779
- assert(includedTypes.includes('countries'));
780
- assert(includedTypes.includes('authors'));
781
- });
777
+ const includedTypes = result.included.map(r => r.type)
778
+ assert(includedTypes.includes('publishers'))
779
+ assert(includedTypes.includes('countries'))
780
+ assert(includedTypes.includes('authors'))
781
+ })
782
782
 
783
783
  it('should handle bulk operations with custom IDs', async () => {
784
784
  const country = await api.resources.countries.post({
785
785
  inputRecord: createJsonApiDocument('countries', { name: 'USA', code: 'US' })
786
- });
786
+ })
787
787
 
788
788
  // Create multiple authors
789
- const authors = [];
789
+ const authors = []
790
790
  for (let i = 1; i <= 3; i++) {
791
791
  const author = await api.resources.authors.post({
792
792
  inputRecord: createJsonApiDocument('authors', { name: `Author ${i}` })
793
- });
794
- authors.push(author.data);
793
+ })
794
+ authors.push(author.data)
795
795
  }
796
796
 
797
797
  // Create book with all authors
@@ -805,29 +805,29 @@ describe('Custom idProperty Relationship Operations', () => {
805
805
  )
806
806
  }
807
807
  )
808
- });
808
+ })
809
809
 
810
810
  // Verify all relationships were created
811
811
  const bookWithAuthors = await api.resources.books.get({
812
812
  id: book.data.id,
813
813
  queryParams: { include: ['authors'] }
814
- });
814
+ })
815
+
816
+ assert.equal(bookWithAuthors.included.length, 3)
815
817
 
816
- assert.equal(bookWithAuthors.included.length, 3);
817
-
818
818
  // Verify pivot table
819
- const pivotCount = await countRecords(knex, 'custom_id_book_authors');
820
- assert.equal(pivotCount, 3);
821
- });
819
+ const pivotCount = await countRecords(knex, 'custom_id_book_authors')
820
+ assert.equal(pivotCount, 3)
821
+ })
822
822
 
823
823
  it('should handle DELETE operations with custom IDs', async () => {
824
824
  const country = await api.resources.countries.post({
825
825
  inputRecord: createJsonApiDocument('countries', { name: 'USA', code: 'US' })
826
- });
826
+ })
827
827
 
828
828
  const author = await api.resources.authors.post({
829
829
  inputRecord: createJsonApiDocument('authors', { name: 'Test Author' })
830
- });
830
+ })
831
831
 
832
832
  const book = await api.resources.books.post({
833
833
  inputRecord: createJsonApiDocument('books',
@@ -837,41 +837,41 @@ describe('Custom idProperty Relationship Operations', () => {
837
837
  authors: createToManyRelationship([resourceIdentifier('authors', author.data.id)])
838
838
  }
839
839
  )
840
- });
840
+ })
841
841
 
842
842
  // Delete the book
843
- await api.resources.books.delete({ id: book.data.id });
843
+ await api.resources.books.delete({ id: book.data.id })
844
844
 
845
845
  // Verify book is deleted
846
846
  try {
847
- await api.resources.books.get({ id: book.data.id });
848
- assert.fail('Should have thrown error');
847
+ await api.resources.books.get({ id: book.data.id })
848
+ assert.fail('Should have thrown error')
849
849
  } catch (error) {
850
- assert(error.message.includes('not found') || error.message.includes('404'));
850
+ assert(error.message.includes('not found') || error.message.includes('404'))
851
851
  }
852
852
 
853
853
  // The REST API correctly does NOT cascade delete pivot records
854
854
  // This is intentional - automatic cascade deletion would be dangerous
855
855
  // Pivot records remain and should be cleaned up by application logic if needed
856
- const pivotCount = await countRecords(knex, 'custom_id_book_authors');
857
- assert.equal(pivotCount, 1, 'Pivot record should still exist');
856
+ const pivotCount = await countRecords(knex, 'custom_id_book_authors')
857
+ assert.equal(pivotCount, 1, 'Pivot record should still exist')
858
858
 
859
859
  // Verify author still exists
860
- const authorStillExists = await api.resources.authors.get({ id: author.data.id });
861
- assert(authorStillExists.data);
862
- });
860
+ const authorStillExists = await api.resources.authors.get({ id: author.data.id })
861
+ assert(authorStillExists.data)
862
+ })
863
863
 
864
864
  it('should handle PUT operations with custom IDs', async () => {
865
865
  const country = await api.resources.countries.post({
866
866
  inputRecord: createJsonApiDocument('countries', { name: 'USA', code: 'US' })
867
- });
867
+ })
868
868
 
869
869
  const publisher = await api.resources.publishers.post({
870
870
  inputRecord: createJsonApiDocument('publishers',
871
871
  { name: 'Original Publisher' },
872
872
  { country: createRelationship(resourceIdentifier('countries', country.data.id)) }
873
873
  )
874
- });
874
+ })
875
875
 
876
876
  // PUT to completely replace the publisher
877
877
  const putResult = await api.resources.publishers.put({
@@ -887,13 +887,13 @@ describe('Custom idProperty Relationship Operations', () => {
887
887
  }
888
888
  }
889
889
  }
890
- });
890
+ })
891
891
 
892
892
  // Verify full record returned
893
- validateJsonApiStructure(putResult, false);
894
- assert.equal(putResult.data.attributes.name, 'Replaced Publisher');
893
+ validateJsonApiStructure(putResult, false)
894
+ assert.equal(putResult.data.attributes.name, 'Replaced Publisher')
895
895
  assertResourceRelationship(putResult.data, 'country',
896
- resourceIdentifier('countries', country.data.id));
897
- });
898
- });
899
- });
896
+ resourceIdentifier('countries', country.data.id))
897
+ })
898
+ })
899
+ })