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,14 +1,14 @@
1
- import { describe, it, before, after, beforeEach } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import knexLib from 'knex';
1
+ import { describe, it, before, after, beforeEach } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import knexLib from 'knex'
4
4
  import {
5
5
  validateJsonApiStructure,
6
6
  cleanTables,
7
7
  createJsonApiDocument,
8
8
  assertResourceAttributes,
9
9
  assertResourceRelationship
10
- } from './helpers/test-utils.js';
11
- import { createAccessControlApi } from './fixtures/api-configs.js';
10
+ } from './helpers/test-utils.js'
11
+ import { createAccessControlApi } from './fixtures/api-configs.js'
12
12
 
13
13
  const knex = knexLib({
14
14
  client: 'better-sqlite3',
@@ -16,191 +16,191 @@ const knex = knexLib({
16
16
  filename: ':memory:'
17
17
  },
18
18
  useNullAsDefault: true
19
- });
19
+ })
20
20
 
21
- let api;
21
+ let api
22
22
 
23
- async function createUser(email, displayName = 'Test User') {
23
+ async function createUser (email, displayName = 'Test User') {
24
24
  const userDoc = createJsonApiDocument('users', {
25
25
  email,
26
26
  display_name: displayName
27
- });
27
+ })
28
28
 
29
29
  const result = await api.resources.users.post({
30
30
  inputRecord: userDoc,
31
31
  simplified: false
32
- }, { auth: { system: true } });
32
+ }, { auth: { system: true } })
33
33
 
34
- validateJsonApiStructure(result);
34
+ validateJsonApiStructure(result)
35
35
  assertResourceAttributes(result.data, {
36
36
  email,
37
37
  display_name: displayName
38
- });
38
+ })
39
39
 
40
- return Number(result.data.id);
40
+ return Number(result.data.id)
41
41
  }
42
42
 
43
- async function createProjectForUser(userId, name, status = null) {
43
+ async function createProjectForUser (userId, name, status = null) {
44
44
  const projectDoc = createJsonApiDocument('projects', {
45
45
  name,
46
46
  status
47
- });
47
+ })
48
48
 
49
49
  const result = await api.resources.projects.post({
50
50
  inputRecord: projectDoc,
51
51
  simplified: false
52
- }, { auth: { userId } });
52
+ }, { auth: { userId } })
53
53
 
54
- validateJsonApiStructure(result);
55
- return result.data;
54
+ validateJsonApiStructure(result)
55
+ return result.data
56
56
  }
57
57
 
58
58
  describe('Access plugin', () => {
59
59
  before(async () => {
60
- api = await createAccessControlApi(knex);
61
- });
60
+ api = await createAccessControlApi(knex)
61
+ })
62
62
 
63
63
  after(async () => {
64
- await knex.destroy();
65
- });
64
+ await knex.destroy()
65
+ })
66
66
 
67
67
  beforeEach(async () => {
68
68
  await cleanTables(knex, [
69
69
  'access_notes',
70
70
  'access_projects',
71
71
  'access_users'
72
- ]);
73
- });
72
+ ])
73
+ })
74
74
 
75
75
  it('rejects unauthenticated requests for protected operations', async () => {
76
76
  const projectDoc = createJsonApiDocument('projects', {
77
77
  name: 'Unauthenticated project'
78
- });
78
+ })
79
79
 
80
80
  await assert.rejects(async () => {
81
81
  await api.resources.projects.post({
82
82
  inputRecord: projectDoc,
83
83
  simplified: false
84
- });
84
+ })
85
85
  }, (error) => {
86
- assert.equal(error.statusCode, 403);
87
- assert.match(error.message, /Access denied/);
88
- return true;
89
- });
90
- });
86
+ assert.equal(error.statusCode, 403)
87
+ assert.match(error.message, /Access denied/)
88
+ return true
89
+ })
90
+ })
91
91
 
92
92
  it('auto assigns ownership when creating a project', async () => {
93
- const ownerId = await createUser('owner@example.com', 'Owner One');
93
+ const ownerId = await createUser('owner@example.com', 'Owner One')
94
94
 
95
95
  const projectDoc = createJsonApiDocument('projects', {
96
96
  name: 'Owned Project',
97
97
  status: 'active'
98
- });
98
+ })
99
99
 
100
100
  const createResult = await api.resources.projects.post({
101
101
  inputRecord: projectDoc,
102
102
  simplified: false
103
- }, { auth: { userId: ownerId } });
103
+ }, { auth: { userId: ownerId } })
104
104
 
105
- validateJsonApiStructure(createResult);
105
+ validateJsonApiStructure(createResult)
106
106
  assertResourceAttributes(createResult.data, {
107
107
  name: 'Owned Project',
108
108
  status: 'active'
109
- });
109
+ })
110
110
  assertResourceRelationship(createResult.data, 'user', {
111
111
  type: 'users',
112
112
  id: String(ownerId)
113
- });
113
+ })
114
114
 
115
115
  const getResult = await api.resources.projects.get({
116
116
  id: createResult.data.id,
117
117
  simplified: false
118
- }, { auth: { userId: ownerId } });
118
+ }, { auth: { userId: ownerId } })
119
119
 
120
- validateJsonApiStructure(getResult);
120
+ validateJsonApiStructure(getResult)
121
121
  assertResourceRelationship(getResult.data, 'user', {
122
122
  type: 'users',
123
123
  id: String(ownerId)
124
- });
125
- });
124
+ })
125
+ })
126
126
 
