json-rest-api 1.0.21 → 1.0.23

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 (88) hide show
  1. package/CLAUDE.md +1 -2
  2. package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +180 -0
  3. package/SINGLE_VALIDATION_CONTRACT_TODO.txt +172 -0
  4. package/docs/API.md +51 -14
  5. package/docs/COMPARISON.md +2 -2
  6. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +28 -1
  7. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +5 -1
  8. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +3 -1
  9. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +12 -60
  10. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +2 -2
  11. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +5 -1
  12. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +2 -0
  13. package/docs/GUIDE/GUIDE_X_Autofiltering.md +355 -0
  14. package/docs/GUIDE/GUIDE_X_Fastify.md +78 -0
  15. package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +250 -0
  16. package/docs/GUIDE/GUIDE_X_Query_Projections.md +159 -0
  17. package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +199 -0
  18. package/docs/GUIDE/index.md +18 -9
  19. package/docs/QUICKSTART.md +5 -1
  20. package/docs/index.md +1 -1
  21. package/index.js +3 -2
  22. package/package.json +4 -4
  23. package/plugins/core/connectors/express-plugin.js +97 -184
  24. package/plugins/core/connectors/fastify-plugin.js +206 -0
  25. package/plugins/core/connectors/lib/connector-core.js +195 -0
  26. package/plugins/core/connectors/lib/transport-http-helpers.js +122 -0
  27. package/plugins/core/connectors/lib/transport-route-schemas.js +92 -0
  28. package/plugins/core/lib/dbIntrospection.js +1078 -0
  29. package/plugins/core/lib/dbTablesOperations.js +1104 -160
  30. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +5 -2
  31. package/plugins/core/lib/querying/knex-process-includes.js +3 -4
  32. package/plugins/core/lib/querying/knex-query-helpers.js +17 -71
  33. package/plugins/core/lib/querying/knex-relationship-includes.js +466 -206
  34. package/plugins/core/lib/querying/query-builder-utils.js +19 -0
  35. package/plugins/core/lib/querying/query-field-sort-helpers.js +135 -0
  36. package/plugins/core/lib/querying/sort-helpers.js +33 -0
  37. package/plugins/core/lib/querying/storage-adapter-utils.js +51 -0
  38. package/plugins/core/lib/querying/url-helpers.js +8 -2
  39. package/plugins/core/lib/querying-writing/compile-schemas.js +12 -1
  40. package/plugins/core/lib/querying-writing/knex-field-helpers.js +83 -13
  41. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +13 -12
  42. package/plugins/core/lib/querying-writing/query-field-helpers.js +88 -0
  43. package/plugins/core/lib/querying-writing/request-contracts.js +590 -0
  44. package/plugins/core/lib/storage/canonical-storage-mapping.js +113 -0
  45. package/plugins/core/lib/storage/storage-adapter.js +79 -2
  46. package/plugins/core/lib/storage/storage-mapping.js +204 -0
  47. package/plugins/core/rest-api-anyapi-knex-plugin.js +355 -228
  48. package/plugins/core/rest-api-autofilter-plugin.js +414 -0
  49. package/plugins/core/rest-api-knex-plugin.js +249 -211
  50. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +10 -5
  51. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +12 -6
  52. package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +41 -0
  53. package/plugins/core/rest-api-plugin-methods/common.js +121 -83
  54. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +6 -1
  55. package/plugins/core/rest-api-plugin-methods/get.js +23 -14
  56. package/plugins/core/rest-api-plugin-methods/patch.js +28 -12
  57. package/plugins/core/rest-api-plugin-methods/post.js +13 -8
  58. package/plugins/core/rest-api-plugin-methods/put.js +45 -21
  59. package/plugins/core/rest-api-plugin-methods/query.js +15 -53
  60. package/plugins/core/rest-api-plugin.js +2 -0
  61. package/plugins/core/rest-api-positioning-plugin.js +1 -1
  62. package/plugins/core/rest-api-query-projections-plugin.js +130 -0
  63. package/plugins/core/socketio-plugin.js +1 -3
  64. package/tests/autofilter.test.js +463 -0
  65. package/tests/custom-id-simplified-post.test.js +75 -0
  66. package/tests/custom-idproperty-relationships.test.js +33 -0
  67. package/tests/db-introspection.test.js +518 -0
  68. package/tests/db-table-operations.test.js +781 -0
  69. package/tests/fastify-plugin.test.js +141 -0
  70. package/tests/field-getters.test.js +1 -2
  71. package/tests/field-setters.test.js +0 -4
  72. package/tests/fixtures/api-configs.js +188 -219
  73. package/tests/helpers/fake-fastify.js +99 -0
  74. package/tests/http-connectors-parity.test.js +235 -0
  75. package/tests/projected-fields.test.js +319 -0
  76. package/tests/relationships.test.js +3 -3
  77. package/tests/searchschema-knex-behavior.test.js +187 -0
  78. package/tests/searchschema-merge.test.js +21 -69
  79. package/tests/storage-mapping.test.js +210 -0
  80. package/docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md +0 -707
  81. package/docs/GUIDE/GUIDE_X_Multihome.md +0 -666
  82. package/plugins/core/lib/querying-writing/payload-validators.js +0 -1314
  83. package/plugins/core/rest-api-access.js +0 -459
  84. package/plugins/core/rest-api-multihome-plugin.js +0 -314
  85. package/tests/multihome.test.js +0 -553
  86. package/tests/multihome.test.js.bak +0 -550
  87. package/tests/ownership-optout.test.js +0 -76
  88. package/tests/rest-api-access.test.js +0 -206
