json-rest-api 1.0.22 → 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 +2 -2
- 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/audit/00-charter.md +0 -103
- package/audit/01-scope-map.md +0 -84
- package/audit/02-coverage-matrix.csv +0 -174
- package/audit/03-invariants.md +0 -123
- package/audit/04-standards-rubric.md +0 -56
- package/audit/05-competitor-rubric.md +0 -77
- package/audit/06-findings.md +0 -160
- package/audit/07-risk-register.csv +0 -10
- package/audit/08-remediation-roadmap.md +0 -177
- package/audit/09-signoff-check.md +0 -23
- package/audit/10-final-synthesis.md +0 -29
- package/audit/evidence/code/all-files-json-rest-api.txt +0 -168
- package/audit/evidence/code/all-files-json-rest-schema.txt +0 -12
- package/audit/evidence/code/boundary-leak-checks.txt +0 -72
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__bulk-operations-plugin.js.txt +0 -480
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__express-plugin.js.txt +0 -532
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__busboy-detector.js.txt +0 -175
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__formidable-detector.js.txt +0 -138
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__request-helpers.js.txt +0 -222
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__file-handling-plugin.js.txt +0 -342
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__dbTablesOperations.js.txt +0 -304
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__fractional-positioning.js.txt +0 -188
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__compile-schemas.js.txt +0 -336
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__connectors-query-parser.js.txt +0 -165
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-capabilities.js.txt +0 -265
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-value-normalizers.js.txt +0 -369
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__default-data-helpers.js.txt +0 -72
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__field-utils.js.txt +0 -139
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-constants.js.txt +0 -45
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-field-helpers.js.txt +0 -396
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-json-api-transformers.js.txt +0 -162
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__payload-validators.js.txt +0 -1314
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__schema-helpers.js.txt +0 -488
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__scope-validations.js.txt +0 -484
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__simplified-helpers.js.txt +0 -857
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-cross-table-search.js.txt +0 -565
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-json-api-transformers-querying.js.txt +0 -485
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-pagination-helpers.js.txt +0 -560
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-process-includes.js.txt +0 -167
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers-base.js.txt +0 -109
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers.js.txt +0 -1064
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-relationship-includes.js.txt +0 -1983
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-window-queries.js.txt +0 -328
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__url-helpers.js.txt +0 -119
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__storage__storage-adapter.js.txt +0 -160
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__many-to-many-manipulations.js.txt +0 -300
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__relationship-processor.js.txt +0 -245
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-access.js.txt +0 -459
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-cors-plugin.js.txt +0 -237
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-knex-plugin.js.txt +0 -1135
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-multihome-plugin.js.txt +0 -314
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__compile-resource-schemas.js.txt +0 -7
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-relationship-routes.js.txt +0 -71
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-scope-routes.js.txt +0 -102
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__turn-scope-init-into-vars.js.txt +0 -49
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__validate-include-configurations.js.txt +0 -39
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__add-route.js.txt +0 -23
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__check-permissions.js.txt +0 -13
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__common.js.txt +0 -639
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete-relationship.js.txt +0 -117
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete.js.txt +0 -104
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__enrich-attributes.js.txt +0 -199
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-related.js.txt +0 -353
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-relationship.js.txt +0 -61
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get.js.txt +0 -184
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch-relationship.js.txt +0 -55
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch.js.txt +0 -191
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post-relationship.js.txt +0 -110
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post.js.txt +0 -168
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__put.js.txt +0 -253
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__query.js.txt +0 -207
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__release.js.txt +0 -12
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin.js.txt +0 -165
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-positioning-plugin.js.txt +0 -668
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__socketio-plugin.js.txt +0 -621
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__local-storage.js.txt +0 -218
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__s3-storage.js.txt +0 -124
- package/audit/evidence/code/critical-pass1/json-rest-schema__src__CorePlugin.js.txt +0 -269
- package/audit/evidence/code/critical-pass1/json-rest-schema__src__Schema.js.txt +0 -242
- package/audit/evidence/code/critical-pass1/json-rest-schema__src__index.js.txt +0 -77
- package/audit/evidence/code/critical-pass1-risk-keywords.txt +0 -238
- package/audit/evidence/code/critical-pass1-summary.csv +0 -67
- package/audit/evidence/code/critical-pass2-summary.csv +0 -67
- package/audit/evidence/code/early-silent-zones-no-test-token.txt +0 -57
- package/audit/evidence/code/error-model-signals.txt +0 -98
- package/audit/evidence/code/git-files-json-rest-api.txt +0 -171
- package/audit/evidence/code/git-files-json-rest-schema.txt +0 -27
- package/audit/evidence/code/hook-flow-signals.txt +0 -282
- package/audit/evidence/code/idproperty-signals.txt +0 -157
- package/audit/evidence/code/import-cycles-non-anyapi.txt +0 -3
- package/audit/evidence/code/import-hotspots-non-anyapi.txt +0 -96
- package/audit/evidence/code/imports-json-rest-api.txt +0 -354
- package/audit/evidence/code/imports-json-rest-schema.txt +0 -15
- package/audit/evidence/code/in-scope-files-json-rest-api.txt +0 -160
- package/audit/evidence/code/in-scope-files-json-rest-schema.txt +0 -13
- package/audit/evidence/code/non-anyapi-core-files.txt +0 -101
- package/audit/evidence/code/permission-signals.txt +0 -193
- package/audit/evidence/code/query-include-signals.txt +0 -687
- package/audit/evidence/code/transaction-signals.txt +0 -28
- package/audit/evidence/code/validation-signals.txt +0 -650
- package/audit/evidence/code/write-path-signals.txt +0 -61
- package/audit/evidence/docs/docs-claims-signals.txt +0 -1075
- package/audit/evidence/docs/schema-docs-claims-signals.txt +0 -179
- package/audit/evidence/external/corpus-freeze.md +0 -21
- package/audit/evidence/external/npm-competitor-metadata.txt +0 -70
- package/audit/evidence/external/npm-download-signals.txt +0 -23
- package/audit/evidence/external/npm-jsonapi-server.txt +0 -8
- package/audit/evidence/external/npm-modern-jsonapi-metadata.txt +0 -38
- package/audit/evidence/external/readme-fortune-signals.txt +0 -26
- package/audit/evidence/external/readme-fortune.md +0 -165
- package/audit/evidence/external/readme-json-api-serializer-signals.txt +0 -80
- package/audit/evidence/external/readme-json-api-serializer.md +0 -690
- package/audit/evidence/external/readme-jsonapi-serde-server-signals.txt +0 -0
- package/audit/evidence/external/readme-jsonapi-serde-server.md +0 -22
- package/audit/evidence/external/readme-jsonapi-serializer-signals.txt +0 -63
- package/audit/evidence/external/readme-jsonapi-serializer.md +0 -315
- package/audit/evidence/external/readme-jsonapi-server-signals.txt +0 -19
- package/audit/evidence/external/readme-jsonapi-server.md +0 -104
- package/audit/evidence/external/readme-nestjs-json-api-signals.txt +0 -4
- package/audit/evidence/external/readme-nestjs-json-api.md +0 -94
- package/audit/evidence/external/readme-yaser-json-api-nestjs-signals.txt +0 -47
- package/audit/evidence/external/readme-yaser-json-api-nestjs.md +0 -447
- package/audit/evidence/external/standard-jsonapi-snippets.txt +0 -84
- package/audit/evidence/external/standard-rfc-snippets.txt +0 -168
- package/audit/evidence/external/standards-sources.txt +0 -6
- package/audit/evidence/runtime/baseline-commits.txt +0 -2
- package/audit/evidence/runtime/db-adapter-assumptions.txt +0 -3
- package/audit/evidence/runtime/environment.txt +0 -4
- package/audit/evidence/runtime/invariant-runtime-mapping.md +0 -38
- package/audit/evidence/runtime/probe-patch-relationship-error.exitcode +0 -1
- package/audit/evidence/runtime/probe-patch-relationship-error.log +0 -76
- package/audit/evidence/runtime/probe-patch-relationship-error.mjs +0 -67
- package/audit/evidence/runtime/probe-patch-relationship-referenceerror.exitcode +0 -1
- package/audit/evidence/runtime/probe-patch-relationship-referenceerror.log +0 -62
- package/audit/evidence/runtime/probe-patch-relationship-referenceerror.mjs +0 -47
- package/audit/evidence/runtime/test-exitcodes.txt +0 -4
- package/audit/evidence/runtime/test-summary.md +0 -107
- package/audit/evidence/runtime/trace-anchors.txt +0 -61
- package/audit/evidence/tests/access-multitenancy-test-signals.txt +0 -125
- package/audit/evidence/tests/failing-tests-summary.txt +0 -25
- package/audit/evidence/tests/npm-test-json-rest-api.exitcode +0 -1
- package/audit/evidence/tests/npm-test-json-rest-api.log +0 -5885
- package/audit/evidence/tests/npm-test-json-rest-schema.exitcode +0 -1
- package/audit/evidence/tests/npm-test-json-rest-schema.log +0 -356
- package/audit/evidence/tests/query-include-test-signals.txt +0 -948
- package/audit/evidence/tests/relationship-test-signals.txt +0 -223
- package/audit/evidence/tests/skipped-tests.txt +0 -10
- package/audit/evidence/tests/test-suite-list-non-anyapi.txt +0 -30
- package/audit/evidence/tests/test-suite-list.txt +0 -32
- package/audit/subsystems/docs-drift.md +0 -49
- package/audit/subsystems/pass2-cross-review.md +0 -131
- package/audit/subsystems/plugins-access.md +0 -51
- package/audit/subsystems/plugins-bulk.md +0 -50
- package/audit/subsystems/plugins-cors.md +0 -48
- package/audit/subsystems/plugins-file.md +0 -53
- package/audit/subsystems/plugins-multihome.md +0 -50
- package/audit/subsystems/plugins-positioning.md +0 -54
- package/audit/subsystems/plugins-socketio.md +0 -50
- package/audit/subsystems/query-engine.md +0 -61
- package/audit/subsystems/relationships.md +0 -55
- package/audit/subsystems/rest-orchestration.md +0 -66
- package/audit/subsystems/schema-core.md +0 -64
- package/audit/subsystems/storage-knex.md +0 -57
- package/audit/subsystems/tests.md +0 -48
- package/audit/subsystems/transport-express.md +0 -55
- package/audit/traces/trace-read-get.md +0 -22
- package/audit/traces/trace-read-query.md +0 -25
- package/audit/traces/trace-relationship-endpoints.md +0 -31
- package/audit/traces/trace-write-delete.md +0 -17
- package/audit/traces/trace-write-patch.md +0 -26
- package/audit/traces/trace-write-post.md +0 -22
- package/audit/traces/trace-write-put.md +0 -23
- 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,253 +0,0 @@
|
|
|
1
|
-
1 import { RestApiResourceError } from '../../../lib/rest-api-errors.js'
|
|
2
|
-
2 import { validatePutPayload } from '../lib/querying-writing/payload-validators.js'
|
|
3
|
-
3 import { processRelationships } from '../lib/writing/relationship-processor.js'
|
|
4
|
-
4 import { updateManyToManyRelationship, createPivotRecords } from '../lib/writing/many-to-many-manipulations.js'
|
|
5
|
-
5 import { ERROR_SUBTYPES } from '../lib/querying-writing/knex-constants.js'
|
|
6
|
-
6 import {
|
|
7
|
-
7 setupCommonRequest,
|
|
8
|
-
8 validateResourceAttributesBeforeWrite,
|
|
9
|
-
9 validateRelationshipAccess,
|
|
10
|
-
10 applyFieldSetters,
|
|
11
|
-
11 validatePivotResource,
|
|
12
|
-
12 handleRecordReturnAfterWrite,
|
|
13
|
-
13 handleWriteMethodError
|
|
14
|
-
14 } from './common.js'
|
|
15
|
-
15
|
|
16
|
-
16 /**
|
|
17
|
-
17 * PUT
|
|
18
|
-
18 * Updates an existing top-level resource by completely replacing it.
|
|
19
|
-
19 * This method supports updating both attributes and relationships (1:1 and n:n).
|
|
20
|
-
20 * PUT is a complete replacement - any relationships not provided will be removed/nulled.
|
|
21
|
-
21 * This method does NOT support creating new related resources via an `included` array.
|
|
22
|
-
22 */
|
|
23
|
-
23 export default async function putMethod ({
|
|
24
|
-
24 params,
|
|
25
|
-
25 context,
|
|
26
|
-
26 vars,
|
|
27
|
-
27 helpers,
|
|
28
|
-
28 scope,
|
|
29
|
-
29 scopes,
|
|
30
|
-
30 runHooks,
|
|
31
|
-
31 apiOptions,
|
|
32
|
-
32 pluginOptions,
|
|
33
|
-
33 scopeOptions,
|
|
34
|
-
34 scopeName,
|
|
35
|
-
35 api,
|
|
36
|
-
36 log
|
|
37
|
-
37 }) {
|
|
38
|
-
38 context.method = 'put'
|
|
39
|
-
39
|
|
40
|
-
40 try {
|
|
41
|
-
41 const { schema, schemaStructure, schemaRelationships } = await setupCommonRequest({
|
|
42
|
-
42 params,
|
|
43
|
-
43 context,
|
|
44
|
-
44 vars,
|
|
45
|
-
45 scopes,
|
|
46
|
-
46 scopeOptions,
|
|
47
|
-
47 scopeName,
|
|
48
|
-
48 api,
|
|
49
|
-
49 helpers
|
|
50
|
-
50 })
|
|
51
|
-
51 context.id = context.inputRecord.data.id
|
|
52
|
-
52
|
|
53
|
-
53 // Run early hooks for pre-processing (e.g., file handling)
|
|
54
|
-
54 await runHooks('beforeProcessing')
|
|
55
|
-
55 await runHooks('beforeProcessingPut')
|
|
56
|
-
56
|
|
57
|
-
57 // Validate PUT payload to ensure it's a complete resource replacement operation.
|
|
58
|
-
58 // PUT requires the full resource representation including ID (unlike POST which generates ID).
|
|
59
|
-
59 // It validates that data.id matches the URL parameter, prevents 'included' array (which is
|
|
60
|
-
60 // read-only), and ensures the payload represents a complete replacement. Any fields not
|
|
61
|
-
61 // provided will be removed or reset to defaults - this is the key difference from PATCH.
|
|
62
|
-
62 // Example: PUT to /articles/123 must have data.id: '123' and all required fields.
|
|
63
|
-
63 validatePutPayload(context.inputRecord, scopes)
|
|
64
|
-
64
|
|
65
|
-
65 // Validate that user has read access to all related resources
|
|
66
|
-
66 // This ensures users can only create relationships to resources they can access
|
|
67
|
-
67 await validateRelationshipAccess(context, context.inputRecord, helpers, runHooks, api)
|
|
68
|
-
68
|
|
69
|
-
69 // Extract foreign keys from JSON:API relationships and prepare many-to-many operations
|
|
70
|
-
70 // Example: relationships.author -> author_id: '123' for storage
|
|
71
|
-
71 // Example: relationships.tags -> array of pivot records to create later
|
|
72
|
-
72 const { belongsToUpdates, manyToManyRelationships } = processRelationships(
|
|
73
|
-
73 scope,
|
|
74
|
-
74 { context }
|
|
75
|
-
75 )
|
|
76
|
-
76
|
|
77
|
-
77 await validateResourceAttributesBeforeWrite({
|
|
78
|
-
78 context,
|
|
79
|
-
79 schema,
|
|
80
|
-
80 belongsToUpdates,
|
|
81
|
-
81 runHooks
|
|
82
|
-
82 })
|
|
83
|
-
83
|
|
84
|
-
84 // Check existence first
|
|
85
|
-
85 context.exists = await helpers.dataExists({
|
|
86
|
-
86 scopeName,
|
|
87
|
-
87 context
|
|
88
|
-
88 })
|
|
89
|
-
89
|
|
90
|
-
90 context.isCreate = !context.exists
|
|
91
|
-
91 context.isUpdate = context.exists
|
|
92
|
-
92
|
|
93
|
-
93 // Fetch minimal record for authorization checks (only for updates)
|
|
94
|
-
94 if (context.isUpdate) {
|
|
95
|
-
95 const minimalRecord = await helpers.dataGetMinimal({
|
|
96
|
-
96 scopeName,
|
|
97
|
-
97 context,
|
|
98
|
-
98 runHooks
|
|
99
|
-
99 })
|
|
100
|
-
100
|
|
101
|
-
101 if (!minimalRecord) {
|
|
102
|
-
102 throw new RestApiResourceError(
|
|
103
|
-
103 `Resource not found: ${scopeName}/${context.id}`,
|
|
104
|
-
104 ERROR_SUBTYPES.NOT_FOUND
|
|
105
|
-
105 )
|
|
106
|
-
106 }
|
|
107
|
-
107
|
|
108
|
-
108 context.minimalRecord = minimalRecord
|
|
109
|
-
109 }
|
|
110
|
-
110
|
|
111
|
-
111 // For PUT, we also need to handle relationships that are NOT provided
|
|
112
|
-
112 // (they should be set to null/empty as PUT is a complete replacement)
|
|
113
|
-
113 const allRelationships = {}
|
|
114
|
-
114
|
|
115
|
-
115 // Collect all defined relationships for this resource
|
|
116
|
-
116 for (const [relName, relDef] of Object.entries(schemaRelationships || {})) {
|
|
117
|
-
117 if (relDef.type === 'manyToMany') {
|
|
118
|
-
118 allRelationships[relName] = {
|
|
119
|
-
119 type: 'manyToMany',
|
|
120
|
-
120 relDef: {
|
|
121
|
-
121 through: relDef.through,
|
|
122
|
-
122 foreignKey: relDef.foreignKey,
|
|
123
|
-
123 otherKey: relDef.otherKey
|
|
124
|
-
124 }
|
|
125
|
-
125 }
|
|
126
|
-
126 }
|
|
127
|
-
127 }
|
|
128
|
-
128
|
|
129
|
-
129 // Also check schema fields for belongsTo relationships
|
|
130
|
-
130 for (const [fieldName, fieldDef] of Object.entries(schemaStructure)) {
|
|
131
|
-
131 if (fieldDef.as && fieldDef.belongsTo) {
|
|
132
|
-
132 allRelationships[fieldDef.as] = {
|
|
133
|
-
133 type: 'belongsTo',
|
|
134
|
-
134 fieldName,
|
|
135
|
-
135 fieldDef
|
|
136
|
-
136 }
|
|
137
|
-
137 }
|
|
138
|
-
138 }
|
|
139
|
-
139
|
|
140
|
-
140 // Process missing relationships (PUT should null them out only if relationships object exists)
|
|
141
|
-
141 const hasRelationshipsObject = context.inputRecord.data.relationships !== undefined
|
|
142
|
-
142 const providedRelationships = new Set(Object.keys(context.inputRecord.data.relationships || {}))
|
|
143
|
-
143
|
|
144
|
-
144 // Only null out missing relationships if a relationships object was provided
|
|
145
|
-
145 if (hasRelationshipsObject) {
|
|
146
|
-
146 for (const [relName, relInfo] of Object.entries(allRelationships)) {
|
|
147
|
-
147 if (!providedRelationships.has(relName)) {
|
|
148
|
-
148 if (relInfo.type === 'belongsTo') {
|
|
149
|
-
149 belongsToUpdates[relInfo.fieldName] = null
|
|
150
|
-
150 } else if (relInfo.type === 'manyToMany') {
|
|
151
|
-
151 // Add to manyToManyRelationships with empty array
|
|
152
|
-
152 manyToManyRelationships.push({
|
|
153
|
-
153 relName,
|
|
154
|
-
154 relDef: relInfo.relDef,
|
|
155
|
-
155 relData: [] // Empty array means delete all
|
|
156
|
-
156 })
|
|
157
|
-
157 }
|
|
158
|
-
158 }
|
|
159
|
-
159 }
|
|
160
|
-
160 }
|
|
161
|
-
161
|
|
162
|
-
162 // Merge belongsTo updates with attributes
|
|
163
|
-
163 if (Object.keys(belongsToUpdates).length > 0) {
|
|
164
|
-
164 context.inputRecord.data.attributes = {
|
|
165
|
-
165 ...context.inputRecord.data.attributes,
|
|
166
|
-
166 ...belongsToUpdates
|
|
167
|
-
167 }
|
|
168
|
-
168 }
|
|
169
|
-
169
|
|
170
|
-
170 // Centralised checkPermissions function
|
|
171
|
-
171 await scope.checkPermissions({
|
|
172
|
-
172 method: 'put',
|
|
173
|
-
173 originalContext: context,
|
|
174
|
-
174 })
|
|
175
|
-
175
|
|
176
|
-
176 await runHooks('beforeDataCall')
|
|
177
|
-
177 await runHooks('beforeDataCallPut')
|
|
178
|
-
178
|
|
179
|
-
179 // Apply field setters after validation and before storage
|
|
180
|
-
180 if (context.inputRecord?.data?.attributes) {
|
|
181
|
-
181 context.inputRecord.data.attributes = await applyFieldSetters(
|
|
182
|
-
182 context.inputRecord.data.attributes,
|
|
183
|
-
183 context.schemaInfo,
|
|
184
|
-
184 context,
|
|
185
|
-
185 api,
|
|
186
|
-
186 helpers
|
|
187
|
-
187 )
|
|
188
|
-
188 }
|
|
189
|
-
189
|
|
190
|
-
190 // Pass the operation type to the helper
|
|
191
|
-
191 await helpers.dataPut({
|
|
192
|
-
192 scopeName,
|
|
193
|
-
193 context
|
|
194
|
-
194 })
|
|
195
|
-
195 await runHooks('afterDataCallPut')
|
|
196
|
-
196 await runHooks('afterDataCall')
|
|
197
|
-
197
|
|
198
|
-
198 // Process many-to-many relationships after main record update/creation
|
|
199
|
-
199 for (const { relName, relDef, relData } of manyToManyRelationships) {
|
|
200
|
-
200 if (relDef?.through && api.anyapi?.links?.syncMany) {
|
|
201
|
-
201 await api.anyapi.links.syncMany({
|
|
202
|
-
202 context,
|
|
203
|
-
203 scopeName,
|
|
204
|
-
204 relName,
|
|
205
|
-
205 relDef,
|
|
206
|
-
206 relData,
|
|
207
|
-
207 isUpdate: context.isUpdate,
|
|
208
|
-
208 })
|
|
209
|
-
209 continue
|
|
210
|
-
210 }
|
|
211
|
-
211
|
|
212
|
-
212 await validatePivotResource(scopes, relDef, relName)
|
|
213
|
-
213
|
|
214
|
-
214 if (context.isUpdate) {
|
|
215
|
-
215 await updateManyToManyRelationship(null, {
|
|
216
|
-
216 api,
|
|
217
|
-
217 context: {
|
|
218
|
-
218 resourceId: context.id,
|
|
219
|
-
219 relDef,
|
|
220
|
-
220 relData,
|
|
221
|
-
221 transaction: context.transaction
|
|
222
|
-
222 }
|
|
223
|
-
223 })
|
|
224
|
-
224 } else if (relData.length > 0) {
|
|
225
|
-
225 await createPivotRecords(api, context.id, relDef, relData, context.transaction)
|
|
226
|
-
226 }
|
|
227
|
-
227 }
|
|
228
|
-
228
|
|
229
|
-
229 const ret = await handleRecordReturnAfterWrite({
|
|
230
|
-
230 context,
|
|
231
|
-
231 scopeName,
|
|
232
|
-
232 api,
|
|
233
|
-
233 scopes,
|
|
234
|
-
234 schemaStructure,
|
|
235
|
-
235 schemaRelationships,
|
|
236
|
-
236 scopeOptions,
|
|
237
|
-
237 vars,
|
|
238
|
-
238 runHooks,
|
|
239
|
-
239 helpers,
|
|
240
|
-
240 log
|
|
241
|
-
241 })
|
|
242
|
-
242
|
|
243
|
-
243 // Commit transaction if we created it
|
|
244
|
-
244 if (context.shouldCommit) {
|
|
245
|
-
245 await context.transaction.commit()
|
|
246
|
-
246 await runHooks('afterCommit')
|
|
247
|
-
247 }
|
|
248
|
-
248
|
|
249
|
-
249 return ret
|
|
250
|
-
250 } catch (error) {
|
|
251
|
-
251 await handleWriteMethodError(error, context, 'PUT', scopeName, log, runHooks)
|
|
252
|
-
252 }
|
|
253
|
-
253 }
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
1 import { RestApiValidationError } from '../../../lib/rest-api-errors.js'
|
|
2
|
-
2 import { validateQueryPayload } from '../lib/querying-writing/payload-validators.js'
|
|
3
|
-
3 import { normalizeRecordAttributes } from '../lib/querying-writing/database-value-normalizers.js'
|
|
4
|
-
4 import { getRequestedComputedFields } from '../lib/querying-writing/knex-field-helpers.js'
|
|
5
|
-
5 import { transformJsonApiToSimplified } from '../lib/querying-writing/simplified-helpers.js'
|
|
6
|
-
6 import { cascadeConfig } from './common.js'
|
|
7
|
-
7
|
|
8
|
-
8 /**
|
|
9
|
-
9 * QUERY
|
|
10
|
-
10 * Retrieves a collection of resources (e.g., a list of articles) based on provided criteria.
|
|
11
|
-
11 * This function sends a GET request to /api/{resourceType}.
|
|
12
|
-
12 *
|
|
13
|
-
13 * @param {string} resourceType - The type of resource collection to fetch (e.g., "articles").
|
|
14
|
-
14 * @param {object} [queryParams={}] - Optional. An object to customize the query for the collection.
|
|
15
|
-
15 * @param {string[]} [queryParams.include=[]] - An optional array of relationship paths to sideload for each resource in the collection. These paths will be converted to a comma-separated string for the URL (e.g., `['author', 'comments.user']` becomes `author,comments.user`). Supports deep relationships (e.g., "publisher.country").
|
|
16
|
-
16 * @param {object} [queryParams.fields] - An object to request only specific fields (sparse fieldsets) for each resource in the collection and its included relationships. Keys are resource types, values are comma-separated field names.
|
|
17
|
-
17 * @param {object} [queryParams.filters] - An object to filter the collection. Keys are filter parameters (specific to your API's implementation, e.g., 'status', 'title'), values are the filter criteria.
|
|
18
|
-
18 * @param {string[]} [queryParams.sort=[]] - An optional array of fields to sort the collection by. Each string represents a field; prefix with '-' for descending order (e.g., `['title', '-published-date']` becomes `title,-published-date`).
|
|
19
|
-
19 * @param {object} [queryParams.page] - An object for pagination. Typically includes `number` (page number) and `size` (items per page). E.g., `{ number: 1, size: 10 }`.
|
|
20
|
-
20 * @returns {Promise<object>} A Promise that resolves to the JSON:API response document containing the resource collection.
|
|
21
|
-
21 */
|
|
22
|
-
22 export default async function queryMethod ({
|
|
23
|
-
23 params,
|
|
24
|
-
24 context,
|
|
25
|
-
25 vars,
|
|
26
|
-
26 helpers,
|
|
27
|
-
27 scope,
|
|
28
|
-
28 scopes,
|
|
29
|
-
29 runHooks,
|
|
30
|
-
30 apiOptions,
|
|
31
|
-
31 pluginOptions,
|
|
32
|
-
32 scopeOptions,
|
|
33
|
-
33 scopeName,
|
|
34
|
-
34 log,
|
|
35
|
-
35 api
|
|
36
|
-
36 }) {
|
|
37
|
-
37 context.method = 'query'
|
|
38
|
-
38
|
|
39
|
-
39 // Determine which simplified setting to use based on transport
|
|
40
|
-
40 const isTransport = params.isTransport === true
|
|
41
|
-
41
|
|
42
|
-
42 // Use vars which automatically cascade from scope to global
|
|
43
|
-
43 const defaultSimplified = isTransport ? vars.simplifiedTransport : vars.simplifiedApi
|
|
44
|
-
44
|
|
45
|
-
45 // Get simplified setting - from params only (per-call override) or use default
|
|
46
|
-
46 context.simplified = params.simplified !== undefined ? params.simplified : defaultSimplified
|
|
47
|
-
47
|
|
48
|
-
48 // Assign common context properties
|
|
49
|
-
49 context.schemaInfo = scopes[scopeName].vars.schemaInfo // This is the object variable created by compileSchemas
|
|
50
|
-
50 context.queryParams = params.queryParams || {}
|
|
51
|
-
51
|
|
52
|
-
52 // These only make sense as parameter per query
|
|
53
|
-
53 context.queryParams.fields = cascadeConfig('fields', [params.queryParams], {})
|
|
54
|
-
54 context.queryParams.include = cascadeConfig('include', [params.queryParams], [])
|
|
55
|
-
55 context.queryParams.sort = cascadeConfig('sort', [params.queryParams], [])
|
|
56
|
-
56 context.queryParams.page = cascadeConfig('page', [params.queryParams], {})
|
|
57
|
-
57
|
|
58
|
-
58 context.transaction = params.transaction
|
|
59
|
-
59 context.db = context.transaction || api.knex.instance
|
|
60
|
-
60
|
|
61
|
-
61 context.scopeName = scopeName
|
|
62
|
-
62
|
|
63
|
-
63 // These are just shortcuts used in this function and will be returned
|
|
64
|
-
64 const searchSchema = context.schemaInfo.searchSchemaInstance
|
|
65
|
-
65 const schemaStructure = context.schemaInfo.schemaInstance.structure
|
|
66
|
-
66 const schemaRelationships = context.schemaInfo.schemaRelationships
|
|
67
|
-
67
|
|
68
|
-
68 // Sortable fields and sort (mab)
|
|
69
|
-
69 context.sortableFields = vars.sortableFields
|
|
70
|
-
70 // Apply default sort if no sort specified
|
|
71
|
-
71 if (context.queryParams.sort.length === 0 && vars.defaultSort) {
|
|
72
|
-
72 context.queryParams.sort = Array.isArray(vars.defaultSort) ? vars.defaultSort : [vars.defaultSort]
|
|
73
|
-
73 }
|
|
74
|
-
74
|
|
75
|
-
75 // Validate query parameters to ensure they follow JSON:API specification and security rules.
|
|
76
|
-
76 // This checks that filters are valid field names, sort fields exist in sortableFields array
|
|
77
|
-
77 // (preventing SQL injection), pagination uses valid page[size]/page[number] format, and include
|
|
78
|
-
78 // paths reference real relationships. Example: sort: ['-createdAt', 'title'] is checked against
|
|
79
|
-
79 // sortableFields to ensure users can't sort by sensitive fields like 'password_hash'.
|
|
80
|
-
80 validateQueryPayload({ queryParams: context.queryParams }, context.sortableFields, vars.includeDepthLimit)
|
|
81
|
-
81
|
|
82
|
-
82 // Validate search/filter parameters against searchSchema
|
|
83
|
-
83 if (context.queryParams.filters && Object.keys(context.queryParams.filters).length > 0) {
|
|
84
|
-
84 // Only allow filtering if searchSchema is defined
|
|
85
|
-
85 if (!searchSchema) {
|
|
86
|
-
86 throw new RestApiValidationError(
|
|
87
|
-
87 `Filtering is not enabled for resource '${scopeName}'. To enable filtering, add 'search: true' to schema fields or define a searchSchema.`,
|
|
88
|
-
88 {
|
|
89
|
-
89 fields: Object.keys(context.queryParams.filters).map(field => `filters.${field}`),
|
|
90
|
-
90 violations: [{
|
|
91
|
-
91 field: 'filters',
|
|
92
|
-
92 rule: 'filtering_not_enabled',
|
|
93
|
-
93 message: 'Resource does not have searchable fields defined'
|
|
94
|
-
94 }]
|
|
95
|
-
95 }
|
|
96
|
-
96 )
|
|
97
|
-
97 }
|
|
98
|
-
98
|
|
99
|
-
99 // Validate the filter parameters searchSchema
|
|
100
|
-
100 const { validatedObject, errors } = await searchSchema.validate(context.queryParams.filters, {
|
|
101
|
-
101 onlyObjectValues: true // Partial validation for filters
|
|
102
|
-
102 })
|
|
103
|
-
103
|
|
104
|
-
104 // If there are validation errors, throw an error
|
|
105
|
-
105 if (Object.keys(errors).length > 0) {
|
|
106
|
-
106 const violations = Object.entries(errors).map(([field, error]) => ({
|
|
107
|
-
107 field: `filters.${field}`,
|
|
108
|
-
108 rule: error.code || 'invalid_value',
|
|
109
|
-
109 message: error.message
|
|
110
|
-
110 }))
|
|
111
|
-
111
|
|
112
|
-
112 throw new RestApiValidationError(
|
|
113
|
-
113 'Invalid filter parameters',
|
|
114
|
-
114 {
|
|
115
|
-
115 fields: Object.keys(errors).map(field => `filters.${field}`),
|
|
116
|
-
116 violations
|
|
117
|
-
117 }
|
|
118
|
-
118 )
|
|
119
|
-
119 }
|
|
120
|
-
120
|
|
121
|
-
121 // Replace filter with validated/transformed values
|
|
122
|
-
122 context.queryParams.filters = validatedObject
|
|
123
|
-
123 }
|
|
124
|
-
124
|
|
125
|
-
125 // Centralised checkPermissions function
|
|
126
|
-
126 await scope.checkPermissions({
|
|
127
|
-
127 method: 'query',
|
|
128
|
-
128 originalContext: context,
|
|
129
|
-
129 })
|
|
130
|
-
130
|
|
131
|
-
131 await runHooks('beforeData')
|
|
132
|
-
132 await runHooks('beforeDataQuery')
|
|
133
|
-
133 context.record = await helpers.dataQuery({
|
|
134
|
-
134 scopeName,
|
|
135
|
-
135 context,
|
|
136
|
-
136 runHooks
|
|
137
|
-
137 })
|
|
138
|
-
138
|
|
139
|
-
139 // Normalize database values (e.g., convert 1/0 to true/false for booleans)
|
|
140
|
-
140 context.record = normalizeRecordAttributes(context.record, scopes)
|
|
141
|
-
141
|
|
142
|
-
142 // Make a backup
|
|
143
|
-
143 try {
|
|
144
|
-
144 context.originalRecord = structuredClone(context.record)
|
|
145
|
-
145 } catch (e) {
|
|
146
|
-
146 log.error('Failed to clone record:', {
|
|
147
|
-
147 error: e.message,
|
|
148
|
-
148 recordKeys: Object.keys(context.record || {}),
|
|
149
|
-
149 hasHttpRequest: !!context.raw?.req
|
|
150
|
-
150 })
|
|
151
|
-
151 throw e
|
|
152
|
-
152 }
|
|
153
|
-
153
|
|
154
|
-
154 // This will enhance record, which is the WHOLE JSON:API record
|
|
155
|
-
155 await runHooks('enrichRecord')
|
|
156
|
-
156
|
|
157
|
-
157 // Get computed field information for main resource
|
|
158
|
-
158 const computedFields = scope.vars.schemaInfo?.computed || {}
|
|
159
|
-
159 const requestedFields = context.queryParams.fields?.[scopeName]
|
|
160
|
-
160 const requestedComputedFields = getRequestedComputedFields(scopeName, requestedFields, computedFields)
|
|
161
|
-
161
|
|
162
|
-
162 // Run enrichAttributes for every single set of attribute, calling it from the right scope
|
|
163
|
-
163 for (const entry of context.record.data) {
|
|
164
|
-
164 entry.attributes = await scope.enrichAttributes({
|
|
165
|
-
165 attributes: entry.attributes,
|
|
166
|
-
166 parentContext: context,
|
|
167
|
-
167 requestedComputedFields,
|
|
168
|
-
168 isMainResource: true,
|
|
169
|
-
169 computedDependencies: context.computedDependencies
|
|
170
|
-
170 })
|
|
171
|
-
171 }
|
|
172
|
-
172 for (const entry of (context.record.included || [])) {
|
|
173
|
-
173 const entryScope = scopes[entry.type]
|
|
174
|
-
174 const entryComputed = entryScope.vars.schemaInfo?.computed || {}
|
|
175
|
-
175 const entryRequestedFields = context.queryParams.fields?.[entry.type]
|
|
176
|
-
176 const entryRequestedComputed = getRequestedComputedFields(
|
|
177
|
-
177 entry.type,
|
|
178
|
-
178 entryRequestedFields,
|
|
179
|
-
179 entryComputed
|
|
180
|
-
180 )
|
|
181
|
-
181
|
|
182
|
-
182 entry.attributes = await entryScope.enrichAttributes({
|
|
183
|
-
183 attributes: entry.attributes,
|
|
184
|
-
184 parentContext: context,
|
|
185
|
-
185 requestedComputedFields: entryRequestedComputed,
|
|
186
|
-
186 isMainResource: false,
|
|
187
|
-
187 computedDependencies: entry.__$jsonrestapi_computed_deps$__
|
|
188
|
-
188 })
|
|
189
|
-
189 }
|
|
190
|
-
190
|
|
191
|
-
191 // The called hooks should NOT change context.record
|
|
192
|
-
192 await runHooks('finish')
|
|
193
|
-
193 await runHooks('finishQuery')
|
|
194
|
-
194
|
|
195
|
-
195 // Transform output if in simplified mode
|
|
196
|
-
196 if (context.simplified) {
|
|
197
|
-
197 // Convert JSON:API response back to simplified format
|
|
198
|
-
198 // Example: {data: {type: 'posts', id: '1', attributes: {title: 'My Post'}, relationships: {author: {data: {type: 'users', id: '123'}}}}}
|
|
199
|
-
199 // becomes: {id: '1', title: 'My Post', author_id: '123'} - flattens structure and restores foreign keys
|
|
200
|
-
200 return transformJsonApiToSimplified(
|
|
201
|
-
201 { record: context.record },
|
|
202
|
-
202 { context: { schemaStructure, schemaRelationships, scopes } }
|
|
203
|
-
203 )
|
|
204
|
-
204 }
|
|
205
|
-
205
|
|
206
|
-
206 return context.record
|
|
207
|
-
207 }
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
1 import { ValidationError } from 'hooked-api'
|
|
2
|
-
2
|
|
3
|
-
3 /**
|
|
4
|
-
4 * release
|
|
5
|
-
5 * Runs the hook that tells plugins to release resources
|
|
6
|
-
6 */
|
|
7
|
-
7 export default async ({ runHooks }) => {
|
|
8
|
-
8 // Run the addRoute hook to notify transport plugins
|
|
9
|
-
9 await runHooks('release')
|
|
10
|
-
10
|
|
11
|
-
11 return { }
|
|
12
|
-
12 }
|
package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin.js.txt
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
1 import { validateRelationships } from './lib/querying-writing/scope-validations.js'
|
|
2
|
-
2
|
|
3
|
-
3 // Import hook functions
|
|
4
|
-
4 import compileResourceSchemas from './rest-api-plugin-hooks/compile-resource-schemas.js'
|
|
5
|
-
5 import validateIncludeConfigurations from './rest-api-plugin-hooks/validate-include-configurations.js'
|
|
6
|
-
6 import turnScopeInitIntoVars from './rest-api-plugin-hooks/turn-scope-init-into-vars.js'
|
|
7
|
-
7 import registerScopeRoutes from './rest-api-plugin-hooks/register-scope-routes.js'
|
|
8
|
-
8 import registerRelationshipRoutes from './rest-api-plugin-hooks/register-relationship-routes.js'
|
|
9
|
-
9
|
|
10
|
-
10 // Import method functions
|
|
11
|
-
11 import queryMethod from './rest-api-plugin-methods/query.js'
|
|
12
|
-
12 import getMethod from './rest-api-plugin-methods/get.js'
|
|
13
|
-
13 import postMethod from './rest-api-plugin-methods/post.js'
|
|
14
|
-
14 import putMethod from './rest-api-plugin-methods/put.js'
|
|
15
|
-
15 import patchMethod from './rest-api-plugin-methods/patch.js'
|
|
16
|
-
16 import deleteMethod from './rest-api-plugin-methods/delete.js'
|
|
17
|
-
17 import enrichAttributesMethod from './rest-api-plugin-methods/enrich-attributes.js'
|
|
18
|
-
18 import checkPermissionsMethod from './rest-api-plugin-methods/check-permissions.js'
|
|
19
|
-
19 import addRouteMethod from './rest-api-plugin-methods/add-route.js'
|
|
20
|
-
20 import releaseMethod from './rest-api-plugin-methods/release.js'
|
|
21
|
-
21 import { defaultDataHelpers } from './lib/querying-writing/default-data-helpers.js'
|
|
22
|
-
22 import { DEFAULT_QUERY_LIMIT, DEFAULT_MAX_QUERY_LIMIT, DEFAULT_INCLUDE_DEPTH_LIMIT } from './lib/querying-writing/knex-constants.js'
|
|
23
|
-
23
|
|
24
|
-
24 import getRelatedMethod from './rest-api-plugin-methods/get-related.js'
|
|
25
|
-
25 import postRelationshipMethod from './rest-api-plugin-methods/post-relationship.js'
|
|
26
|
-
26 import getRelationshipMethod from './rest-api-plugin-methods/get-relationship.js'
|
|
27
|
-
27 import patchRelationshipMethod from './rest-api-plugin-methods/patch-relationship.js'
|
|
28
|
-
28 import deleteRelationshipMethod from './rest-api-plugin-methods/delete-relationship.js'
|
|
29
|
-
29
|
|
30
|
-
30 export const RestApiPlugin = {
|
|
31
|
-
31 name: 'rest-api',
|
|
32
|
-
32
|
|
33
|
-
33 install ({ helpers, addScopeMethod, addApiMethod, vars, addHook, runHooks, apiOptions, pluginOptions, api, setScopeAlias, scopes, log, on }) {
|
|
34
|
-
34 // **************
|
|
35
|
-
35 // Initial setup
|
|
36
|
-
36 // **************
|
|
37
|
-
37
|
|
38
|
-
38 // Initialize the rest namespace for REST API functionality
|
|
39
|
-
39 api.rest = {}
|
|
40
|
-
40
|
|
41
|
-
41 // Set up REST-friendly aliases
|
|
42
|
-
42 setScopeAlias('resources', 'addResource')
|
|
43
|
-
43
|
|
44
|
-
44 // **********
|
|
45
|
-
45 // Variables
|
|
46
|
-
46 // **********
|
|
47
|
-
47
|
|
48
|
-
48 // Initialize default vars for the plugin from pluginOptions
|
|
49
|
-
49 const restApiOptions = pluginOptions || {}
|
|
50
|
-
50
|
|
51
|
-
51 // These will be used as default fallbacks by the vars proxy if
|
|
52
|
-
52 // they are not set in the scope options
|
|
53
|
-
53 vars.queryDefaultLimit = restApiOptions.queryDefaultLimit || DEFAULT_QUERY_LIMIT
|
|
54
|
-
54 vars.queryMaxLimit = restApiOptions.queryMaxLimit || DEFAULT_MAX_QUERY_LIMIT
|
|
55
|
-
55 vars.includeDepthLimit = restApiOptions.includeDepthLimit || DEFAULT_INCLUDE_DEPTH_LIMIT
|
|
56
|
-
56 vars.enablePaginationCounts = restApiOptions.enablePaginationCounts || true
|
|
57
|
-
57
|
|
58
|
-
58 // New simplified settings
|
|
59
|
-
59 vars.simplifiedTransport = restApiOptions.simplifiedTransport !== undefined
|
|
60
|
-
60 ? restApiOptions.simplifiedTransport
|
|
61
|
-
61 : false // Default false for JSON:API compliance over the wire
|
|
62
|
-
62
|
|
63
|
-
63 vars.simplifiedApi = restApiOptions.simplifiedApi !== undefined
|
|
64
|
-
64 ? restApiOptions.simplifiedApi
|
|
65
|
-
65 : true // Default true for better DX in programmatic API
|
|
66
|
-
66
|
|
67
|
-
67 vars.idProperty = restApiOptions.idProperty || 'id'
|
|
68
|
-
68
|
|
69
|
-
69 // Return full record configuration for API and Transport
|
|
70
|
-
70 // Support values: 'no', 'minimal', 'full'
|
|
71
|
-
71 const normalizeReturnValue = (value, defaultValue) => {
|
|
72
|
-
72 if (['no', 'minimal', 'full'].includes(value)) return value
|
|
73
|
-
73 return defaultValue
|
|
74
|
-
74 }
|
|
75
|
-
75
|
|
76
|
-
76 const optionConfigs = [
|
|
77
|
-
77 {
|
|
78
|
-
78 propName: 'returnRecordApi',
|
|
79
|
-
79 defaultValue: 'full',
|
|
80
|
-
80 },
|
|
81
|
-
81 {
|
|
82
|
-
82 propName: 'returnRecordTransport',
|
|
83
|
-
83 defaultValue: 'no',
|
|
84
|
-
84 },
|
|
85
|
-
85 ]
|
|
86
|
-
86
|
|
87
|
-
87 for (const config of optionConfigs) {
|
|
88
|
-
88 const optionValue = restApiOptions[config.propName]
|
|
89
|
-
89 let processedValue
|
|
90
|
-
90
|
|
91
|
-
91 if (typeof optionValue === 'object' && optionValue !== null) {
|
|
92
|
-
92 processedValue = {
|
|
93
|
-
93 post: normalizeReturnValue(optionValue.post, config.defaultValue),
|
|
94
|
-
94 put: normalizeReturnValue(optionValue.put, config.defaultValue),
|
|
95
|
-
95 patch: normalizeReturnValue(optionValue.patch, config.defaultValue),
|
|
96
|
-
96 }
|
|
97
|
-
97 } else if (optionValue !== undefined) {
|
|
98
|
-
98 const normalized = normalizeReturnValue(optionValue, config.defaultValue)
|
|
99
|
-
99 processedValue = { post: normalized, put: normalized, patch: normalized }
|
|
100
|
-
100 } else {
|
|
101
|
-
101 processedValue = { post: config.defaultValue, put: config.defaultValue, patch: config.defaultValue }
|
|
102
|
-
102 }
|
|
103
|
-
103 vars[config.propName] = processedValue
|
|
104
|
-
104 }
|
|
105
|
-
105
|
|
106
|
-
106 log.debug('returnRecordApi configuration:', vars.returnRecordApi)
|
|
107
|
-
107 log.debug('returnRecordTransport configuration:', vars.returnRecordTransport)
|
|
108
|
-
108
|
|
109
|
-
109 // Schema cache vars
|
|
110
|
-
110 vars.schemaProcessed = false
|
|
111
|
-
111 vars.schema = null
|
|
112
|
-
112
|
|
113
|
-
113 // ******************************
|
|
114
|
-
114 // Scope (resources) added hooks
|
|
115
|
-
115 // ******************************
|
|
116
|
-
116
|
|
117
|
-
117 addHook('scope:added', 'validateRelationships', {}, validateRelationships)
|
|
118
|
-
118 addHook('scope:added', 'compileResourceSchemas', {}, compileResourceSchemas)
|
|
119
|
-
119 addHook('scope:added', 'validateIncludeConfigurations', {}, validateIncludeConfigurations)
|
|
120
|
-
120 addHook('scope:added', 'turnScopeInitIntoVars', {}, turnScopeInitIntoVars)
|
|
121
|
-
121
|
|
122
|
-
122 // *********
|
|
123
|
-
123 // Methods
|
|
124
|
-
124 // *********
|
|
125
|
-
125
|
|
126
|
-
126 addApiMethod('addRoute', addRouteMethod)
|
|
127
|
-
127
|
|
128
|
-
128 addApiMethod('release', releaseMethod)
|
|
129
|
-
129
|
|
130
|
-
130 // Main REST methods
|
|
131
|
-
131 addScopeMethod('query', queryMethod)
|
|
132
|
-
132 addScopeMethod('get', getMethod)
|
|
133
|
-
133 addScopeMethod('post', postMethod)
|
|
134
|
-
134 addScopeMethod('put', putMethod)
|
|
135
|
-
135 addScopeMethod('patch', patchMethod)
|
|
136
|
-
136 addScopeMethod('delete', deleteMethod)
|
|
137
|
-
137
|
|
138
|
-
138 // Relationship methods
|
|
139
|
-
139 addScopeMethod('getRelationship', getRelationshipMethod)
|
|
140
|
-
140 addScopeMethod('getRelated', getRelatedMethod)
|
|
141
|
-
141 addScopeMethod('postRelationship', postRelationshipMethod)
|
|
142
|
-
142 addScopeMethod('patchRelationship', patchRelationshipMethod)
|
|
143
|
-
143 addScopeMethod('deleteRelationship', deleteRelationshipMethod)
|
|
144
|
-
144
|
|
145
|
-
145 addHook('scope:added', 'registerRelationshipRoutes', {}, registerRelationshipRoutes)
|
|
146
|
-
146 addHook('scope:added', 'registerScopeRoutes', {}, registerScopeRoutes)
|
|
147
|
-
147
|
|
148
|
-
148 // Non-URL methods
|
|
149
|
-
149 addScopeMethod('enrichAttributes', enrichAttributesMethod)
|
|
150
|
-
150 addScopeMethod('checkPermissions', checkPermissionsMethod)
|
|
151
|
-
151
|
|
152
|
-
152 // *********
|
|
153
|
-
153 // Helpers
|
|
154
|
-
154 // *********
|
|
155
|
-
155
|
|
156
|
-
156 // Initialize default data helpers that throw errors until a storage plugin is installed
|
|
157
|
-
157 // These placeholders show storage plugin developers what methods to implement
|
|
158
|
-
158 // Example: helpers.dataGet, helpers.dataPost, etc. will throw "No storage implementation" errors
|
|
159
|
-
159 Object.assign(helpers, defaultDataHelpers)
|
|
160
|
-
160
|
|
161
|
-
161 // Add default getLocation helper for generating resource URLs
|
|
162
|
-
162 // This can be overridden by storage plugins if needed
|
|
163
|
-
163 helpers.getLocation = ({ scopeName, id }) => `/${scopeName}/${id}`
|
|
164
|
-
164 }
|
|
165
|
-
165 }
|