127
127
  it('filters query results by ownership for non-admin users', async () => {
128
- const aliceId = await createUser('alice@example.com', 'Alice');
129
- const bobId = await createUser('bob@example.com', 'Bob');
128
+ const aliceId = await createUser('alice@example.com', 'Alice')
129
+ const bobId = await createUser('bob@example.com', 'Bob')
130
130
 
131
- const aliceProject = await createProjectForUser(aliceId, 'Alice Project', 'active');
132
- await createProjectForUser(bobId, 'Bob Project', 'planning');
131
+ const aliceProject = await createProjectForUser(aliceId, 'Alice Project', 'active')
132
+ await createProjectForUser(bobId, 'Bob Project', 'planning')
133
133
 
134
134
  const queryResult = await api.resources.projects.query({
135
135
  simplified: false
136
- }, { auth: { userId: aliceId } });
136
+ }, { auth: { userId: aliceId } })
137
137
 
138
- validateJsonApiStructure(queryResult, true);
139
- assert.equal(queryResult.data.length, 1);
140
- assert.equal(queryResult.data[0].id, aliceProject.id);
138
+ validateJsonApiStructure(queryResult, true)
139
+ assert.equal(queryResult.data.length, 1)
140
+ assert.equal(queryResult.data[0].id, aliceProject.id)
141
141
  assertResourceRelationship(queryResult.data[0], 'user', {
142
142
  type: 'users',
143
143
  id: String(aliceId)
144
- });
145
- });
144
+ })
145
+ })
146
146
 
147
147
  it('allows admin roles to bypass ownership filtering', async () => {
148
- const userA = await createUser('admin_a@example.com', 'Admin A');
149
- const userB = await createUser('admin_b@example.com', 'Admin B');
148
+ const userA = await createUser('admin_a@example.com', 'Admin A')
149
+ const userB = await createUser('admin_b@example.com', 'Admin B')
150
150
 
151
- await createProjectForUser(userA, 'Admin Project A');
152
- await createProjectForUser(userB, 'Admin Project B');
151
+ await createProjectForUser(userA, 'Admin Project A')
152
+ await createProjectForUser(userB, 'Admin Project B')
153
153
 
154
154
  const adminQuery = await api.resources.projects.query({
155
155
  simplified: false
156
- }, { auth: { userId: 999, roles: ['admin'] } });
156
+ }, { auth: { userId: 999, roles: ['admin'] } })
157
157
 
158
- validateJsonApiStructure(adminQuery, true);
159
- assert.equal(adminQuery.data.length, 2);
158
+ validateJsonApiStructure(adminQuery, true)
159
+ assert.equal(adminQuery.data.length, 2)
160
160
 
161
- const relatedUserIds = adminQuery.data.map((resource) => resource.relationships.user.data.id).sort();
162
- assert.deepEqual(relatedUserIds, [String(userA), String(userB)]);
163
- });
161
+ const relatedUserIds = adminQuery.data.map((resource) => resource.relationships.user.data.id).sort()
162
+ assert.deepEqual(relatedUserIds, [String(userA), String(userB)])
163
+ })
164
164
 
165
165
  it('prevents users from accessing records they do not own', async () => {
166
- const ownerId = await createUser('primary@example.com', 'Primary');
167
- const otherId = await createUser('secondary@example.com', 'Secondary');
166
+ const ownerId = await createUser('primary@example.com', 'Primary')
167
+ const otherId = await createUser('secondary@example.com', 'Secondary')
168
168
 
169
- const project = await createProjectForUser(ownerId, 'Primary Project');
169
+ const project = await createProjectForUser(ownerId, 'Primary Project')
170
170
 
171
171
  await assert.rejects(async () => {
172
172
  await api.resources.projects.get({
173
173
  id: project.id,
174
174
  simplified: false
175
- }, { auth: { userId: otherId } });
175
+ }, { auth: { userId: otherId } })
176
176
  }, (error) => {
177
- assert.equal(error.statusCode, 403);
178
- assert.match(error.message, /Access denied/);
179
- return true;
180
- });
181
- });
177
+ assert.equal(error.statusCode, 403)
178
+ assert.match(error.message, /Access denied/)
179
+ return true
180
+ })
181
+ })
182
182
 
183
183
  it('enforces helper ownership checks', () => {
184
- const context = { auth: { userId: 123 } };
185
- const resource = { user_id: 123 };
184
+ const context = { auth: { userId: 123 } }
185
+ const resource = { user_id: 123 }
186
186
 
187
- const auth = api.helpers.auth.requireOwnership(context, resource);
188
- assert.equal(auth.userId, 123);
187
+ const auth = api.helpers.auth.requireOwnership(context, resource)
188
+ assert.equal(auth.userId, 123)
189
189
 
190
190
  assert.throws(() => {
191
- api.helpers.auth.requireOwnership({ auth: { userId: 123 } }, { user_id: 456 });
191
+ api.helpers.auth.requireOwnership({ auth: { userId: 123 } }, { user_id: 456 })
192
192
  }, (error) => {
193
- assert.equal(error.statusCode, 403);
194
- assert.match(error.message, /Access denied/);
195
- return true;
196
- });
193
+ assert.equal(error.statusCode, 403)
194
+ assert.match(error.message, /Access denied/)
195
+ return true
196
+ })
197
197
 
198
198
  assert.throws(() => {
199
- api.helpers.auth.requireAuth({});
199
+ api.helpers.auth.requireAuth({})
200
200
  }, (error) => {
201
- assert.equal(error.statusCode, 401);
202
- assert.match(error.message, /Authentication required/);
203
- return true;
204
- });
205
- });
206
- });
201
+ assert.equal(error.statusCode, 401)
202
+ assert.match(error.message, /Authentication required/)
203
+ return true
204
+ })
205
+ })
206
+ })