json-rest-api 1.0.14 → 1.0.16

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 +11 -2
  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 +1 -1
  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 +99 -99
  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 +11 -15
  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,15 +1,15 @@
1
- import { describe, it, before, after, beforeEach } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import knexLib from 'knex';
4
- import {
5
- validateJsonApiStructure,
6
- cleanTables,
1
+ import { describe, it, before, after, beforeEach } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import knexLib from 'knex'
4
+ import {
5
+ validateJsonApiStructure,
6
+ cleanTables,
7
7
  createJsonApiDocument,
8
8
  assertResourceAttributes
9
- } from './helpers/test-utils.js';
10
- import { Api } from 'hooked-api';
11
- import { RestApiPlugin } from '../plugins/core/rest-api-plugin.js';
12
- import { RestApiKnexPlugin } from '../plugins/core/rest-api-knex-plugin.js';
9
+ } from './helpers/test-utils.js'
10
+ import { Api } from 'hooked-api'
11
+ import { RestApiPlugin } from '../plugins/core/rest-api-plugin.js'
12
+ import { RestApiKnexPlugin } from '../plugins/core/rest-api-knex-plugin.js'
13
13
 
14
14
  // Create Knex instance for tests - always use SQLite in-memory
15
15
  const knex = knexLib({
@@ -18,10 +18,10 @@ const knex = knexLib({
18
18
  filename: ':memory:'
19
19
  },
20
20
  useNullAsDefault: true
21
- });
21
+ })
22
22
 
23
23
  // API instance that persists across ALL tests
24
- let api;
24
+ let api
25
25
 
26
26
  describe('Virtual Fields Tests', () => {
27
27
  // IMPORTANT: before() runs ONCE for the entire test suite
@@ -29,8 +29,8 @@ describe('Virtual Fields Tests', () => {
29
29
  // Create a custom API with virtual fields
30
30
  api = new Api({
31
31
  name: 'virtual-fields-test',
32
- });
33
-
32
+ })
33
+
34
34
  // Install plugins
35
35
  await api.use(RestApiPlugin, {
36
36
  simplifiedApi: false,
@@ -40,9 +40,9 @@ describe('Virtual Fields Tests', () => {
40
40
  put: 'full',
41
41
  patch: 'full'
42
42
  }
43
- });
44
- await api.use(RestApiKnexPlugin, { knex });
45
-
43
+ })
44
+ await api.use(RestApiKnexPlugin, { knex })
45
+
46
46
  // Add a resource with virtual fields
47
47
  await api.addResource('users', {
48
48
  schema: {
@@ -53,23 +53,23 @@ describe('Virtual Fields Tests', () => {
53
53
  passwordConfirmation: { type: 'string', virtual: true },
54
54
  termsAccepted: { type: 'boolean', virtual: true }
55
55
  }
56
- });
57
-
56
+ })
57
+
58
58
  // Create table
59
- await api.resources.users.createKnexTable();
60
- });
61
-
59
+ await api.resources.users.createKnexTable()
60
+ })
61
+
62
62
  // IMPORTANT: after() cleans up resources
63
63
  after(async () => {
64
64
  // Always destroy knex connection to allow tests to exit
65
- await knex.destroy();
66
- });
67
-
65
+ await knex.destroy()
66
+ })
67
+
68
68
  // IMPORTANT: beforeEach() cleans data but does NOT recreate API
69
69
  beforeEach(async () => {
70
70
  // Clean all tables - list ALL tables your tests use
71
- await cleanTables(knex, ['users']);
72
- });
71
+ await cleanTables(knex, ['users'])
72
+ })
73
73
 
