json-rest-api 1.0.21 → 1.0.22
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/audit/00-charter.md +103 -0
- package/audit/01-scope-map.md +84 -0
- package/audit/02-coverage-matrix.csv +174 -0
- package/audit/03-invariants.md +123 -0
- package/audit/04-standards-rubric.md +56 -0
- package/audit/05-competitor-rubric.md +77 -0
- package/audit/06-findings.md +160 -0
- package/audit/07-risk-register.csv +10 -0
- package/audit/08-remediation-roadmap.md +177 -0
- package/audit/09-signoff-check.md +23 -0
- package/audit/10-final-synthesis.md +29 -0
- package/audit/evidence/code/all-files-json-rest-api.txt +168 -0
- package/audit/evidence/code/all-files-json-rest-schema.txt +12 -0
- package/audit/evidence/code/boundary-leak-checks.txt +72 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__bulk-operations-plugin.js.txt +480 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__express-plugin.js.txt +532 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__busboy-detector.js.txt +175 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__formidable-detector.js.txt +138 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__request-helpers.js.txt +222 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__file-handling-plugin.js.txt +342 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__dbTablesOperations.js.txt +304 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__fractional-positioning.js.txt +188 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__compile-schemas.js.txt +336 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__connectors-query-parser.js.txt +165 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-capabilities.js.txt +265 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-value-normalizers.js.txt +369 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__default-data-helpers.js.txt +72 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__field-utils.js.txt +139 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-constants.js.txt +45 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-field-helpers.js.txt +396 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-json-api-transformers.js.txt +162 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__payload-validators.js.txt +1314 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__schema-helpers.js.txt +488 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__scope-validations.js.txt +484 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__simplified-helpers.js.txt +857 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-cross-table-search.js.txt +565 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-json-api-transformers-querying.js.txt +485 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-pagination-helpers.js.txt +560 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-process-includes.js.txt +167 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers-base.js.txt +109 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers.js.txt +1064 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-relationship-includes.js.txt +1983 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-window-queries.js.txt +328 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__url-helpers.js.txt +119 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__storage__storage-adapter.js.txt +160 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__many-to-many-manipulations.js.txt +300 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__relationship-processor.js.txt +245 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-access.js.txt +459 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-cors-plugin.js.txt +237 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-knex-plugin.js.txt +1135 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-multihome-plugin.js.txt +314 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__compile-resource-schemas.js.txt +7 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-relationship-routes.js.txt +71 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-scope-routes.js.txt +102 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__turn-scope-init-into-vars.js.txt +49 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__validate-include-configurations.js.txt +39 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__add-route.js.txt +23 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__check-permissions.js.txt +13 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__common.js.txt +639 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete-relationship.js.txt +117 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete.js.txt +104 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__enrich-attributes.js.txt +199 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-related.js.txt +353 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-relationship.js.txt +61 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get.js.txt +184 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch-relationship.js.txt +55 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch.js.txt +191 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post-relationship.js.txt +110 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post.js.txt +168 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__put.js.txt +253 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__query.js.txt +207 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__release.js.txt +12 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin.js.txt +165 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-positioning-plugin.js.txt +668 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__socketio-plugin.js.txt +621 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__local-storage.js.txt +218 -0
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__s3-storage.js.txt +124 -0
- package/audit/evidence/code/critical-pass1/json-rest-schema__src__CorePlugin.js.txt +269 -0
- package/audit/evidence/code/critical-pass1/json-rest-schema__src__Schema.js.txt +242 -0
- package/audit/evidence/code/critical-pass1/json-rest-schema__src__index.js.txt +77 -0
- package/audit/evidence/code/critical-pass1-risk-keywords.txt +238 -0
- package/audit/evidence/code/critical-pass1-summary.csv +67 -0
- package/audit/evidence/code/critical-pass2-summary.csv +67 -0
- package/audit/evidence/code/early-silent-zones-no-test-token.txt +57 -0
- package/audit/evidence/code/error-model-signals.txt +98 -0
- package/audit/evidence/code/git-files-json-rest-api.txt +171 -0
- package/audit/evidence/code/git-files-json-rest-schema.txt +27 -0
- package/audit/evidence/code/hook-flow-signals.txt +282 -0
- package/audit/evidence/code/idproperty-signals.txt +157 -0
- package/audit/evidence/code/import-cycles-non-anyapi.txt +3 -0
- package/audit/evidence/code/import-hotspots-non-anyapi.txt +96 -0
- package/audit/evidence/code/imports-json-rest-api.txt +354 -0
- package/audit/evidence/code/imports-json-rest-schema.txt +15 -0
- package/audit/evidence/code/in-scope-files-json-rest-api.txt +160 -0
- package/audit/evidence/code/in-scope-files-json-rest-schema.txt +13 -0
- package/audit/evidence/code/non-anyapi-core-files.txt +101 -0
- package/audit/evidence/code/permission-signals.txt +193 -0
- package/audit/evidence/code/query-include-signals.txt +687 -0
- package/audit/evidence/code/transaction-signals.txt +28 -0
- package/audit/evidence/code/validation-signals.txt +650 -0
- package/audit/evidence/code/write-path-signals.txt +61 -0
- package/audit/evidence/docs/docs-claims-signals.txt +1075 -0
- package/audit/evidence/docs/schema-docs-claims-signals.txt +179 -0
- package/audit/evidence/external/corpus-freeze.md +21 -0
- package/audit/evidence/external/npm-competitor-metadata.txt +70 -0
- package/audit/evidence/external/npm-download-signals.txt +23 -0
- package/audit/evidence/external/npm-jsonapi-server.txt +8 -0
- package/audit/evidence/external/npm-modern-jsonapi-metadata.txt +38 -0
- package/audit/evidence/external/readme-fortune-signals.txt +26 -0
- package/audit/evidence/external/readme-fortune.md +165 -0
- package/audit/evidence/external/readme-json-api-serializer-signals.txt +80 -0
- package/audit/evidence/external/readme-json-api-serializer.md +690 -0
- package/audit/evidence/external/readme-jsonapi-serde-server-signals.txt +0 -0
- package/audit/evidence/external/readme-jsonapi-serde-server.md +22 -0
- package/audit/evidence/external/readme-jsonapi-serializer-signals.txt +63 -0
- package/audit/evidence/external/readme-jsonapi-serializer.md +315 -0
- package/audit/evidence/external/readme-jsonapi-server-signals.txt +19 -0
- package/audit/evidence/external/readme-jsonapi-server.md +104 -0
- package/audit/evidence/external/readme-nestjs-json-api-signals.txt +4 -0
- package/audit/evidence/external/readme-nestjs-json-api.md +94 -0
- package/audit/evidence/external/readme-yaser-json-api-nestjs-signals.txt +47 -0
- package/audit/evidence/external/readme-yaser-json-api-nestjs.md +447 -0
- package/audit/evidence/external/standard-jsonapi-snippets.txt +84 -0
- package/audit/evidence/external/standard-rfc-snippets.txt +168 -0
- package/audit/evidence/external/standards-sources.txt +6 -0
- package/audit/evidence/runtime/baseline-commits.txt +2 -0
- package/audit/evidence/runtime/db-adapter-assumptions.txt +3 -0
- package/audit/evidence/runtime/environment.txt +4 -0
- package/audit/evidence/runtime/invariant-runtime-mapping.md +38 -0
- package/audit/evidence/runtime/probe-patch-relationship-error.exitcode +1 -0
- package/audit/evidence/runtime/probe-patch-relationship-error.log +76 -0
- package/audit/evidence/runtime/probe-patch-relationship-error.mjs +67 -0
- package/audit/evidence/runtime/probe-patch-relationship-referenceerror.exitcode +1 -0
- package/audit/evidence/runtime/probe-patch-relationship-referenceerror.log +62 -0
- package/audit/evidence/runtime/probe-patch-relationship-referenceerror.mjs +47 -0
- package/audit/evidence/runtime/test-exitcodes.txt +4 -0
- package/audit/evidence/runtime/test-summary.md +107 -0
- package/audit/evidence/runtime/trace-anchors.txt +61 -0
- package/audit/evidence/tests/access-multitenancy-test-signals.txt +125 -0
- package/audit/evidence/tests/failing-tests-summary.txt +25 -0
- package/audit/evidence/tests/npm-test-json-rest-api.exitcode +1 -0
- package/audit/evidence/tests/npm-test-json-rest-api.log +5885 -0
- package/audit/evidence/tests/npm-test-json-rest-schema.exitcode +1 -0
- package/audit/evidence/tests/npm-test-json-rest-schema.log +356 -0
- package/audit/evidence/tests/query-include-test-signals.txt +948 -0
- package/audit/evidence/tests/relationship-test-signals.txt +223 -0
- package/audit/evidence/tests/skipped-tests.txt +10 -0
- package/audit/evidence/tests/test-suite-list-non-anyapi.txt +30 -0
- package/audit/evidence/tests/test-suite-list.txt +32 -0
- package/audit/subsystems/docs-drift.md +49 -0
- package/audit/subsystems/pass2-cross-review.md +131 -0
- package/audit/subsystems/plugins-access.md +51 -0
- package/audit/subsystems/plugins-bulk.md +50 -0
- package/audit/subsystems/plugins-cors.md +48 -0
- package/audit/subsystems/plugins-file.md +53 -0
- package/audit/subsystems/plugins-multihome.md +50 -0
- package/audit/subsystems/plugins-positioning.md +54 -0
- package/audit/subsystems/plugins-socketio.md +50 -0
- package/audit/subsystems/query-engine.md +61 -0
- package/audit/subsystems/relationships.md +55 -0
- package/audit/subsystems/rest-orchestration.md +66 -0
- package/audit/subsystems/schema-core.md +64 -0
- package/audit/subsystems/storage-knex.md +57 -0
- package/audit/subsystems/tests.md +48 -0
- package/audit/subsystems/transport-express.md +55 -0
- package/audit/traces/trace-read-get.md +22 -0
- package/audit/traces/trace-read-query.md +25 -0
- package/audit/traces/trace-relationship-endpoints.md +31 -0
- package/audit/traces/trace-write-delete.md +17 -0
- package/audit/traces/trace-write-patch.md +26 -0
- package/audit/traces/trace-write-post.md +22 -0
- package/audit/traces/trace-write-put.md +23 -0
- package/package.json +3 -3
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
1 import { buildIncludedResources } from './knex-relationship-includes.js'
|
|
2
|
+
2
|
|
3
|
+
3 /**
|
|
4
|
+
4 * Processes the ?include= parameter to load related resources efficiently
|
|
5
|
+
5 *
|
|
6
|
+
6 * @param {Object} scope - The scope object for the primary resource
|
|
7
|
+
7 * @param {Array<Object>} records - The primary records to load includes for
|
|
8
|
+
8 * @param {Object} deps - Dependencies object containing log, scopes, knex, and context
|
|
9
|
+
9 * @returns {Promise<Array<Object>>} Array of included resources in JSON:API format
|
|
10
|
+
10 *
|
|
11
|
+
11 * @example
|
|
12
|
+
12 * // Input: Articles with author relationship
|
|
13
|
+
13 * const articles = [
|
|
14
|
+
14 * { id: 1, title: 'First Article', author_id: 10 },
|
|
15
|
+
15 * { id: 2, title: 'Second Article', author_id: 11 },
|
|
16
|
+
16 * { id: 3, title: 'Third Article', author_id: 10 } // Same author as article 1
|
|
17
|
+
17 * ];
|
|
18
|
+
18 *
|
|
19
|
+
19 * const deps = {
|
|
20
|
+
20 * context: {
|
|
21
|
+
21 * scopeName: 'articles',
|
|
22
|
+
22 * queryParams: { include: ['author'] },
|
|
23
|
+
23 * schemaInfo: { idProperty: 'id' }
|
|
24
|
+
24 * }
|
|
25
|
+
25 * };
|
|
26
|
+
26 *
|
|
27
|
+
27 * const included = await processIncludes(scope, articles, deps);
|
|
28
|
+
28 *
|
|
29
|
+
29 * // Output: Deduplicated authors (only 2, not 3)
|
|
30
|
+
30 * // [
|
|
31
|
+
31 * // { type: 'users', id: '10', attributes: { name: 'Alice', email: 'alice@example.com' } },
|
|
32
|
+
32 * // { type: 'users', id: '11', attributes: { name: 'Bob', email: 'bob@example.com' } }
|
|
33
|
+
33 * // ]
|
|
34
|
+
34 *
|
|
35
|
+
35 * @example
|
|
36
|
+
36 * // Input: Nested includes with dot notation
|
|
37
|
+
37 * const articles = [{ id: 1, title: 'Article', author_id: 10 }];
|
|
38
|
+
38 *
|
|
39
|
+
39 * const deps = {
|
|
40
|
+
40 * context: {
|
|
41
|
+
41 * queryParams: {
|
|
42
|
+
42 * include: ['author', 'comments.user'] // Load author AND comment users
|
|
43
|
+
43 * }
|
|
44
|
+
44 * }
|
|
45
|
+
45 * };
|
|
46
|
+
46 *
|
|
47
|
+
47 * const included = await processIncludes(scope, articles, deps);
|
|
48
|
+
48 *
|
|
49
|
+
49 * // Output: All related resources in flat array
|
|
50
|
+
50 * // [
|
|
51
|
+
51 * // { type: 'users', id: '10', attributes: { name: 'Author' } },
|
|
52
|
+
52 * // { type: 'comments', id: '1', attributes: { text: 'Great!' } },
|
|
53
|
+
53 * // { type: 'comments', id: '2', attributes: { text: 'Nice!' } },
|
|
54
|
+
54 * // { type: 'users', id: '20', attributes: { name: 'Commenter1' } },
|
|
55
|
+
55 * // { type: 'users', id: '21', attributes: { name: 'Commenter2' } }
|
|
56
|
+
56 * // ]
|
|
57
|
+
57 *
|
|
58
|
+
58 * @example
|
|
59
|
+
59 * // Input: With sparse fieldsets limiting included data
|
|
60
|
+
60 * const deps = {
|
|
61
|
+
61 * context: {
|
|
62
|
+
62 * queryParams: {
|
|
63
|
+
63 * include: ['author'],
|
|
64
|
+
64 * fields: {
|
|
65
|
+
65 * users: 'name' // Only include name field for users
|
|
66
|
+
66 * }
|
|
67
|
+
67 * }
|
|
68
|
+
68 * }
|
|
69
|
+
69 * };
|
|
70
|
+
70 *
|
|
71
|
+
71 * const included = await processIncludes(scope, articles, deps);
|
|
72
|
+
72 *
|
|
73
|
+
73 * // Output: Users with only requested fields
|
|
74
|
+
74 * // [
|
|
75
|
+
75 * // { type: 'users', id: '10', attributes: { name: 'Alice' } }
|
|
76
|
+
76 * // // email field excluded due to sparse fieldset
|
|
77
|
+
77 * // ]
|
|
78
|
+
78 *
|
|
79
|
+
79 * @description
|
|
80
|
+
80 * Used by:
|
|
81
|
+
81 * - rest-api-knex-plugin's dataGet method when includes are requested
|
|
82
|
+
82 * - rest-api-knex-plugin's dataQuery method for collection includes
|
|
83
|
+
83 * - Called after primary records are fetched but before response assembly
|
|
84
|
+
84 *
|
|
85
|
+
85 * Purpose:
|
|
86
|
+
86 * - Implements JSON:API compound documents with primary data and includes
|
|
87
|
+
87 * - Prevents N+1 queries by batch loading all related resources
|
|
88
|
+
88 * - Handles complex nested includes like 'comments.author.company'
|
|
89
|
+
89 * - Automatically deduplicates included resources
|
|
90
|
+
90 * - Respects sparse fieldsets on included resources
|
|
91
|
+
91 *
|
|
92
|
+
92 * Data flow:
|
|
93
|
+
93 * 1. Receives primary records and include directives
|
|
94
|
+
94 * 2. Parses include parameter (comma-separated, possibly nested with dots)
|
|
95
|
+
95 * 3. Delegates to buildIncludedResources for actual loading
|
|
96
|
+
96 * 4. Returns flat array of all included resources
|
|
97
|
+
97 * 5. These resources go into the 'included' section of JSON:API response
|
|
98
|
+
98 */
|
|
99
|
+
99 export const processIncludes = async (scope, records, deps) => {
|
|
100
|
+
100 try {
|
|
101
|
+
101 const { log, scopes, knex, context, api } = deps
|
|
102
|
+
102
|
|
103
|
+
103 const scopeName = context.scopeName
|
|
104
|
+
104
|
|
105
|
+
105 if (!scopeName) {
|
|
106
|
+
106 log.error('[PROCESS-INCLUDES] scopeName is undefined in context!', {
|
|
107
|
+
107 contextKeys: Object.keys(context || {})
|
|
108
|
+
108 })
|
|
109
|
+
109 throw new Error('scopeName is undefined in context')
|
|
110
|
+
110 }
|
|
111
|
+
111
|
|
112
|
+
112 const db = context.transaction || knex
|
|
113
|
+
113 const queryParams = context.queryParams
|
|
114
|
+
114 const idProperty = context.schemaInfo.idProperty
|
|
115
|
+
115
|
|
116
|
+
116 if (!queryParams.include) {
|
|
117
|
+
117 return []
|
|
118
|
+
118 }
|
|
119
|
+
119
|
|
120
|
+
120 log.debug('[PROCESS-INCLUDES] Processing includes:', queryParams.include)
|
|
121
|
+
121
|
|
122
|
+
122 const includeResult = await buildIncludedResources(
|
|
123
|
+
123 {
|
|
124
|
+
124 records,
|
|
125
|
+
125 scopeName,
|
|
126
|
+
126 includeParam: queryParams.include,
|
|
127
|
+
127 fields: queryParams.fields || {},
|
|
128
|
+
128 idProperty
|
|
129
|
+
129 },
|
|
130
|
+
130 {
|
|
131
|
+
131 context: {
|
|
132
|
+
132 scopes,
|
|
133
|
+
133 log,
|
|
134
|
+
134 knex: db,
|
|
135
|
+
135 capabilities: api?.knex?.capabilities
|
|
136
|
+
136 }
|
|
137
|
+
137 }
|
|
138
|
+
138 )
|
|
139
|
+
139
|
|
140
|
+
140 log.debug('[PROCESS-INCLUDES] Include result:', {
|
|
141
|
+
141 includedCount: includeResult.included.length,
|
|
142
|
+
142 types: [...new Set(includeResult.included.map(r => r.type))]
|
|
143
|
+
143 })
|
|
144
|
+
144
|
|
145
|
+
145 return includeResult.included
|
|
146
|
+
146 } catch (error) {
|
|
147
|
+
147 const { log, context } = deps || {}
|
|
148
|
+
148
|
|
149
|
+
149 if (log) {
|
|
150
|
+
150 log.error('[PROCESS-INCLUDES] Error processing includes:', {
|
|
151
|
+
151 scopeName: context?.scopeName,
|
|
152
|
+
152 recordCount: records?.length || 0,
|
|
153
|
+
153 includeParam: context?.queryParams?.include,
|
|
154
|
+
154 error: error.message,
|
|
155
|
+
155 stack: error.stack
|
|
156
|
+
156 })
|
|
157
|
+
157 } else {
|
|
158
|
+
158 console.error('[PROCESS-INCLUDES] Error processing includes:', error)
|
|
159
|
+
159 }
|
|
160
|
+
160
|
|
161
|
+
161 const enhancedError = new Error(
|
|
162
|
+
162 `Failed to process includes${context?.scopeName ? ` for scope '${context.scopeName}'` : ''}: ${error.message}`
|
|
163
|
+
163 )
|
|
164
|
+
164 enhancedError.originalError = error
|
|
165
|
+
165 throw enhancedError
|
|
166
|
+
166 }
|
|
167
|
+
167 }
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
1 /**
|
|
2
|
+
2 * Builds the SELECT clause for a Knex query, handling field selection and table prefixing
|
|
3
|
+
3 *
|
|
4
|
+
4 * @param {Object} query - The Knex query builder instance
|
|
5
|
+
5 * @param {string} tableName - The table name
|
|
6
|
+
6 * @param {Array<string>|string} fieldsToSelect - Fields to select or '*'
|
|
7
|
+
7 * @param {boolean} useTablePrefix - Whether to prefix fields with table name
|
|
8
|
+
8 * @returns {Object} The modified query builder
|
|
9
|
+
9 *
|
|
10
|
+
10 * @example
|
|
11
|
+
11 * // Input: Basic query without specific fields
|
|
12
|
+
12 * const query = knex('articles');
|
|
13
|
+
13 * buildQuerySelection(query, 'articles', '*', false);
|
|
14
|
+
14 *
|
|
15
|
+
15 * // Effect on query: No modification, Knex defaults to SELECT *
|
|
16
|
+
16 * // SQL generated: SELECT * FROM articles
|
|
17
|
+
17 *
|
|
18
|
+
18 * @example
|
|
19
|
+
19 * // Input: Query with sparse fieldset
|
|
20
|
+
20 * const query = knex('articles');
|
|
21
|
+
21 * const fields = ['id', 'title', 'author_id'];
|
|
22
|
+
22 * buildQuerySelection(query, 'articles', fields, false);
|
|
23
|
+
23 *
|
|
24
|
+
24 * // Effect on query: Adds select() with specific fields
|
|
25
|
+
25 * // SQL generated: SELECT id, title, author_id FROM articles
|
|
26
|
+
26 * // Response will only include these 3 fields, reducing payload size
|
|
27
|
+
27 *
|
|
28
|
+
28 * @example
|
|
29
|
+
29 * // Input: Query with joins needing table prefixes
|
|
30
|
+
30 * const query = knex('articles')
|
|
31
|
+
31 * .join('users', 'articles.author_id', 'users.id');
|
|
32
|
+
32 * const fields = ['id', 'title', 'created_at as published'];
|
|
33
|
+
33 * buildQuerySelection(query, 'articles', fields, true);
|
|
34
|
+
34 *
|
|
35
|
+
35 * // Effect on query: Prefixes all fields with table name
|
|
36
|
+
36 * // SQL generated: SELECT articles.id, articles.title, articles.created_at as published
|
|
37
|
+
37 * // Prevents "column 'id' is ambiguous" errors since both tables have id
|
|
38
|
+
38 *
|
|
39
|
+
39 * @description
|
|
40
|
+
40 * Used by:
|
|
41
|
+
41 * - knex-query-helpers.js calls this when building queries with sparse fieldsets
|
|
42
|
+
42 * - rest-api-knex-plugin uses this in dataQuery to optimize SELECT clauses
|
|
43
|
+
43 * - Called whenever fields parameter limits which attributes to return
|
|
44
|
+
44 *
|
|
45
|
+
45 * Purpose:
|
|
46
|
+
46 * - Implements JSON:API sparse fieldsets by selecting only requested fields
|
|
47
|
+
47 * - Prevents ambiguous column errors in queries with joins
|
|
48
|
+
48 * - Reduces database transfer by not selecting unnecessary columns
|
|
49
|
+
49 * - Handles field aliases (e.g., "created_at as published")
|
|
50
|
+
50 *
|
|
51
|
+
51 * Data flow:
|
|
52
|
+
52 * 1. Query parser extracts fields parameter (e.g., fields[articles]=title,author)
|
|
53
|
+
53 * 2. This function adds appropriate SELECT clause to query
|
|
54
|
+
54 * 3. Database returns only selected columns
|
|
55
|
+
55 * 4. Smaller result sets improve query performance
|
|
56
|
+
56 * 5. JSON:API response includes only requested attributes
|
|
57
|
+
57 */
|
|
58
|
+
58 export const buildQuerySelection = (
|
|
59
|
+
59 query,
|
|
60
|
+
60 tableName,
|
|
61
|
+
61 fieldsToSelect,
|
|
62
|
+
62 useTablePrefix = false,
|
|
63
|
+
63 options = {}
|
|
64
|
+
64 ) => {
|
|
65
|
+
65 const translateColumn = options.translateColumn
|
|
66
|
+
66
|
|
67
|
+
67 const applyTranslation = (field, aliasForField) => {
|
|
68
|
+
68 if (translateColumn) {
|
|
69
|
+
69 const translated = translateColumn(field, aliasForField)
|
|
70
|
+
70 if (translated) {
|
|
71
|
+
71 return translated
|
|
72
|
+
72 }
|
|
73
|
+
73 }
|
|
74
|
+
74
|
|
75
|
+
75 if (aliasForField) {
|
|
76
|
+
76 return `${aliasForField}.${field}`
|
|
77
|
+
77 }
|
|
78
|
+
78 return field
|
|
79
|
+
79 }
|
|
80
|
+
80
|
|
81
|
+
81 const targetAlias = useTablePrefix ? tableName : null
|
|
82
|
+
82
|
|
83
|
+
83 if (fieldsToSelect === '*') {
|
|
84
|
+
84 const translated = translateColumn ? translateColumn('*', targetAlias) : null
|
|
85
|
+
85 if (translated) {
|
|
86
|
+
86 return query.select(translated)
|
|
87
|
+
87 }
|
|
88
|
+
88 return useTablePrefix ? query.select(`${tableName}.*`) : query
|
|
89
|
+
89 }
|
|
90
|
+
90
|
|
91
|
+
91 const fieldsArray = Array.isArray(fieldsToSelect)
|
|
92
|
+
92 ? fieldsToSelect
|
|
93
|
+
93 : Array.from(fieldsToSelect || [])
|
|
94
|
+
94
|
|
95
|
+
95 const translatedFields = fieldsArray.map((field) => {
|
|
96
|
+
96 if (typeof field !== 'string') return field
|
|
97
|
+
97
|
|
98
|
+
98 const aliasMatch = field.match(/\s+as\s+/i)
|
|
99
|
+
99 if (aliasMatch) {
|
|
100
|
+
100 const [source, alias] = field.split(/\s+as\s+/i)
|
|
101
|
+
101 const translatedSource = applyTranslation(source.trim(), targetAlias)
|
|
102
|
+
102 return `${translatedSource} as ${alias.trim()}`
|
|
103
|
+
103 }
|
|
104
|
+
104
|
|
105
|
+
105 return applyTranslation(field.trim(), targetAlias)
|
|
106
|
+
106 })
|
|
107
|
+
107
|
|
108
|
+
108 return query.select(translatedFields)
|
|
109
|
+
109 }
|