json-rest-api 1.0.14 → 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.
- package/connectors_ideas/fastify.js +66 -68
- package/connectors_ideas/koa-plugin.js +50 -51
- package/eslint.config.js +11 -2
- package/examples/url-override-example.js +63 -63
- package/index.js +13 -13
- package/lib/enhanced-logger.js +51 -51
- package/lib/error-formatter.js +61 -61
- package/lib/rest-api-errors.js +28 -28
- package/package.json +1 -1
- package/plugins/core/bulk-operations-plugin.js +142 -145
- package/plugins/core/connectors/express-plugin.js +210 -217
- package/plugins/core/connectors/lib/busboy-detector.js +84 -84
- package/plugins/core/connectors/lib/formidable-detector.js +50 -50
- package/plugins/core/connectors/lib/request-helpers.js +74 -74
- package/plugins/core/file-handling-plugin.js +125 -125
- package/plugins/core/lib/anyapi/anyapi-registry.js +254 -254
- package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +287 -287
- package/plugins/core/lib/anyapi/schema-utils.js +101 -101
- package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +41 -41
- package/plugins/core/lib/dbTablesOperations.js +134 -135
- package/plugins/core/lib/fractional-positioning.js +74 -76
- package/plugins/core/lib/querying/knex-cross-table-search.js +320 -320
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +144 -144
- package/plugins/core/lib/querying/knex-pagination-helpers.js +164 -164
- package/plugins/core/lib/querying/knex-process-includes.js +50 -50
- package/plugins/core/lib/querying/knex-query-helpers-base.js +31 -31
- package/plugins/core/lib/querying/knex-query-helpers.js +451 -452
- package/plugins/core/lib/querying/knex-relationship-includes.js +1160 -1170
- package/plugins/core/lib/querying/knex-window-queries.js +89 -89
- package/plugins/core/lib/querying/url-helpers.js +35 -35
- package/plugins/core/lib/querying-writing/compile-schemas.js +97 -98
- package/plugins/core/lib/querying-writing/connectors-query-parser.js +31 -31
- package/plugins/core/lib/querying-writing/database-capabilities.js +94 -94
- package/plugins/core/lib/querying-writing/database-value-normalizers.js +76 -76
- package/plugins/core/lib/querying-writing/default-data-helpers.js +27 -27
- package/plugins/core/lib/querying-writing/field-utils.js +44 -42
- package/plugins/core/lib/querying-writing/knex-constants.js +15 -16
- package/plugins/core/lib/querying-writing/knex-field-helpers.js +117 -115
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +41 -41
- package/plugins/core/lib/querying-writing/payload-validators.js +275 -275
- package/plugins/core/lib/querying-writing/schema-helpers.js +129 -129
- package/plugins/core/lib/querying-writing/scope-validations.js +143 -146
- package/plugins/core/lib/querying-writing/simplified-helpers.js +133 -129
- package/plugins/core/lib/storage/storage-adapter.js +68 -68
- package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +31 -31
- package/plugins/core/lib/writing/many-to-many-manipulations.js +81 -81
- package/plugins/core/lib/writing/relationship-processor.js +58 -58
- package/plugins/core/rest-api-access.js +8 -8
- package/plugins/core/rest-api-anyapi-knex-plugin.js +920 -920
- package/plugins/core/rest-api-cors-plugin.js +170 -171
- package/plugins/core/rest-api-knex-plugin.js +482 -493
- package/plugins/core/rest-api-multihome-plugin.js +247 -251
- package/plugins/core/rest-api-plugin-hooks/compile-resource-schemas.js +5 -5
- package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +20 -21
- package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +44 -44
- package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +23 -23
- package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +15 -15
- package/plugins/core/rest-api-plugin-methods/add-route.js +9 -9
- package/plugins/core/rest-api-plugin-methods/check-permissions.js +5 -6
- package/plugins/core/rest-api-plugin-methods/common.js +295 -297
- package/plugins/core/rest-api-plugin-methods/delete-relationship.js +35 -37
- package/plugins/core/rest-api-plugin-methods/delete.js +57 -59
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +172 -171
- package/plugins/core/rest-api-plugin-methods/get-related.js +96 -96
- package/plugins/core/rest-api-plugin-methods/get-relationship.js +16 -18
- package/plugins/core/rest-api-plugin-methods/get.js +86 -86
- package/plugins/core/rest-api-plugin-methods/patch-relationship.js +16 -17
- package/plugins/core/rest-api-plugin-methods/patch.js +63 -64
- package/plugins/core/rest-api-plugin-methods/post-relationship.js +54 -55
- package/plugins/core/rest-api-plugin-methods/post.js +61 -62
- package/plugins/core/rest-api-plugin-methods/put.js +86 -87
- package/plugins/core/rest-api-plugin-methods/query.js +88 -88
- package/plugins/core/rest-api-plugin-methods/release.js +5 -6
- package/plugins/core/rest-api-plugin.js +76 -78
- package/plugins/core/rest-api-positioning-plugin.js +251 -252
- package/plugins/core/socketio-plugin.js +240 -236
- package/plugins/storage/local-storage.js +103 -103
- package/plugins/storage/s3-storage.js +41 -42
- package/quickTest.js +37 -37
- package/scripts/debug-anyapi-review-filter.mjs +18 -18
- package/scripts/inspect-anyapi-descriptor-country.mjs +10 -10
- package/scripts/inspect-anyapi-descriptor.mjs +10 -10
- package/scripts/run-docs.js +112 -113
- package/test-debug.js +28 -28
- package/tests/TEST_TEMPLATE.test.js +51 -51
- package/tests/anyapi-basic.test.js +42 -42
- package/tests/anyapi-cursor-pagination.test.js +48 -48
- package/tests/bulk-operations.test.js +208 -208
- package/tests/computed-fields.test.js +204 -204
- package/tests/cors.test.js +412 -413
- package/tests/curl.test.js +275 -276
- package/tests/custom-idproperty-relationships.test.js +266 -266
- package/tests/field-getters.test.js +132 -132
- package/tests/field-setters.test.js +110 -110
- package/tests/fixtures/api-configs.js +699 -699
- package/tests/helpers/storage-mode.js +32 -32
- package/tests/helpers/test-utils.js +76 -76
- package/tests/include-depth-validation.test.js +76 -76
- package/tests/includes.test.js +145 -145
- package/tests/multihome.test.js +175 -175
- package/tests/nested-includes.test.js +255 -255
- package/tests/pagination-cursor-multifield.test.js +164 -164
- package/tests/pagination-enhanced.test.js +225 -225
- package/tests/polymorphic-relationships.test.js +96 -96
- package/tests/polymorphic-simple.test.js +44 -44
- package/tests/positioning-advanced.test.js +226 -226
- package/tests/positioning-extended.test.js +399 -399
- package/tests/positioning-multifilter.test.js +204 -204
- package/tests/positioning.test.js +266 -266
- package/tests/queries.test.js +184 -184
- package/tests/query-limits.test.js +154 -155
- package/tests/relationship-endpoints.test.js +127 -127
- package/tests/relationships.test.js +178 -179
- package/tests/rest-api-access.test.js +86 -86
- package/tests/rest-api.test.js +103 -103
- package/tests/searchschema-merge.test.js +93 -93
- package/tests/socketio.test.js +326 -326
- package/tests/virtual-fields.test.js +164 -164
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { describe, it, before, beforeEach, after } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import knexLib from 'knex'
|
|
4
|
-
import {
|
|
5
|
-
cleanTables,
|
|
6
|
-
createJsonApiDocument,
|
|
7
|
-
createRelationship
|
|
8
|
-
} from './helpers/test-utils.js'
|
|
9
|
-
import { createSearchSchemaMergeApi } 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 {
|
|
5
|
+
cleanTables,
|
|
6
|
+
createJsonApiDocument,
|
|
7
|
+
createRelationship
|
|
8
|
+
} from './helpers/test-utils.js'
|
|
9
|
+
import { createSearchSchemaMergeApi } from './fixtures/api-configs.js'
|
|
10
10
|
|
|
11
11
|
// Create Knex instance for tests - always use SQLite in-memory
|
|
12
12
|
const knex = knexLib({
|
|
@@ -15,105 +15,105 @@ const knex = knexLib({
|
|
|
15
15
|
filename: ':memory:'
|
|
16
16
|
},
|
|
17
17
|
useNullAsDefault: true
|
|
18
|
-
})
|
|
18
|
+
})
|
|
19
19
|
|
|
20
20
|
describe('SearchSchema Merge Behavior', () => {
|
|
21
|
-
let api
|
|
21
|
+
let api
|
|
22
22
|
|
|
23
23
|
before(async () => {
|
|
24
24
|
// Create API instance once - NEVER in beforeEach
|
|
25
|
-
api = await createSearchSchemaMergeApi(knex)
|
|
26
|
-
})
|
|
25
|
+
api = await createSearchSchemaMergeApi(knex)
|
|
26
|
+
})
|
|
27
27
|
|
|
28
28
|
after(async () => {
|
|
29
29
|
// Clean up - destroy connection
|
|
30
|
-
await knex.destroy()
|
|
31
|
-
})
|
|
30
|
+
await knex.destroy()
|
|
31
|
+
})
|
|
32
32
|
|
|
33
33
|
beforeEach(async () => {
|
|
34
34
|
// Clean tables between tests
|
|
35
|
-
await cleanTables(knex, ['searchmerge_products', 'searchmerge_users', 'searchmerge_orders'])
|
|
36
|
-
})
|
|
35
|
+
await cleanTables(knex, ['searchmerge_products', 'searchmerge_users', 'searchmerge_orders'])
|
|
36
|
+
})
|
|
37
37
|
|
|
38
38
|
it('should merge search:true fields with explicit searchSchema', async () => {
|
|
39
39
|
// First do a query to trigger schema compilation
|
|
40
40
|
await api.resources.products.query({
|
|
41
41
|
queryParams: {}
|
|
42
|
-
})
|
|
43
|
-
|
|
42
|
+
})
|
|
43
|
+
|
|
44
44
|
// Get the compiled searchSchema for products (has both search:true and searchSchema)
|
|
45
|
-
const scope = api.resources.products
|
|
46
|
-
const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
|
|
47
|
-
|
|
45
|
+
const scope = api.resources.products
|
|
46
|
+
const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
|
|
47
|
+
|
|
48
48
|
// Verify searchSchema exists
|
|
49
|
-
assert.ok(searchSchema, 'searchSchema should exist')
|
|
50
|
-
assert.ok(searchSchema.structure, 'searchSchema should have structure')
|
|
51
|
-
|
|
49
|
+
assert.ok(searchSchema, 'searchSchema should exist')
|
|
50
|
+
assert.ok(searchSchema.structure, 'searchSchema should have structure')
|
|
51
|
+
|
|
52
52
|
// Check that search:true fields were added
|
|
53
|
-
assert.ok(searchSchema.structure.name, 'name field should be searchable')
|
|
54
|
-
assert.equal(searchSchema.structure.name.filterOperator || '=', '=', 'name should use default = operator')
|
|
55
|
-
|
|
56
|
-
assert.ok(searchSchema.structure.description, 'description field should be searchable')
|
|
57
|
-
assert.equal(searchSchema.structure.description.filterOperator || '=', '=', 'description should use default = operator')
|
|
58
|
-
|
|
59
|
-
assert.ok(searchSchema.structure.sku, 'sku field should be searchable')
|
|
60
|
-
assert.equal(searchSchema.structure.sku.filterOperator || '=', '=', 'sku should use default = operator')
|
|
61
|
-
|
|
53
|
+
assert.ok(searchSchema.structure.name, 'name field should be searchable')
|
|
54
|
+
assert.equal(searchSchema.structure.name.filterOperator || '=', '=', 'name should use default = operator')
|
|
55
|
+
|
|
56
|
+
assert.ok(searchSchema.structure.description, 'description field should be searchable')
|
|
57
|
+
assert.equal(searchSchema.structure.description.filterOperator || '=', '=', 'description should use default = operator')
|
|
58
|
+
|
|
59
|
+
assert.ok(searchSchema.structure.sku, 'sku field should be searchable')
|
|
60
|
+
assert.equal(searchSchema.structure.sku.filterOperator || '=', '=', 'sku should use default = operator')
|
|
61
|
+
|
|
62
62
|
// Check that explicit searchSchema fields override search:true
|
|
63
|
-
assert.ok(searchSchema.structure.price, 'price field should be searchable')
|
|
64
|
-
assert.equal(searchSchema.structure.price.filterOperator, 'between', 'price should use between operator from searchSchema')
|
|
65
|
-
|
|
63
|
+
assert.ok(searchSchema.structure.price, 'price field should be searchable')
|
|
64
|
+
assert.equal(searchSchema.structure.price.filterOperator, 'between', 'price should use between operator from searchSchema')
|
|
65
|
+
|
|
66
66
|
// Check that virtual fields from searchSchema are present
|
|
67
|
-
assert.ok(searchSchema.structure.category_name, 'category_name virtual field should be searchable')
|
|
68
|
-
assert.equal(searchSchema.structure.category_name.filterOperator, 'like', 'category_name should use like operator')
|
|
69
|
-
assert.equal(searchSchema.structure.category_name.actualField, 'category.name', 'category_name should map to category.name')
|
|
70
|
-
|
|
67
|
+
assert.ok(searchSchema.structure.category_name, 'category_name virtual field should be searchable')
|
|
68
|
+
assert.equal(searchSchema.structure.category_name.filterOperator, 'like', 'category_name should use like operator')
|
|
69
|
+
assert.equal(searchSchema.structure.category_name.actualField, 'category.name', 'category_name should map to category.name')
|
|
70
|
+
|
|
71
71
|
// Check explicit searchSchema field not marked with search:true
|
|
72
|
-
assert.ok(searchSchema.structure.status, 'status field should be searchable from searchSchema')
|
|
73
|
-
assert.equal(searchSchema.structure.status.filterOperator, 'in', 'status should use in operator')
|
|
74
|
-
|
|
72
|
+
assert.ok(searchSchema.structure.status, 'status field should be searchable from searchSchema')
|
|
73
|
+
assert.equal(searchSchema.structure.status.filterOperator, 'in', 'status should use in operator')
|
|
74
|
+
|
|
75
75
|
// Check that non-searchable fields are not included
|
|
76
|
-
assert.ok(!searchSchema.structure.category_id, 'category_id should not be searchable')
|
|
77
|
-
})
|
|
76
|
+
assert.ok(!searchSchema.structure.category_id, 'category_id should not be searchable')
|
|
77
|
+
})
|
|
78
78
|
|
|
79
79
|
it('should work with only search:true fields (no explicit searchSchema)', async () => {
|
|
80
80
|
// First do a query to trigger schema compilation
|
|
81
81
|
await api.resources.users.query({
|
|
82
82
|
queryParams: {}
|
|
83
|
-
})
|
|
84
|
-
|
|
83
|
+
})
|
|
84
|
+
|
|
85
85
|
// Get the compiled searchSchema for users (only search:true fields)
|
|
86
|
-
const scope = api.resources.users
|
|
87
|
-
const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
|
|
88
|
-
|
|
89
|
-
assert.ok(searchSchema, 'searchSchema should exist')
|
|
90
|
-
assert.ok(searchSchema.structure.username, 'username should be searchable')
|
|
91
|
-
assert.ok(searchSchema.structure.email, 'email should be searchable')
|
|
92
|
-
assert.ok(searchSchema.structure.bio, 'bio should be searchable')
|
|
93
|
-
assert.equal(searchSchema.structure.bio.filterOperator, 'like', 'bio should use like operator')
|
|
94
|
-
assert.ok(!searchSchema.structure.age, 'age should not be searchable')
|
|
95
|
-
})
|
|
86
|
+
const scope = api.resources.users
|
|
87
|
+
const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
|
|
88
|
+
|
|
89
|
+
assert.ok(searchSchema, 'searchSchema should exist')
|
|
90
|
+
assert.ok(searchSchema.structure.username, 'username should be searchable')
|
|
91
|
+
assert.ok(searchSchema.structure.email, 'email should be searchable')
|
|
92
|
+
assert.ok(searchSchema.structure.bio, 'bio should be searchable')
|
|
93
|
+
assert.equal(searchSchema.structure.bio.filterOperator, 'like', 'bio should use like operator')
|
|
94
|
+
assert.ok(!searchSchema.structure.age, 'age should not be searchable')
|
|
95
|
+
})
|
|
96
96
|
|
|
97
97
|
it('should work with only explicit searchSchema (no search:true)', async () => {
|
|
98
98
|
// First do a query to trigger schema compilation
|
|
99
99
|
await api.resources.orders.query({
|
|
100
100
|
queryParams: {}
|
|
101
|
-
})
|
|
102
|
-
|
|
101
|
+
})
|
|
102
|
+
|
|
103
103
|
// Get the compiled searchSchema for orders (only explicit searchSchema)
|
|
104
|
-
const scope = api.resources.orders
|
|
105
|
-
const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
|
|
106
|
-
|
|
107
|
-
assert.ok(searchSchema, 'searchSchema should exist')
|
|
108
|
-
assert.ok(searchSchema.structure.order_number, 'order_number should be searchable')
|
|
109
|
-
assert.ok(searchSchema.structure.status, 'status should be searchable')
|
|
110
|
-
assert.equal(searchSchema.structure.status.filterOperator, 'in', 'status should use in operator')
|
|
111
|
-
assert.ok(!searchSchema.structure.total, 'total should not be searchable')
|
|
112
|
-
})
|
|
113
|
-
|
|
104
|
+
const scope = api.resources.orders
|
|
105
|
+
const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
|
|
106
|
+
|
|
107
|
+
assert.ok(searchSchema, 'searchSchema should exist')
|
|
108
|
+
assert.ok(searchSchema.structure.order_number, 'order_number should be searchable')
|
|
109
|
+
assert.ok(searchSchema.structure.status, 'status should be searchable')
|
|
110
|
+
assert.equal(searchSchema.structure.status.filterOperator, 'in', 'status should use in operator')
|
|
111
|
+
assert.ok(!searchSchema.structure.total, 'total should not be searchable')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
114
|
it('should allow filtering with merged searchSchema fields', async () => {
|
|
115
|
-
const context = {}
|
|
116
|
-
|
|
115
|
+
const context = {}
|
|
116
|
+
|
|
117
117
|
// Create some test data using API methods
|
|
118
118
|
const product1 = await api.resources.products.post({
|
|
119
119
|
inputRecord: createJsonApiDocument('products', {
|
|
@@ -123,8 +123,8 @@ describe('SearchSchema Merge Behavior', () => {
|
|
|
123
123
|
sku: 'WGT-001',
|
|
124
124
|
status: 'active'
|
|
125
125
|
})
|
|
126
|
-
}, context)
|
|
127
|
-
|
|
126
|
+
}, context)
|
|
127
|
+
|
|
128
128
|
const product2 = await api.resources.products.post({
|
|
129
129
|
inputRecord: createJsonApiDocument('products', {
|
|
130
130
|
name: 'Widget B',
|
|
@@ -133,8 +133,8 @@ describe('SearchSchema Merge Behavior', () => {
|
|
|
133
133
|
sku: 'WGT-002',
|
|
134
134
|
status: 'inactive'
|
|
135
135
|
})
|
|
136
|
-
}, context)
|
|
137
|
-
|
|
136
|
+
}, context)
|
|
137
|
+
|
|
138
138
|
// Test filtering with field from search:true
|
|
139
139
|
const nameResults = await api.resources.products.query({
|
|
140
140
|
queryParams: {
|
|
@@ -142,10 +142,10 @@ describe('SearchSchema Merge Behavior', () => {
|
|
|
142
142
|
name: 'Widget A'
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
-
}, context)
|
|
146
|
-
assert.equal(nameResults.data.length, 1, 'Should find one product by name')
|
|
147
|
-
assert.equal(nameResults.data[0].attributes.name, 'Widget A')
|
|
148
|
-
|
|
145
|
+
}, context)
|
|
146
|
+
assert.equal(nameResults.data.length, 1, 'Should find one product by name')
|
|
147
|
+
assert.equal(nameResults.data[0].attributes.name, 'Widget A')
|
|
148
|
+
|
|
149
149
|
// Test filtering with field from explicit searchSchema (between operator)
|
|
150
150
|
const priceResults = await api.resources.products.query({
|
|
151
151
|
queryParams: {
|
|
@@ -153,10 +153,10 @@ describe('SearchSchema Merge Behavior', () => {
|
|
|
153
153
|
price: [40, 60] // Between 40 and 60
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
-
}, context)
|
|
157
|
-
assert.equal(priceResults.data.length, 1, 'Should find one product in price range')
|
|
158
|
-
assert.equal(priceResults.data[0].attributes.price, 50)
|
|
159
|
-
|
|
156
|
+
}, context)
|
|
157
|
+
assert.equal(priceResults.data.length, 1, 'Should find one product in price range')
|
|
158
|
+
assert.equal(priceResults.data[0].attributes.price, 50)
|
|
159
|
+
|
|
160
160
|
// Test filtering with explicit searchSchema field (in operator)
|
|
161
161
|
const statusResults = await api.resources.products.query({
|
|
162
162
|
queryParams: {
|
|
@@ -164,10 +164,10 @@ describe('SearchSchema Merge Behavior', () => {
|
|
|
164
164
|
status: ['active']
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
-
}, context)
|
|
168
|
-
assert.equal(statusResults.data.length, 1, 'Should find one active product')
|
|
169
|
-
assert.equal(statusResults.data[0].attributes.status, 'active')
|
|
170
|
-
|
|
167
|
+
}, context)
|
|
168
|
+
assert.equal(statusResults.data.length, 1, 'Should find one active product')
|
|
169
|
+
assert.equal(statusResults.data[0].attributes.status, 'active')
|
|
170
|
+
|
|
171
171
|
// Test filtering with search:true field that wasn't overridden
|
|
172
172
|
const skuResults = await api.resources.products.query({
|
|
173
173
|
queryParams: {
|
|
@@ -175,8 +175,8 @@ describe('SearchSchema Merge Behavior', () => {
|
|
|
175
175
|
sku: 'WGT-002'
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
}, context)
|
|
179
|
-
assert.equal(skuResults.data.length, 1, 'Should find one product by SKU')
|
|
180
|
-
assert.equal(skuResults.data[0].attributes.sku, 'WGT-002')
|
|
181
|
-
})
|
|
182
|
-
})
|
|
178
|
+
}, context)
|
|
179
|
+
assert.equal(skuResults.data.length, 1, 'Should find one product by SKU')
|
|
180
|
+
assert.equal(skuResults.data[0].attributes.sku, 'WGT-002')
|
|
181
|
+
})
|
|
182
|
+
})
|