74
74
  describe('Virtual field validation', () => {
75
75
  it('should accept virtual fields in POST request', async () => {
@@ -79,30 +79,30 @@ describe('Virtual Fields Tests', () => {
79
79
  password: 'secret123',
80
80
  passwordConfirmation: 'secret123',
81
81
  termsAccepted: true
82
- });
83
-
82
+ })
83
+
84
84
  const result = await api.resources.users.post({
85
85
  inputRecord: doc,
86
86
  simplified: false
87
- });
88
-
87
+ })
88
+
89
89
  // Validate response structure - POST returns single resource, not array
90
- validateJsonApiStructure(result);
91
- assert.equal(result.data.type, 'users');
92
- assert.ok(result.data.id);
93
-
90
+ validateJsonApiStructure(result)
91
+ assert.equal(result.data.type, 'users')
92
+ assert.ok(result.data.id)
93
+
94
94
  // Virtual fields should be returned in response
95
95
  assertResourceAttributes(result.data, {
96
96
  username: 'testuser',
97
97
  email: 'test@example.com',
98
98
  passwordConfirmation: 'secret123',
99
99
  termsAccepted: true
100
- });
101
-
100
+ })
101
+
102
102
  // Password should NOT be in response (security)
103
- assert.ok(!('password' in result.data.attributes));
104
- });
105
-
103
+ assert.ok(!('password' in result.data.attributes))
104
+ })
105
+
106
106
  it('should not store virtual fields in database', async () => {
107
107
  const doc = createJsonApiDocument('users', {
108
108
  username: 'testuser2',
@@ -110,43 +110,43 @@ describe('Virtual Fields Tests', () => {
110
110
  password: 'secret456',
111
111
  passwordConfirmation: 'secret456',
112
112
  termsAccepted: false
113
- });
114
-
113
+ })
114
+
115
115
  const createResult = await api.resources.users.post({
116
116
  inputRecord: doc,
117
117
  simplified: false
118
- });
119
-
118
+ })
119
+
120
120
  // Verify data was created
121
- const userId = createResult.data.id;
122
-
121
+ const userId = createResult.data.id
122
+
123
123
  // Check database directly - virtual fields should not be stored
124
- const dbRecord = await knex('users').where('id', userId).first();
125
- assert.ok(dbRecord);
126
- assert.equal(dbRecord.username, 'testuser2');
127
- assert.equal(dbRecord.email, 'test2@example.com');
128
- assert.ok(dbRecord.password); // Password is stored
129
-
124
+ const dbRecord = await knex('users').where('id', userId).first()
125
+ assert.ok(dbRecord)
126
+ assert.equal(dbRecord.username, 'testuser2')
127
+ assert.equal(dbRecord.email, 'test2@example.com')
128
+ assert.ok(dbRecord.password) // Password is stored
129
+
130
130
  // Virtual fields should NOT exist in database
131
- assert.ok(!('passwordConfirmation' in dbRecord));
132
- assert.ok(!('termsAccepted' in dbRecord));
133
- });
134
-
131
+ assert.ok(!('passwordConfirmation' in dbRecord))
132
+ assert.ok(!('termsAccepted' in dbRecord))
133
+ })
134
+
135
135
  it('should handle virtual fields in PATCH request', async () => {
136
136
  // Create a user first
137
137
  const createDoc = createJsonApiDocument('users', {
138
138
  username: 'patchuser',
139
139
  email: 'patch@example.com',
140
140
  password: 'oldpassword'
141
- });
142
-
141
+ })
142
+
143
143
  const createResult = await api.resources.users.post({
144
144
  inputRecord: createDoc,
145
145
  simplified: false
146
- });
147
-
148
- const userId = createResult.data.id;
149
-
146
+ })
147
+
148
+ const userId = createResult.data.id
149
+
150
150
  // Update with virtual field
151
151
  const patchDoc = {
152
152
  data: {
@@ -157,37 +157,37 @@ describe('Virtual Fields Tests', () => {
157
157
  passwordConfirmation: 'newpassword'
158
158
  }
159
159
  }
160
- };
161
-
160
+ }
161
+
162
162
  const patchResult = await api.resources.users.patch({
163
163
  id: userId,
164
164
  inputRecord: patchDoc,
165
165
  simplified: false
166
- });
167
-
166
+ })
167
+
168
168
  // Virtual field should be in response
