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.
- package/CLAUDE.md +1 -2
- package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +180 -0
- package/SINGLE_VALIDATION_CONTRACT_TODO.txt +172 -0
- package/docs/API.md +51 -14
- package/docs/COMPARISON.md +2 -2
- package/docs/GUIDE/GUIDE_1_Initial_Setup.md +28 -1
- package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +5 -1
- package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +3 -1
- package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +12 -60
- package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +2 -2
- package/docs/GUIDE/GUIDE_3_Field_Transformations.md +5 -1
- package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +2 -0
- package/docs/GUIDE/GUIDE_X_Autofiltering.md +355 -0
- package/docs/GUIDE/GUIDE_X_Fastify.md +78 -0
- package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +250 -0
- package/docs/GUIDE/GUIDE_X_Query_Projections.md +159 -0
- package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +199 -0
- package/docs/GUIDE/index.md +18 -9
- package/docs/QUICKSTART.md +5 -1
- package/docs/index.md +1 -1
- package/index.js +3 -2
- package/package.json +4 -4
- package/plugins/core/connectors/express-plugin.js +97 -184
- package/plugins/core/connectors/fastify-plugin.js +206 -0
- package/plugins/core/connectors/lib/connector-core.js +195 -0
- package/plugins/core/connectors/lib/transport-http-helpers.js +122 -0
- package/plugins/core/connectors/lib/transport-route-schemas.js +92 -0
- package/plugins/core/lib/dbIntrospection.js +1078 -0
- package/plugins/core/lib/dbTablesOperations.js +1104 -160
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +5 -2
- package/plugins/core/lib/querying/knex-process-includes.js +3 -4
- package/plugins/core/lib/querying/knex-query-helpers.js +17 -71
- package/plugins/core/lib/querying/knex-relationship-includes.js +466 -206
- package/plugins/core/lib/querying/query-builder-utils.js +19 -0
- package/plugins/core/lib/querying/query-field-sort-helpers.js +135 -0
- package/plugins/core/lib/querying/sort-helpers.js +33 -0
- package/plugins/core/lib/querying/storage-adapter-utils.js +51 -0
- package/plugins/core/lib/querying/url-helpers.js +8 -2
- package/plugins/core/lib/querying-writing/compile-schemas.js +12 -1
- package/plugins/core/lib/querying-writing/knex-field-helpers.js +83 -13
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +13 -12
- package/plugins/core/lib/querying-writing/query-field-helpers.js +88 -0
- package/plugins/core/lib/querying-writing/request-contracts.js +590 -0
- package/plugins/core/lib/storage/canonical-storage-mapping.js +113 -0
- package/plugins/core/lib/storage/storage-adapter.js +79 -2
- package/plugins/core/lib/storage/storage-mapping.js +204 -0
- package/plugins/core/rest-api-anyapi-knex-plugin.js +355 -228
- package/plugins/core/rest-api-autofilter-plugin.js +414 -0
- package/plugins/core/rest-api-knex-plugin.js +249 -211
- package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +10 -5
- package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +12 -6
- package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +41 -0
- package/plugins/core/rest-api-plugin-methods/common.js +121 -83
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +6 -1
- package/plugins/core/rest-api-plugin-methods/get.js +23 -14
- package/plugins/core/rest-api-plugin-methods/patch.js +28 -12
- package/plugins/core/rest-api-plugin-methods/post.js +13 -8
- package/plugins/core/rest-api-plugin-methods/put.js +45 -21
- package/plugins/core/rest-api-plugin-methods/query.js +15 -53
- package/plugins/core/rest-api-plugin.js +2 -0
- package/plugins/core/rest-api-positioning-plugin.js +1 -1
- package/plugins/core/rest-api-query-projections-plugin.js +130 -0
- package/plugins/core/socketio-plugin.js +1 -3
- package/tests/autofilter.test.js +463 -0
- package/tests/custom-id-simplified-post.test.js +75 -0
- package/tests/custom-idproperty-relationships.test.js +33 -0
- package/tests/db-introspection.test.js +518 -0
- package/tests/db-table-operations.test.js +781 -0
- package/tests/fastify-plugin.test.js +141 -0
- package/tests/field-getters.test.js +1 -2
- package/tests/field-setters.test.js +0 -4
- package/tests/fixtures/api-configs.js +188 -219
- package/tests/helpers/fake-fastify.js +99 -0
- package/tests/http-connectors-parity.test.js +235 -0
- package/tests/projected-fields.test.js +319 -0
- package/tests/relationships.test.js +3 -3
- package/tests/searchschema-knex-behavior.test.js +187 -0
- package/tests/searchschema-merge.test.js +21 -69
- package/tests/storage-mapping.test.js +210 -0
- package/docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md +0 -707
- package/docs/GUIDE/GUIDE_X_Multihome.md +0 -666
- package/plugins/core/lib/querying-writing/payload-validators.js +0 -1314
- package/plugins/core/rest-api-access.js +0 -459
- package/plugins/core/rest-api-multihome-plugin.js +0 -314
- package/tests/multihome.test.js +0 -553
- package/tests/multihome.test.js.bak +0 -550
- package/tests/ownership-optout.test.js +0 -76
- package/tests/rest-api-access.test.js +0 -206
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getForeignKeyFields as getForeignKeyFieldsFromUtils } from '../querying-writing/field-utils.js'
|
|
2
2
|
import { RELATIONSHIPS_KEY, RELATIONSHIP_METADATA_KEY, ROW_NUMBER_KEY, COMPUTED_DEPENDENCIES_KEY, getSchemaStructure } from '../querying-writing/knex-constants.js'
|
|
3
3
|
import { getUrlPrefix, buildResourceUrl, buildRelationshipUrl } from './url-helpers.js'
|
|
4
|
+
import { translateRecordFromStorage } from '../storage/storage-mapping.js'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Transforms a flat database record into JSON:API resource format
|
|
@@ -75,14 +76,16 @@ import { getUrlPrefix, buildResourceUrl, buildRelationshipUrl } from './url-help
|
|
|
75
76
|
const toJsonApi = (scope, record, deps) => {
|
|
76
77
|
if (!record) return null
|
|
77
78
|
|
|
78
|
-
const
|
|
79
|
+
const schemaInfo = scope.vars.schemaInfo
|
|
80
|
+
const schemaInstance = schemaInfo.schemaInstance
|
|
81
|
+
const logicalRecord = translateRecordFromStorage(record, schemaInfo)
|
|
79
82
|
|
|
80
83
|
const { context } = deps
|
|
81
84
|
const scopeName = context.scopeName
|
|
82
85
|
const idProperty = context.schemaInfo?.idProperty || 'id'
|
|
83
86
|
const polymorphicFields = context.polymorphicFields || new Set()
|
|
84
87
|
|
|
85
|
-
const { id, ...allAttributes } =
|
|
88
|
+
const { id, ...allAttributes } = logicalRecord
|
|
86
89
|
|
|
87
90
|
const foreignKeys = schemaInstance ? getForeignKeyFieldsFromUtils(schemaInstance) : new Set()
|
|
88
91
|
|
|
@@ -111,7 +111,6 @@ export const processIncludes = async (scope, records, deps) => {
|
|
|
111
111
|
|
|
112
112
|
const db = context.transaction || knex
|
|
113
113
|
const queryParams = context.queryParams
|
|
114
|
-
const idProperty = context.schemaInfo.idProperty
|
|
115
114
|
|
|
116
115
|
if (!queryParams.include) {
|
|
117
116
|
return []
|
|
@@ -124,15 +123,15 @@ export const processIncludes = async (scope, records, deps) => {
|
|
|
124
123
|
records,
|
|
125
124
|
scopeName,
|
|
126
125
|
includeParam: queryParams.include,
|
|
127
|
-
fields: queryParams.fields || {}
|
|
128
|
-
idProperty
|
|
126
|
+
fields: queryParams.fields || {}
|
|
129
127
|
},
|
|
130
128
|
{
|
|
131
129
|
context: {
|
|
132
130
|
scopes,
|
|
133
131
|
log,
|
|
134
132
|
knex: db,
|
|
135
|
-
capabilities: api?.knex?.capabilities
|
|
133
|
+
capabilities: api?.knex?.capabilities,
|
|
134
|
+
requestContext: context
|
|
136
135
|
}
|
|
137
136
|
}
|
|
138
137
|
)
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { analyzeRequiredIndexes, buildJoinChain } from './knex-cross-table-search.js'
|
|
2
|
+
import { createStorageAdapterUtilities } from './storage-adapter-utils.js'
|
|
2
3
|
|
|
3
4
|
// Resolve operator with sensible defaults for fields declared in searchSchema.
|
|
4
5
|
// - If filterOperator is provided, use it as-is
|
|
5
|
-
// - If
|
|
6
|
-
// - Otherwise default to '='
|
|
6
|
+
// - If no operator is provided, default to '='
|
|
7
7
|
export function resolveSearchOperator (fieldDef) {
|
|
8
8
|
if (fieldDef && fieldDef.filterOperator) return String(fieldDef.filterOperator)
|
|
9
|
-
if (fieldDef && fieldDef.type === 'string') return 'like'
|
|
10
9
|
return '='
|
|
11
10
|
}
|
|
12
11
|
|
|
@@ -74,58 +73,6 @@ export function applyWhereForOperator ({ builder, columnRef, operator, value, kn
|
|
|
74
73
|
builder[method](columnRef, operator || '=', firstVal)
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
const createAdapterUtilities = (hookParams, { getStorageAdapter } = {}) => {
|
|
78
|
-
const context = hookParams.context || {}
|
|
79
|
-
const storageCache = new Map()
|
|
80
|
-
|
|
81
|
-
const fetchStorageAdapter = (scopeName) => {
|
|
82
|
-
if (!scopeName) return null
|
|
83
|
-
if (storageCache.has(scopeName)) return storageCache.get(scopeName)
|
|
84
|
-
|
|
85
|
-
let adapter = null
|
|
86
|
-
if (scopeName === context?.knexQuery?.scopeName) {
|
|
87
|
-
adapter = context.storageAdapter ||
|
|
88
|
-
context.knexQuery?.storageAdapter ||
|
|
89
|
-
(typeof getStorageAdapter === 'function' ? getStorageAdapter(scopeName) : null)
|
|
90
|
-
if (adapter && !context.storageAdapter) {
|
|
91
|
-
context.storageAdapter = adapter
|
|
92
|
-
}
|
|
93
|
-
} else {
|
|
94
|
-
adapter = typeof getStorageAdapter === 'function' ? getStorageAdapter(scopeName) : null
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
storageCache.set(scopeName, adapter)
|
|
98
|
-
return adapter
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const defaultAliasForScope = (scopeName) => {
|
|
102
|
-
if (scopeName === context?.knexQuery?.scopeName) {
|
|
103
|
-
return context.knexQuery?.tableName || scopeName
|
|
104
|
-
}
|
|
105
|
-
return scopeName
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const translateColumn = (scopeName, field, alias = defaultAliasForScope(scopeName)) => {
|
|
109
|
-
const adapter = fetchStorageAdapter(scopeName)
|
|
110
|
-
const translated = adapter?.translateColumn?.(field) ?? field
|
|
111
|
-
if (!alias) return translated
|
|
112
|
-
return `${alias}.${translated}`
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const translateFilterValue = (scopeName, field, value) => {
|
|
116
|
-
const adapter = fetchStorageAdapter(scopeName)
|
|
117
|
-
if (!adapter?.translateFilterValue) return value
|
|
118
|
-
return adapter.translateFilterValue(field, value)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
fetchStorageAdapter,
|
|
123
|
-
defaultAliasForScope,
|
|
124
|
-
translateColumn,
|
|
125
|
-
translateFilterValue,
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
76
|
/**
|
|
130
77
|
* Processes filters that target polymorphic relationships where a single relationship can point to different types of resources
|
|
131
78
|
*
|
|
@@ -201,7 +148,7 @@ const createAdapterUtilities = (hookParams, { getStorageAdapter } = {}) => {
|
|
|
201
148
|
*/
|
|
202
149
|
export const polymorphicFiltersHook = async (hookParams, dependencies) => {
|
|
203
150
|
const { log, scopes, knex } = dependencies
|
|
204
|
-
const adapterUtils =
|
|
151
|
+
const adapterUtils = createStorageAdapterUtilities(hookParams, dependencies)
|
|
205
152
|
|
|
206
153
|
// Extract context
|
|
207
154
|
const scopeName = hookParams.context?.knexQuery?.scopeName
|
|
@@ -209,13 +156,13 @@ export const polymorphicFiltersHook = async (hookParams, dependencies) => {
|
|
|
209
156
|
const query = hookParams.context?.knexQuery?.query
|
|
210
157
|
const db = hookParams.context?.knexQuery?.db || knex
|
|
211
158
|
|
|
212
|
-
|
|
213
|
-
const tableAlias = adapterUtils.defaultAliasForScope(scopeName)
|
|
214
|
-
|
|
215
|
-
if (!filters) {
|
|
159
|
+
if (!scopeName || !filters || !scopes[scopeName]) {
|
|
216
160
|
return
|
|
217
161
|
}
|
|
218
162
|
|
|
163
|
+
const schemaInfo = scopes[scopeName].vars.schemaInfo
|
|
164
|
+
const tableAlias = adapterUtils.defaultAliasForScope(scopeName)
|
|
165
|
+
|
|
219
166
|
// Step 1: Identify polymorphic searches
|
|
220
167
|
const polymorphicSearches = new Map()
|
|
221
168
|
const polymorphicJoins = new Map()
|
|
@@ -525,7 +472,7 @@ export const polymorphicFiltersHook = async (hookParams, dependencies) => {
|
|
|
525
472
|
*/
|
|
526
473
|
export const crossTableFiltersHook = async (hookParams, dependencies) => {
|
|
527
474
|
const { log, scopes, knex } = dependencies
|
|
528
|
-
const adapterUtils =
|
|
475
|
+
const adapterUtils = createStorageAdapterUtilities(hookParams, dependencies)
|
|
529
476
|
|
|
530
477
|
// Extract context
|
|
531
478
|
const scopeName = hookParams.context?.knexQuery?.scopeName
|
|
@@ -533,16 +480,16 @@ export const crossTableFiltersHook = async (hookParams, dependencies) => {
|
|
|
533
480
|
const query = hookParams.context?.knexQuery?.query
|
|
534
481
|
const db = hookParams.context?.knexQuery?.db || knex
|
|
535
482
|
|
|
483
|
+
if (!scopeName || !filters || !scopes[scopeName]) {
|
|
484
|
+
return
|
|
485
|
+
}
|
|
486
|
+
|
|
536
487
|
const schemaInfo = scopes[scopeName].vars.schemaInfo
|
|
537
488
|
const tableName = schemaInfo.tableName
|
|
538
489
|
const tableAlias = adapterUtils.defaultAliasForScope(scopeName)
|
|
539
490
|
const aliasScopeMap = new Map()
|
|
540
491
|
aliasScopeMap.set(tableAlias, scopeName)
|
|
541
492
|
|
|
542
|
-
if (!filters) {
|
|
543
|
-
return
|
|
544
|
-
}
|
|
545
|
-
|
|
546
493
|
// Step 1: Analyze indexes
|
|
547
494
|
const requiredIndexes = analyzeRequiredIndexes(scopes, log, scopeName, schemaInfo)
|
|
548
495
|
if (requiredIndexes.length > 0) {
|
|
@@ -938,7 +885,7 @@ export const crossTableFiltersHook = async (hookParams, dependencies) => {
|
|
|
938
885
|
*/
|
|
939
886
|
export const basicFiltersHook = async (hookParams, dependencies) => {
|
|
940
887
|
const { log, scopes, knex } = dependencies
|
|
941
|
-
const adapterUtils =
|
|
888
|
+
const adapterUtils = createStorageAdapterUtilities(hookParams, dependencies)
|
|
942
889
|
|
|
943
890
|
// Extract context
|
|
944
891
|
const scopeName = hookParams.context?.knexQuery?.scopeName
|
|
@@ -946,6 +893,10 @@ export const basicFiltersHook = async (hookParams, dependencies) => {
|
|
|
946
893
|
const query = hookParams.context?.knexQuery?.query
|
|
947
894
|
const db = hookParams.context?.knexQuery?.db || knex
|
|
948
895
|
|
|
896
|
+
if (!scopeName || !filters || !scopes[scopeName]) {
|
|
897
|
+
return
|
|
898
|
+
}
|
|
899
|
+
|
|
949
900
|
const schemaInfo = scopes[scopeName].vars.schemaInfo
|
|
950
901
|
const tableName = schemaInfo.tableName
|
|
951
902
|
const tableAlias = adapterUtils.defaultAliasForScope(scopeName)
|
|
@@ -958,11 +909,6 @@ export const basicFiltersHook = async (hookParams, dependencies) => {
|
|
|
958
909
|
tableName
|
|
959
910
|
})
|
|
960
911
|
|
|
961
|
-
if (!filters) {
|
|
962
|
-
log.trace('[DEBUG basicFiltersHook] Returning early - no filters')
|
|
963
|
-
return
|
|
964
|
-
}
|
|
965
|
-
|
|
966
912
|
// Check if we have any JOINs applied (to know if we need to qualify fields)
|
|
967
913
|
// Instead of relying on hasJoins flag, always qualify fields for safety
|
|
968
914
|
const qualifyField = (field) => adapterUtils.translateColumn(scopeName, field, tableAlias)
|