json-rest-api 1.0.29 → 2.0.0
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/LICENSE +10 -670
- package/LICENSE-GPL-3.0 +674 -0
- package/LICENSE-MIT +21 -0
- package/README.md +39 -30
- package/docs/API.md +744 -1273
- package/docs/GUIDE/01-getting-started.md +338 -0
- package/docs/GUIDE/02-resources-and-relationships.md +33 -0
- package/docs/GUIDE/03-running-example.md +87 -0
- package/docs/GUIDE/04-creating-and-querying.md +181 -0
- package/docs/GUIDE/05-belongs-to.md +177 -0
- package/docs/GUIDE/06-has-many.md +184 -0
- package/docs/GUIDE/07-polymorphic-relationships.md +191 -0
- package/docs/GUIDE/08-many-to-many.md +184 -0
- package/docs/GUIDE/09-pagination-and-sorting.md +149 -0
- package/docs/GUIDE/10-put-and-patch.md +174 -0
- package/docs/GUIDE/11-relationship-endpoints.md +174 -0
- package/docs/GUIDE/12-field-transformations.md +287 -0
- package/docs/GUIDE/13-hooks-and-lifecycle.md +479 -0
- package/docs/GUIDE/14-custom-resource-methods.md +125 -0
- package/docs/GUIDE/15-query-projections.md +266 -0
- package/docs/GUIDE/{GUIDE_X_Autofiltering.md → 16-autofiltering.md} +77 -15
- package/docs/GUIDE/{GUIDE_X_Row_Policies.md → 17-row-policies.md} +137 -44
- package/docs/GUIDE/18-labels.md +86 -0
- package/docs/GUIDE/19-managed-transactions.md +179 -0
- package/docs/GUIDE/20-transaction-outcomes.md +207 -0
- package/docs/GUIDE/21-schema-and-migrations.md +634 -0
- package/docs/GUIDE/22-version-field-migrations.md +208 -0
- package/docs/GUIDE/23-fastify.md +179 -0
- package/docs/GUIDE/24-cors.md +172 -0
- package/docs/GUIDE/{GUIDE_X_URL_Management.md → 25-url-management.md} +7 -1
- package/docs/GUIDE/26-file-uploads.md +278 -0
- package/docs/GUIDE/27-bulk-operations.md +277 -0
- package/docs/GUIDE/28-socketio.md +325 -0
- package/docs/GUIDE/29-writing-plugins.md +278 -0
- package/docs/GUIDE/30-backend-capabilities.md +100 -0
- package/docs/GUIDE/31-positioning.md +181 -0
- package/docs/GUIDE/{GUIDE_Y_Appendices.md → 32-date-and-time.md} +74 -30
- package/docs/GUIDE/33-migrating-to-v2.md +3827 -0
- package/docs/GUIDE/index.md +51 -59
- package/docs/QUICKSTART.md +120 -287
- package/docs/architecture.md +75 -0
- package/docs/contributing.md +178 -0
- package/index.d.ts +36 -0
- package/index.js +1 -22
- package/lib/enhanced-logger.js +91 -85
- package/lib/error-context.js +445 -5
- package/lib/error-formatter.js +219 -59
- package/lib/knex-transaction.js +64 -0
- package/lib/logger-types.d.ts +22 -0
- package/lib/missing-package.js +4 -0
- package/lib/rest-api-errors.js +103 -8
- package/lib/runtime/json-rest-api.js +172 -0
- package/lib/transaction-types.d.ts +59 -0
- package/package.json +97 -27
- package/plugins/core/bulk-operations-plugin.js +191 -184
- package/plugins/core/connectors/express-plugin.js +148 -219
- package/plugins/core/connectors/fastify-plugin.js +125 -82
- package/plugins/core/connectors/lib/busboy-detector.js +74 -156
- package/plugins/core/connectors/lib/connector-core.js +144 -20
- package/plugins/core/connectors/lib/formidable-detector.js +63 -120
- package/plugins/core/connectors/lib/http-validators.js +77 -0
- package/plugins/core/connectors/lib/multipart-helpers.js +18 -0
- package/plugins/core/connectors/lib/request-helpers.js +10 -1
- package/plugins/core/connectors/lib/transport-http-helpers.js +89 -8
- package/plugins/core/connectors/lib/transport-route-schemas.js +7 -4
- package/plugins/core/file-handling-plugin.js +87 -177
- package/plugins/core/lib/anyapi/anyapi-registry.js +213 -164
- package/plugins/core/lib/anyapi/canonical-link-store.js +507 -0
- package/plugins/core/lib/anyapi/canonical-relationship-reader.js +598 -0
- package/plugins/core/lib/anyapi/schema-utils.js +25 -16
- package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +0 -64
- package/plugins/core/lib/dbIntrospection.js +228 -21
- package/plugins/core/lib/dbTablesOperations.js +409 -92
- package/plugins/core/lib/fractional-positioning.js +12 -48
- package/plugins/core/lib/querying/identifier-query.js +19 -0
- package/plugins/core/lib/querying/include-query-helpers.js +364 -0
- package/plugins/core/lib/querying/include-to-many.js +428 -0
- package/plugins/core/lib/querying/include-to-one.js +579 -0
- package/plugins/core/lib/querying/knex-cross-table-search.js +31 -307
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +99 -401
- package/plugins/core/lib/querying/knex-pagination-helpers.js +48 -317
- package/plugins/core/lib/querying/knex-process-includes.js +5 -110
- package/plugins/core/lib/querying/knex-query-helpers-base.js +21 -56
- package/plugins/core/lib/querying/knex-query-helpers.js +298 -448
- package/plugins/core/lib/querying/knex-relationship-includes.js +120 -2138
- package/plugins/core/lib/querying/knex-window-queries.js +62 -253
- package/plugins/core/lib/querying/query-builder-utils.js +44 -4
- package/plugins/core/lib/querying/query-constraint.d.ts +12 -0
- package/plugins/core/lib/querying/query-constraint.js +27 -0
- package/plugins/core/lib/querying/query-field-sort-helpers.js +134 -37
- package/plugins/core/lib/querying/relationship-identifiers.js +208 -0
- package/plugins/core/lib/querying/sort-helpers.js +13 -1
- package/plugins/core/lib/querying/storage-adapter-utils.js +13 -5
- package/plugins/core/lib/querying/url-helpers.js +11 -4
- package/plugins/core/lib/querying-writing/compile-schemas.js +148 -249
- package/plugins/core/lib/querying-writing/connectors-query-parser.js +33 -118
- package/plugins/core/lib/querying-writing/database-capabilities.js +149 -223
- package/plugins/core/lib/querying-writing/database-value-normalizers.js +179 -194
- package/plugins/core/lib/querying-writing/default-data-helpers.js +15 -45
- package/plugins/core/lib/querying-writing/field-selection-types.d.ts +43 -0
- package/plugins/core/lib/querying-writing/field-utils.js +113 -92
- package/plugins/core/lib/querying-writing/knex-constants.d.ts +18 -0
- package/plugins/core/lib/querying-writing/knex-constants.js +2 -26
- package/plugins/core/lib/querying-writing/knex-field-helpers.js +100 -290
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +31 -120
- package/plugins/core/lib/querying-writing/query-field-helpers.js +108 -32
- package/plugins/core/lib/querying-writing/query-field-types.d.ts +60 -0
- package/plugins/core/lib/querying-writing/relationship-contracts.js +33 -13
- package/plugins/core/lib/querying-writing/request-contracts.js +93 -38
- package/plugins/core/lib/querying-writing/resource-id-normalization.js +23 -5
- package/plugins/core/lib/querying-writing/response-options.js +35 -0
- package/plugins/core/lib/querying-writing/schema-helpers.js +203 -324
- package/plugins/core/lib/querying-writing/scope-validations.js +92 -290
- package/plugins/core/lib/querying-writing/simplified-helpers.js +113 -712
- package/plugins/core/lib/storage/canonical-data-helpers.js +683 -0
- package/plugins/core/lib/storage/canonical-storage-mapping.js +51 -20
- package/plugins/core/lib/storage/ordinary-data-helpers.js +710 -0
- package/plugins/core/lib/storage/storage-adapter.js +128 -62
- package/plugins/core/lib/storage/storage-mapping.js +78 -28
- package/plugins/core/lib/storage/storage-types.d.ts +502 -0
- package/plugins/core/lib/writing/many-to-many-manipulations.js +114 -284
- package/plugins/core/lib/writing/relationship-processor.js +94 -158
- package/plugins/core/lib/writing/resource-version.js +172 -0
- package/plugins/core/lib/writing/reverse-relationship-manipulations.js +110 -0
- package/plugins/core/rest-api-anyapi-knex-plugin.js +116 -2485
- package/plugins/core/rest-api-autofilter-plugin.js +17 -19
- package/plugins/core/rest-api-cors-plugin.js +52 -205
- package/plugins/core/rest-api-knex-plugin.js +45 -1077
- package/plugins/core/rest-api-label-plugin.js +25 -38
- package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +5 -5
- package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +9 -6
- package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +7 -26
- package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +20 -26
- package/plugins/core/rest-api-plugin-methods/add-route.js +4 -6
- package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +42 -13
- package/plugins/core/rest-api-plugin-methods/check-permissions.js +11 -5
- package/plugins/core/rest-api-plugin-methods/common.js +291 -395
- package/plugins/core/rest-api-plugin-methods/delete-relationship.js +49 -47
- package/plugins/core/rest-api-plugin-methods/delete.js +31 -25
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +108 -148
- package/plugins/core/rest-api-plugin-methods/get-related.js +113 -305
- package/plugins/core/rest-api-plugin-methods/get-relationship.js +25 -18
- package/plugins/core/rest-api-plugin-methods/get.js +36 -68
- package/plugins/core/rest-api-plugin-methods/lifecycle-types.d.ts +225 -0
- package/plugins/core/rest-api-plugin-methods/patch-relationship.js +28 -25
- package/plugins/core/rest-api-plugin-methods/patch.js +46 -99
- package/plugins/core/rest-api-plugin-methods/post-relationship.js +49 -53
- package/plugins/core/rest-api-plugin-methods/post.js +51 -51
- package/plugins/core/rest-api-plugin-methods/put.js +69 -148
- package/plugins/core/rest-api-plugin-methods/query.js +37 -81
- package/plugins/core/rest-api-plugin-methods/release.js +2 -1
- package/plugins/core/rest-api-plugin.js +32 -46
- package/plugins/core/rest-api-positioning-plugin.js +183 -628
- package/plugins/core/rest-api-query-projections-plugin.js +19 -102
- package/plugins/core/rest-api-row-policy-plugin.js +3 -3
- package/plugins/core/socketio-plugin.js +364 -344
- package/plugins/storage/local-storage.js +40 -36
- package/types/autofilter.d.ts +57 -0
- package/types/bulk-methods.d.ts +63 -0
- package/types/errors.d.ts +118 -0
- package/types/fastify-options.d.ts +19 -0
- package/types/file-detectors.d.ts +33 -0
- package/types/file-storage.d.ts +48 -0
- package/types/hook-context.d.ts +180 -0
- package/types/plugin-options.d.ts +53 -0
- package/types/relationship-methods.d.ts +74 -0
- package/types/representations.d.ts +50 -0
- package/types/resource-methods.d.ts +95 -0
- package/types/resource-schema.d.ts +157 -0
- package/types/row-policy.d.ts +31 -0
- package/types/runtime.d.ts +132 -0
- package/types/transactions.d.ts +21 -0
- package/.claude/agents/library-guide-expert.md +0 -30
- package/.claude/agents/rest-api-internals-expert.md +0 -54
- package/.claude/settings.local.json +0 -70
- package/AGENTS.md +0 -34
- package/CLAUDE.md +0 -160
- package/CRUSH.md +0 -55
- package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +0 -180
- package/SINGLE_VALIDATION_CONTRACT_TODO.txt +0 -172
- package/TODO.todo +0 -13
- package/connectors_ideas/fastify.js +0 -217
- package/connectors_ideas/koa-plugin.js +0 -112
- package/docs/.bundle/config +0 -2
- package/docs/COMPARISON.md +0 -433
- package/docs/GUIDE/GUIDE_1_Initial_Setup.md +0 -1057
- package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +0 -88
- package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +0 -808
- package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +0 -491
- package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +0 -398
- package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +0 -898
- package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +0 -712
- package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +0 -1247
- package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +0 -748
- package/docs/GUIDE/GUIDE_2_9_Relationships_Urls.md +0 -517
- package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +0 -66
- package/docs/GUIDE/GUIDE_3_Field_Transformations.md +0 -1252
- package/docs/GUIDE/GUIDE_5_Non-Db_Resources.md +0 -1107
- package/docs/GUIDE/GUIDE_6_Positioning.md +0 -692
- package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +0 -1491
- package/docs/GUIDE/GUIDE_X_Bulk_Operations.md +0 -732
- package/docs/GUIDE/GUIDE_X_Cors.md +0 -1957
- package/docs/GUIDE/GUIDE_X_Fastify.md +0 -78
- package/docs/GUIDE/GUIDE_X_File_Uploads.md +0 -729
- package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +0 -370
- package/docs/GUIDE/GUIDE_X_Query_Projections.md +0 -159
- package/docs/GUIDE/GUIDE_X_SocketIO.md +0 -1040
- package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +0 -201
- package/docs/Gemfile +0 -6
- package/docs/Gemfile.lock +0 -308
- package/docs/ONBOARDING.md +0 -315
- package/docs/_config.yml +0 -36
- package/docs/_layouts/default.html +0 -123
- package/docs/apple-touch-icon.svg +0 -4
- package/docs/assets/css/style.scss +0 -392
- package/docs/favicon.svg +0 -4
- package/docs/index.md +0 -164
- package/docs/mini-guides/file-uploads-complete.md +0 -161
- package/eslint.config.js +0 -13
- package/examples/url-override-example.js +0 -137
- package/fixing_plan.md +0 -112
- package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +0 -657
- package/plugins/core/lib/querying-writing/return-record-settings.js +0 -47
- package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +0 -110
- package/plugins/storage/s3-storage.js +0 -93
- package/quickTest.js +0 -136
- package/scripts/debug-anyapi-review-filter.mjs +0 -63
- package/scripts/inspect-anyapi-descriptor-country.mjs +0 -19
- package/scripts/inspect-anyapi-descriptor.mjs +0 -19
- package/scripts/run-docs.js +0 -133
- package/test-debug.js +0 -59
- package/tests/README.md +0 -202
- package/tests/TEST_TEMPLATE.test.js +0 -178
- package/tests/anyapi-basic.test.js +0 -105
- package/tests/anyapi-cursor-pagination.test.js +0 -148
- package/tests/anyapi-custom-idproperty.test.js +0 -163
- package/tests/autofilter.test.js +0 -463
- package/tests/bulk-operations.test.js +0 -657
- package/tests/computed-fields.test.js +0 -586
- package/tests/cors.test.js +0 -834
- package/tests/curl.test.js +0 -467
- package/tests/custom-id-mysql-returning-fallback.test.js +0 -128
- package/tests/custom-id-simplified-post.test.js +0 -75
- package/tests/custom-idproperty-relationships.test.js +0 -932
- package/tests/db-introspection.test.js +0 -521
- package/tests/db-table-operations.test.js +0 -798
- package/tests/fastify-plugin.test.js +0 -184
- package/tests/field-getters.test.js +0 -370
- package/tests/field-setters.test.js +0 -306
- package/tests/file-handling.test.js +0 -155
- package/tests/fixtures/api-configs.js +0 -2566
- package/tests/full-jsonapi-belongsto-linkage.test.js +0 -150
- package/tests/helpers/fake-fastify.js +0 -99
- package/tests/helpers/storage-mode.js +0 -65
- package/tests/helpers/test-utils.js +0 -203
- package/tests/http-connectors-parity.test.js +0 -395
- package/tests/id-normalization.test.js +0 -444
- package/tests/include-depth-validation.test.js +0 -331
- package/tests/includes.test.js +0 -427
- package/tests/local-storage.test.js +0 -96
- package/tests/logical-id-write-validation.test.js +0 -151
- package/tests/nested-includes.test.js +0 -617
- package/tests/pagination-boundaries.test.js +0 -154
- package/tests/pagination-contract.test.js +0 -122
- package/tests/pagination-cursor-multifield.test.js +0 -641
- package/tests/pagination-enhanced.test.js +0 -563
- package/tests/polymorphic-relationships.test.js +0 -347
- package/tests/polymorphic-simple.test.js +0 -148
- package/tests/positioning-advanced.test.js +0 -692
- package/tests/positioning-extended.test.js +0 -915
- package/tests/positioning-multifilter.test.js +0 -699
- package/tests/positioning.test.js +0 -560
- package/tests/post-commit-hooks.test.js +0 -120
- package/tests/projected-fields.test.js +0 -319
- package/tests/public-exports.test.js +0 -15
- package/tests/put-create-parity.test.js +0 -216
- package/tests/queries.test.js +0 -521
- package/tests/query-limits.test.js +0 -584
- package/tests/relationship-endpoints.test.js +0 -620
- package/tests/relationship-include-storage-adapter.test.js +0 -56
- package/tests/relationships.test.js +0 -681
- package/tests/rest-api.test.js +0 -337
- package/tests/return-record-settings.test.js +0 -184
- package/tests/row-policy.test.js +0 -489
- package/tests/s3-storage.test.js +0 -38
- package/tests/searchschema-knex-behavior.test.js +0 -187
- package/tests/searchschema-merge.test.js +0 -151
- package/tests/socketio.test.js +0 -860
- package/tests/storage-mapping.test.js +0 -362
- package/tests/temporal-boundaries.test.js +0 -208
- package/tests/test-development-guide.md +0 -370
- package/tests/virtual-fields.test.js +0 -404
|
@@ -1,187 +0,0 @@
|
|
|
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
|
-
} from './helpers/test-utils.js'
|
|
8
|
-
import { storageMode } from './helpers/storage-mode.js'
|
|
9
|
-
import { createSearchSchemaMergeApi } from './fixtures/api-configs.js'
|
|
10
|
-
|
|
11
|
-
const maybeDescribe = storageMode.isAnyApi() ? describe.skip : describe
|
|
12
|
-
|
|
13
|
-
// Create Knex instance for tests - always use SQLite in-memory
|
|
14
|
-
const knex = knexLib({
|
|
15
|
-
client: 'better-sqlite3',
|
|
16
|
-
connection: {
|
|
17
|
-
filename: ':memory:'
|
|
18
|
-
},
|
|
19
|
-
useNullAsDefault: true
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
maybeDescribe('SearchSchema Merge Behavior (Knex execution)', () => {
|
|
23
|
-
let api
|
|
24
|
-
|
|
25
|
-
before(async () => {
|
|
26
|
-
api = await createSearchSchemaMergeApi(knex)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
after(async () => {
|
|
30
|
-
await knex.destroy()
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
beforeEach(async () => {
|
|
34
|
-
await cleanTables(knex, ['searchmerge_products', 'searchmerge_users', 'searchmerge_orders'])
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('should use exact matching for search:true strings unless filterOperator is explicit', async () => {
|
|
38
|
-
const context = {}
|
|
39
|
-
|
|
40
|
-
await api.resources.users.post({
|
|
41
|
-
inputRecord: createJsonApiDocument('users', {
|
|
42
|
-
username: 'ann',
|
|
43
|
-
email: 'ann@example.com',
|
|
44
|
-
bio: 'Writes API guides'
|
|
45
|
-
})
|
|
46
|
-
}, context)
|
|
47
|
-
|
|
48
|
-
await api.resources.users.post({
|
|
49
|
-
inputRecord: createJsonApiDocument('users', {
|
|
50
|
-
username: 'anna',
|
|
51
|
-
email: 'anna@example.com',
|
|
52
|
-
bio: 'Writing API tutorials'
|
|
53
|
-
})
|
|
54
|
-
}, context)
|
|
55
|
-
|
|
56
|
-
const usernameResults = await api.resources.users.query({
|
|
57
|
-
queryParams: {
|
|
58
|
-
filters: {
|
|
59
|
-
username: 'ann'
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}, context)
|
|
63
|
-
|
|
64
|
-
assert.equal(usernameResults.data.length, 1, 'search:true username should use exact matching')
|
|
65
|
-
assert.equal(usernameResults.data[0].attributes.username, 'ann')
|
|
66
|
-
|
|
67
|
-
const bioResults = await api.resources.users.query({
|
|
68
|
-
queryParams: {
|
|
69
|
-
filters: {
|
|
70
|
-
bio: 'Writ'
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}, context)
|
|
74
|
-
|
|
75
|
-
assert.equal(bioResults.data.length, 2, 'explicit like operator should still support partial matching')
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('should allow filtering with merged searchSchema fields', async () => {
|
|
79
|
-
const context = {}
|
|
80
|
-
|
|
81
|
-
await api.resources.products.post({
|
|
82
|
-
inputRecord: createJsonApiDocument('products', {
|
|
83
|
-
name: 'Widget A',
|
|
84
|
-
description: 'A great widget',
|
|
85
|
-
price: 50,
|
|
86
|
-
sku: 'WGT-001',
|
|
87
|
-
status: 'active'
|
|
88
|
-
})
|
|
89
|
-
}, context)
|
|
90
|
-
|
|
91
|
-
await api.resources.products.post({
|
|
92
|
-
inputRecord: createJsonApiDocument('products', {
|
|
93
|
-
name: 'Widget B',
|
|
94
|
-
description: 'Another widget',
|
|
95
|
-
price: 150,
|
|
96
|
-
sku: 'WGT-002',
|
|
97
|
-
status: 'inactive'
|
|
98
|
-
})
|
|
99
|
-
}, context)
|
|
100
|
-
|
|
101
|
-
const nameResults = await api.resources.products.query({
|
|
102
|
-
queryParams: {
|
|
103
|
-
filters: {
|
|
104
|
-
name: 'Widget A'
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}, context)
|
|
108
|
-
assert.equal(nameResults.data.length, 1, 'Should find one product by name')
|
|
109
|
-
assert.equal(nameResults.data[0].attributes.name, 'Widget A')
|
|
110
|
-
|
|
111
|
-
const priceResults = await api.resources.products.query({
|
|
112
|
-
queryParams: {
|
|
113
|
-
filters: {
|
|
114
|
-
price: [40, 60]
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}, context)
|
|
118
|
-
assert.equal(priceResults.data.length, 1, 'Should find one product in price range')
|
|
119
|
-
assert.equal(priceResults.data[0].attributes.price, 50)
|
|
120
|
-
|
|
121
|
-
const statusResults = await api.resources.products.query({
|
|
122
|
-
queryParams: {
|
|
123
|
-
filters: {
|
|
124
|
-
status: ['active']
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}, context)
|
|
128
|
-
assert.equal(statusResults.data.length, 1, 'Should find one active product')
|
|
129
|
-
assert.equal(statusResults.data[0].attributes.status, 'active')
|
|
130
|
-
|
|
131
|
-
const skuResults = await api.resources.products.query({
|
|
132
|
-
queryParams: {
|
|
133
|
-
filters: {
|
|
134
|
-
sku: 'WGT-002'
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}, context)
|
|
138
|
-
assert.equal(skuResults.data.length, 1, 'Should find one product by SKU')
|
|
139
|
-
assert.equal(skuResults.data[0].attributes.sku, 'WGT-002')
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
it('should support arbitrary public filter keys when searchSchema maps them intentionally', async () => {
|
|
143
|
-
const context = {}
|
|
144
|
-
|
|
145
|
-
await api.resources.products.post({
|
|
146
|
-
inputRecord: createJsonApiDocument('products', {
|
|
147
|
-
name: 'Widget Alpha',
|
|
148
|
-
description: 'The great all-purpose widget',
|
|
149
|
-
price: 50,
|
|
150
|
-
sku: 'WGT-101',
|
|
151
|
-
status: 'active'
|
|
152
|
-
})
|
|
153
|
-
}, context)
|
|
154
|
-
|
|
155
|
-
await api.resources.products.post({
|
|
156
|
-
inputRecord: createJsonApiDocument('products', {
|
|
157
|
-
name: 'Widget Beta',
|
|
158
|
-
description: 'A quiet replacement unit',
|
|
159
|
-
price: 60,
|
|
160
|
-
sku: 'WGT-102',
|
|
161
|
-
status: 'inactive'
|
|
162
|
-
})
|
|
163
|
-
}, context)
|
|
164
|
-
|
|
165
|
-
const aliasResults = await api.resources.products.query({
|
|
166
|
-
queryParams: {
|
|
167
|
-
filters: {
|
|
168
|
-
term: 'great'
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}, context)
|
|
172
|
-
|
|
173
|
-
assert.equal(aliasResults.data.length, 1, 'term should be resolved by backend search mapping')
|
|
174
|
-
assert.equal(aliasResults.data[0].attributes.name, 'Widget Alpha')
|
|
175
|
-
|
|
176
|
-
const customResults = await api.resources.products.query({
|
|
177
|
-
queryParams: {
|
|
178
|
-
filters: {
|
|
179
|
-
availability: true
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}, context)
|
|
183
|
-
|
|
184
|
-
assert.equal(customResults.data.length, 1, 'availability should be interpreted by backend custom filter logic')
|
|
185
|
-
assert.equal(customResults.data[0].attributes.status, 'active')
|
|
186
|
-
})
|
|
187
|
-
})
|
|
@@ -1,151 +0,0 @@
|
|
|
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
|
-
} from './helpers/test-utils.js'
|
|
7
|
-
import { createSearchSchemaMergeApi } from './fixtures/api-configs.js'
|
|
8
|
-
import { generateSearchSchemaFromSchema } from '../plugins/core/lib/querying-writing/schema-helpers.js'
|
|
9
|
-
|
|
10
|
-
// Create Knex instance for tests - always use SQLite in-memory
|
|
11
|
-
const knex = knexLib({
|
|
12
|
-
client: 'better-sqlite3',
|
|
13
|
-
connection: {
|
|
14
|
-
filename: ':memory:'
|
|
15
|
-
},
|
|
16
|
-
useNullAsDefault: true
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
describe('SearchSchema Merge Behavior', () => {
|
|
20
|
-
let api
|
|
21
|
-
|
|
22
|
-
before(async () => {
|
|
23
|
-
// Create API instance once - NEVER in beforeEach
|
|
24
|
-
api = await createSearchSchemaMergeApi(knex)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
after(async () => {
|
|
28
|
-
// Clean up - destroy connection
|
|
29
|
-
await knex.destroy()
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
beforeEach(async () => {
|
|
33
|
-
// Clean tables between tests
|
|
34
|
-
await cleanTables(knex, ['searchmerge_products', 'searchmerge_users', 'searchmerge_orders'])
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('should merge search:true fields with explicit searchSchema', async () => {
|
|
38
|
-
// First do a query to trigger schema compilation
|
|
39
|
-
await api.resources.products.query({
|
|
40
|
-
queryParams: {}
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
// Get the compiled searchSchema for products (has both search:true and searchSchema)
|
|
44
|
-
const scope = api.resources.products
|
|
45
|
-
const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
|
|
46
|
-
|
|
47
|
-
// Verify searchSchema exists
|
|
48
|
-
assert.ok(searchSchema, 'searchSchema should exist')
|
|
49
|
-
assert.ok(searchSchema.structure, 'searchSchema should have structure')
|
|
50
|
-
|
|
51
|
-
// Check that search:true fields were added
|
|
52
|
-
assert.ok(searchSchema.structure.name, 'name field should be searchable')
|
|
53
|
-
assert.equal(searchSchema.structure.name.filterOperator || '=', '=', 'name should use default = operator')
|
|
54
|
-
|
|
55
|
-
assert.ok(searchSchema.structure.description, 'description field should be searchable')
|
|
56
|
-
assert.equal(searchSchema.structure.description.filterOperator || '=', '=', 'description should use default = operator')
|
|
57
|
-
|
|
58
|
-
assert.ok(searchSchema.structure.sku, 'sku field should be searchable')
|
|
59
|
-
assert.equal(searchSchema.structure.sku.filterOperator || '=', '=', 'sku should use default = operator')
|
|
60
|
-
|
|
61
|
-
// Check that explicit searchSchema fields override search:true
|
|
62
|
-
assert.ok(searchSchema.structure.price, 'price field should be searchable')
|
|
63
|
-
assert.equal(searchSchema.structure.price.filterOperator, 'between', 'price should use between operator from searchSchema')
|
|
64
|
-
|
|
65
|
-
// Check that arbitrary public filter keys are allowed when defined intentionally
|
|
66
|
-
assert.ok(searchSchema.structure.term, 'term alias should be searchable')
|
|
67
|
-
assert.deepEqual(searchSchema.structure.term.oneOf, ['name', 'description'], 'term should map to multiple fields')
|
|
68
|
-
|
|
69
|
-
// Check that virtual fields from searchSchema are present
|
|
70
|
-
assert.ok(searchSchema.structure.category_name, 'category_name virtual field should be searchable')
|
|
71
|
-
assert.equal(searchSchema.structure.category_name.filterOperator, 'like', 'category_name should use like operator')
|
|
72
|
-
assert.equal(searchSchema.structure.category_name.actualField, 'category.name', 'category_name should map to category.name')
|
|
73
|
-
|
|
74
|
-
assert.ok(searchSchema.structure.availability, 'availability custom filter should be searchable')
|
|
75
|
-
assert.equal(typeof searchSchema.structure.availability.applyFilter, 'function', 'availability should use custom backend logic')
|
|
76
|
-
|
|
77
|
-
// Check explicit searchSchema field not marked with search:true
|
|
78
|
-
assert.ok(searchSchema.structure.status, 'status field should be searchable from searchSchema')
|
|
79
|
-
assert.equal(searchSchema.structure.status.filterOperator, 'in', 'status should use in operator')
|
|
80
|
-
|
|
81
|
-
// Check that non-searchable fields are not included
|
|
82
|
-
assert.ok(!searchSchema.structure.category_id, 'category_id should not be searchable')
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('should work with only search:true fields (no explicit searchSchema)', async () => {
|
|
86
|
-
// First do a query to trigger schema compilation
|
|
87
|
-
await api.resources.users.query({
|
|
88
|
-
queryParams: {}
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
// Get the compiled searchSchema for users (only search:true fields)
|
|
92
|
-
const scope = api.resources.users
|
|
93
|
-
const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
|
|
94
|
-
|
|
95
|
-
assert.ok(searchSchema, 'searchSchema should exist')
|
|
96
|
-
assert.ok(searchSchema.structure.username, 'username should be searchable')
|
|
97
|
-
assert.ok(searchSchema.structure.email, 'email should be searchable')
|
|
98
|
-
assert.ok(searchSchema.structure.bio, 'bio should be searchable')
|
|
99
|
-
assert.equal(searchSchema.structure.bio.filterOperator, 'like', 'bio should use like operator')
|
|
100
|
-
assert.ok(!searchSchema.structure.age, 'age should not be searchable')
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('should work with only explicit searchSchema (no search:true)', async () => {
|
|
104
|
-
// First do a query to trigger schema compilation
|
|
105
|
-
await api.resources.orders.query({
|
|
106
|
-
queryParams: {}
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
// Get the compiled searchSchema for orders (only explicit searchSchema)
|
|
110
|
-
const scope = api.resources.orders
|
|
111
|
-
const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
|
|
112
|
-
|
|
113
|
-
assert.ok(searchSchema, 'searchSchema should exist')
|
|
114
|
-
assert.ok(searchSchema.structure.order_number, 'order_number should be searchable')
|
|
115
|
-
assert.ok(searchSchema.structure.status, 'status should be searchable')
|
|
116
|
-
assert.equal(searchSchema.structure.status.filterOperator, 'in', 'status should use in operator')
|
|
117
|
-
assert.ok(!searchSchema.structure.total, 'total should not be searchable')
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it('should reject invalid filter keys that are not part of the public searchSchema contract', async () => {
|
|
121
|
-
await assert.rejects(
|
|
122
|
-
async () => {
|
|
123
|
-
await api.resources.products.query({
|
|
124
|
-
queryParams: {
|
|
125
|
-
filters: {
|
|
126
|
-
invalid_field: 'nope'
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
})
|
|
130
|
-
},
|
|
131
|
-
(error) => error?.code === 'REST_API_VALIDATION',
|
|
132
|
-
'Should reject fields that are not defined in searchSchema'
|
|
133
|
-
)
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
it('should keep canonical belongsTo field keys searchable alongside relationship aliases', () => {
|
|
137
|
-
const searchSchema = generateSearchSchemaFromSchema({
|
|
138
|
-
petId: {
|
|
139
|
-
type: 'id',
|
|
140
|
-
belongsTo: 'pets',
|
|
141
|
-
as: 'pet',
|
|
142
|
-
search: true
|
|
143
|
-
}
|
|
144
|
-
}, null)
|
|
145
|
-
|
|
146
|
-
assert.ok(searchSchema.pet, 'relationship alias should stay searchable')
|
|
147
|
-
assert.equal(searchSchema.pet.actualField, 'petId')
|
|
148
|
-
assert.ok(searchSchema.petId, 'canonical field key should stay searchable for parent filters')
|
|
149
|
-
assert.equal(searchSchema.petId.actualField, 'petId')
|
|
150
|
-
})
|
|
151
|
-
})
|