169
- assert.equal(patchResult.data.attributes.passwordConfirmation, 'newpassword');
170
-
169
+ assert.equal(patchResult.data.attributes.passwordConfirmation, 'newpassword')
170
+
171
171
  // Check database - passwordConfirmation should not be stored
172
- const dbRecord = await knex('users').where('id', userId).first();
173
- assert.ok(!('passwordConfirmation' in dbRecord));
174
- });
175
-
172
+ const dbRecord = await knex('users').where('id', userId).first()
173
+ assert.ok(!('passwordConfirmation' in dbRecord))
174
+ })
175
+
176
176
  it('should respect sparse fieldsets with virtual fields on PATCH', async () => {
177
177
  // Create a user first
178
178
  const createDoc = createJsonApiDocument('users', {
179
179
  username: 'sparseuser',
180
180
  email: 'sparse@example.com',
181
181
  password: 'password123'
182
- });
183
-
182
+ })
183
+
184
184
  const createResult = await api.resources.users.post({
185
185
  inputRecord: createDoc,
186
186
  simplified: false
187
- });
188
-
189
- const userId = createResult.data.id;
190
-
187
+ })
188
+
189
+ const userId = createResult.data.id
190
+
191
191
  // Update with virtual field and request specific fields in response
192
192
  const patchDoc = {
193
193
  data: {
@@ -198,8 +198,8 @@ describe('Virtual Fields Tests', () => {
198
198
  passwordConfirmation: 'password123'
199
199
  }
200
200
  }
201
- };
202
-
201
+ }
202
+
203
203
  const patchResult = await api.resources.users.patch({
204
204
  id: userId,
205
205
  inputRecord: patchDoc,
@@ -209,17 +209,17 @@ describe('Virtual Fields Tests', () => {
209
209
  }
210
210
  },
211
211
  simplified: false
212
- });
213
-
212
+ })
213
+
214
214
  // Should only have requested fields
215
- assert.equal(Object.keys(patchResult.data.attributes).length, 2);
216
- assert.equal(patchResult.data.attributes.email, 'newemail@example.com');
217
- assert.equal(patchResult.data.attributes.passwordConfirmation, 'password123');
218
-
215
+ assert.equal(Object.keys(patchResult.data.attributes).length, 2)
216
+ assert.equal(patchResult.data.attributes.email, 'newemail@example.com')
217
+ assert.equal(patchResult.data.attributes.passwordConfirmation, 'password123')
218
+
219
219
  // Other fields should not be present
220
- assert.ok(!('username' in patchResult.data.attributes));
221
- });
222
-
220
+ assert.ok(!('username' in patchResult.data.attributes))
221
+ })
222
+
223
223
  it('should exclude virtual fields when not requested in sparse fieldsets', async () => {
224
224
  // Create a user
225
225
  const doc = createJsonApiDocument('users', {
@@ -228,15 +228,15 @@ describe('Virtual Fields Tests', () => {
228
228
  password: 'password123',
229
229
  passwordConfirmation: 'password123',
230
230
  termsAccepted: true
231
- });
232
-
231
+ })
232
+
233
233
  const createResult = await api.resources.users.post({
234
234
  inputRecord: doc,
235
235
  simplified: false
236
- });
237
-
238
- const userId = createResult.data.id;
239
-
236
+ })
237
+
238
+ const userId = createResult.data.id
239
+
240
240
  // Fetch with sparse fieldsets NOT including virtual fields
241
241
  const getResult = await api.resources.users.get({
242
242
  id: userId,
@@ -246,33 +246,33 @@ describe('Virtual Fields Tests', () => {
246
246
  }
247
247
  },
248
248
  simplified: false
249
- });
250
-
249
+ })
250
+
251
251
  // Should only have requested fields