@@ -1,206 +0,0 @@
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
- createJsonApiDocument,
8
- assertResourceAttributes,
9
- assertResourceRelationship
10
- } from './helpers/test-utils.js'
11
- import { createAccessControlApi } from './fixtures/api-configs.js'
12
-
13
- const knex = knexLib({
14
- client: 'better-sqlite3',
15
- connection: {
16
- filename: ':memory:'
17
- },
18
- useNullAsDefault: true
19
- })
20
-
21
- let api
22
-
23
- async function createUser (email, displayName = 'Test User') {
24
- const userDoc = createJsonApiDocument('users', {
25
- email,
26
- display_name: displayName
27
- })
28
-
29
- const result = await api.resources.users.post({
30
- inputRecord: userDoc,
31
- simplified: false
32
- }, { auth: { system: true } })
33
-
34
- validateJsonApiStructure(result)
35
- assertResourceAttributes(result.data, {
36
- email,
37
- display_name: displayName
38
- })
39
-
40
- return Number(result.data.id)
41
- }
42
-
43
- async function createProjectForUser (userId, name, status = null) {
44
- const projectDoc = createJsonApiDocument('projects', {
45
- name,
46
- status
47
- })
48
-
49
- const result = await api.resources.projects.post({
50
- inputRecord: projectDoc,
51
- simplified: false
52
- }, { auth: { userId } })
53
-
54
- validateJsonApiStructure(result)
55
- return result.data
56
- }
57
-
58
- describe('Access plugin', () => {
59
- before(async () => {
60
- api = await createAccessControlApi(knex)
61
- })
62
-
63
- after(async () => {
64
- await knex.destroy()
65
- })
66
-
67
- beforeEach(async () => {
68
- await cleanTables(knex, [
69
- 'access_notes',
70
- 'access_projects',
71
- 'access_users'
72
- ])
73
- })
74
-
75
- it('rejects unauthenticated requests for protected operations', async () => {
76
- const projectDoc = createJsonApiDocument('projects', {
77
- name: 'Unauthenticated project'
78
- })
79
-
80
- await assert.rejects(async () => {
81
- await api.resources.projects.post({
82
- inputRecord: projectDoc,
83
- simplified: false
84
- })
85
- }, (error) => {
86
- assert.equal(error.statusCode, 403)
87
- assert.match(error.message, /Access denied/)
88
- return true
89
- })
90
- })
91
-
92
- it('auto assigns ownership when creating a project', async () => {
93
- const ownerId = await createUser('owner@example.com', 'Owner One')
94
-
95
- const projectDoc = createJsonApiDocument('projects', {
96
- name: 'Owned Project',
97
- status: 'active'
98
- })
99
-
100
- const createResult = await api.resources.projects.post({
101
- inputRecord: projectDoc,
102
- simplified: false
103
- }, { auth: { userId: ownerId } })
104
-
105
- validateJsonApiStructure(createResult)
106
- assertResourceAttributes(createResult.data, {
107
- name: 'Owned Project',
108
- status: 'active'
109
- })
110
- assertResourceRelationship(createResult.data, 'user', {
111
- type: 'users',
112
- id: String(ownerId)
113
- })
114
-
115
- const getResult = await api.resources.projects.get({
116
- id: createResult.data.id,
117
- simplified: false
118
- }, { auth: { userId: ownerId } })
119
-
120
- validateJsonApiStructure(getResult)
121
- assertResourceRelationship(getResult.data, 'user', {
122
- type: 'users',
123
- id: String(ownerId)
124
- })
125
- })
126
-
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')
130
-
131
- const aliceProject = await createProjectForUser(aliceId, 'Alice Project', 'active')
132
- await createProjectForUser(bobId, 'Bob Project', 'planning')
133
-
134
- const queryResult = await api.resources.projects.query({
135
- simplified: false
136
- }, { auth: { userId: aliceId } })
137
-
138
- validateJsonApiStructure(queryResult, true)
139
- assert.equal(queryResult.data.length, 1)
140
- assert.equal(queryResult.data[0].id, aliceProject.id)
141
- assertResourceRelationship(queryResult.data[0], 'user', {
142
- type: 'users',
143
- id: String(aliceId)
144
- })
145
- })
146
-
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')
150
-
151
- await createProjectForUser(userA, 'Admin Project A')
152
- await createProjectForUser(userB, 'Admin Project B')
153
-
154
- const adminQuery = await api.resources.projects.query({
155
- simplified: false
156
- }, { auth: { userId: 999, roles: ['admin'] } })
157
-
158
- validateJsonApiStructure(adminQuery, true)
159
- assert.equal(adminQuery.data.length, 2)
160
-
161
- const relatedUserIds = adminQuery.data.map((resource) => resource.relationships.user.data.id).sort()
162
- assert.deepEqual(relatedUserIds, [String(userA), String(userB)])
163
- })
164
-
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')
168
-
169
- const project = await createProjectForUser(ownerId, 'Primary Project')
170
-
171
- await assert.rejects(async () => {
172
- await api.resources.projects.get({
173
- id: project.id,
174
- simplified: false
175
- }, { auth: { userId: otherId } })
176
- }, (error) => {
177
- assert.equal(error.statusCode, 403)
178
- assert.match(error.message, /Access denied/)
179
- return true
180
- })
181
- })
182
-
183
- it('enforces helper ownership checks', () => {
184
- const context = { auth: { userId: 123 } }
185
- const resource = { user_id: 123 }
186
-
187
- const auth = api.helpers.auth.requireOwnership(context, resource)
188
- assert.equal(auth.userId, 123)
189
-
190
- assert.throws(() => {
191
- api.helpers.auth.requireOwnership({ auth: { userId: 123 } }, { user_id: 456 })
192
- }, (error) => {
193
- assert.equal(error.statusCode, 403)
194
- assert.match(error.message, /Access denied/)
195
- return true
196
- })
197
-
198
- assert.throws(() => {
199
- api.helpers.auth.requireAuth({})
200
- }, (error) => {
201
- assert.equal(error.statusCode, 401)
202
- assert.match(error.message, /Authentication required/)
203
- return true
204
- })
205
- })
206
- })