252
- assert.equal(Object.keys(getResult.data.attributes).length, 2);
253
- assert.equal(getResult.data.attributes.username, 'excludeuser');
254
- assert.equal(getResult.data.attributes.email, 'exclude@example.com');
255
-
252
+ assert.equal(Object.keys(getResult.data.attributes).length, 2)
253
+ assert.equal(getResult.data.attributes.username, 'excludeuser')
254
+ assert.equal(getResult.data.attributes.email, 'exclude@example.com')
255
+
256
256
  // Virtual fields should not be present
257
- assert.ok(!('passwordConfirmation' in getResult.data.attributes));
258
- assert.ok(!('termsAccepted' in getResult.data.attributes));
259
- });
260
-
257
+ assert.ok(!('passwordConfirmation' in getResult.data.attributes))
258
+ assert.ok(!('termsAccepted' in getResult.data.attributes))
259
+ })
260
+
261
261
  it('should handle PUT requests with virtual fields', async () => {
262
262
  // Create initial record
263
263
  const createDoc = createJsonApiDocument('users', {
264
264
  username: 'putuser',
265
265
  email: 'put@example.com',
266
266
  password: 'initialpass'
267
- });
268
-
267
+ })
268
+
269
269
  const createResult = await api.resources.users.post({
270
270
  inputRecord: createDoc,
271
271
  simplified: false
272
- });
273
-
274
- const userId = createResult.data.id;
275
-
272
+ })
273
+
274
+ const userId = createResult.data.id
275
+
276
276
  // Replace entire record with PUT, including virtual fields
277
277
  const putDoc = {
278
278
  data: {
@@ -286,31 +286,31 @@ describe('Virtual Fields Tests', () => {
286
286
  termsAccepted: false
287
287
  }
288
288
  }
289
- };
290
-
289
+ }
290
+
291
291
  const putResult = await api.resources.users.put({
292
292
  id: userId,
293
293
  inputRecord: putDoc,
294
294
  simplified: false
295
- });
296
-
295
+ })
296
+
297
297
  // Virtual fields should be in response
298
298
  assertResourceAttributes(putResult.data, {
299
299
  username: 'updateduser',
300
300
  email: 'updated@example.com',
301
301
  passwordConfirmation: 'newpass',
302
302
  termsAccepted: false
303
- });
304
-
303
+ })
304
+
305
305
  // Check database - virtual fields should not be stored
306
- const dbRecord = await knex('users').where('id', userId).first();
307
- assert.equal(dbRecord.username, 'updateduser');
308
- assert.equal(dbRecord.email, 'updated@example.com');
309
- assert.ok(!('passwordConfirmation' in dbRecord));
310
- assert.ok(!('termsAccepted' in dbRecord));
311
- });
312
- });
313
-
306
+ const dbRecord = await knex('users').where('id', userId).first()
307
+ assert.equal(dbRecord.username, 'updateduser')
308
+ assert.equal(dbRecord.email, 'updated@example.com')
309
+ assert.ok(!('passwordConfirmation' in dbRecord))
310
+ assert.ok(!('termsAccepted' in dbRecord))
311
+ })
312
+ })
313
+
314
314
  describe('Virtual fields vs computed fields', () => {
315
315
  before(async () => {
316
316
  // Add a resource with both virtual and computed fields
@@ -327,20 +327,20 @@ describe('Virtual Fields Tests', () => {
327
327
  computed: true,
328
328
  dependencies: ['price', 'cost'],
329
329
  compute: ({ attributes }) => {
330
- if (!attributes.price || !attributes.cost) return null;
331
- return ((attributes.price - attributes.cost) / attributes.price * 100).toFixed(2);
330
+ if (!attributes.price || !attributes.cost) return null
331
+ return ((attributes.price - attributes.cost) / attributes.price * 100).toFixed(2)
332
332
  }
333
333
  }
334
334
  }
335
- });
336
-
337
- await api.resources.products.createKnexTable();
338
- });
339
-
335
+ })
336
+
337
+ await api.resources.products.createKnexTable()
338
+ })
339
+
340
340
  beforeEach(async () => {
341
- await cleanTables(knex, ['products']);
342
- });
343
-
341
+ await cleanTables(knex, ['products'])
342
+ })
343
+
344
344
  it('should handle both virtual and computed fields correctly', async () => {
345
345
  // Create product with virtual fields
346
346
  const doc = createJsonApiDocument('products', {
@@ -349,13 +349,13 @@ describe('Virtual Fields Tests', () => {
349
349
  cost: 60,
350
350
  priceInCents: 10000,
351
351
  discountCode: 'SAVE20'
352
- });
353
-
352
+ })
353
+
354
354
  const result = await api.resources.products.post({
355
355
  inputRecord: doc,
356
356
  simplified: false
357
- });
358
-
357
+ })
358
+
359
359
  // Check response
360
360
  assertResourceAttributes(result.data, {
361
361
  name: 'Test Product',
@@ -363,42 +363,42 @@ describe('Virtual Fields Tests', () => {
363
363
  profitMargin: '40.00', // Computed field
364
364
  priceInCents: 10000, // Virtual field
365
365
  discountCode: 'SAVE20' // Virtual field
366
- });
367
-
366
+ })
367
+
368
368
  // Cost should not be in response (normallyHidden)
369
- assert.ok(!('cost' in result.data.attributes));
370
-
369
+ assert.ok(!('cost' in result.data.attributes))
370
+
371
371
  // Check database
372
- const dbRecord = await knex('products').where('id', result.data.id).first();
373
- assert.equal(dbRecord.name, 'Test Product');
374
- assert.equal(dbRecord.price, 100);
375
- assert.equal(dbRecord.cost, 60); // Cost is stored in DB
376
-
372
+ const dbRecord = await knex('products').where('id', result.data.id).first()
373
+ assert.equal(dbRecord.name, 'Test Product')
374
+ assert.equal(dbRecord.price, 100)
375
+ assert.equal(dbRecord.cost, 60) // Cost is stored in DB
376
+
377
377
  // Neither virtual nor computed fields should be in DB
378
- assert.ok(!('profitMargin' in dbRecord));
379
- assert.ok(!('priceInCents' in dbRecord));
380
- assert.ok(!('discountCode' in dbRecord));
381
- });
382
-
378
+ assert.ok(!('profitMargin' in dbRecord))
379
+ assert.ok(!('priceInCents' in dbRecord))
380
+ assert.ok(!('discountCode' in dbRecord))
381
+ })
382
+
383
383
  it('should calculate computed fields regardless of input', async () => {
384
384
  // Create product without trying to set computed field
385
385
  const doc = createJsonApiDocument('products', {
386
386
  name: 'Test Product 2',
387
387
  price: 200,
388
388
  cost: 50
389
- });
390
-
389
+ })
390
+
391
391
  const result = await api.resources.products.post({
392
392
  inputRecord: doc,
393
393
  simplified: false
394
- });
395
-
394
+ })
395
+
396
396
  // Computed field should have calculated value
397
- assert.equal(result.data.attributes.profitMargin, '75.00');
398
-
397
+ assert.equal(result.data.attributes.profitMargin, '75.00')
398
+
399
399
  // Virtual fields were not provided, so they shouldn't be in response
400
- assert.ok(!('priceInCents' in result.data.attributes));
401
- assert.ok(!('discountCode' in result.data.attributes));
402
- });
403
- });
404
- });
400
+ assert.ok(!('priceInCents' in result.data.attributes))
401
+ assert.ok(!('discountCode' in result.data.attributes))
402
+ })
403
+ })
404
+ })