json-rest-api 1.0.29 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +10 -670
- package/LICENSE-GPL-3.0 +674 -0
- package/LICENSE-MIT +21 -0
- package/README.md +39 -30
- package/docs/API.md +744 -1273
- package/docs/GUIDE/01-getting-started.md +338 -0
- package/docs/GUIDE/02-resources-and-relationships.md +33 -0
- package/docs/GUIDE/03-running-example.md +87 -0
- package/docs/GUIDE/04-creating-and-querying.md +181 -0
- package/docs/GUIDE/05-belongs-to.md +177 -0
- package/docs/GUIDE/06-has-many.md +184 -0
- package/docs/GUIDE/07-polymorphic-relationships.md +191 -0
- package/docs/GUIDE/08-many-to-many.md +184 -0
- package/docs/GUIDE/09-pagination-and-sorting.md +149 -0
- package/docs/GUIDE/10-put-and-patch.md +174 -0
- package/docs/GUIDE/11-relationship-endpoints.md +174 -0
- package/docs/GUIDE/12-field-transformations.md +287 -0
- package/docs/GUIDE/13-hooks-and-lifecycle.md +479 -0
- package/docs/GUIDE/14-custom-resource-methods.md +125 -0
- package/docs/GUIDE/15-query-projections.md +266 -0
- package/docs/GUIDE/{GUIDE_X_Autofiltering.md → 16-autofiltering.md} +77 -15
- package/docs/GUIDE/{GUIDE_X_Row_Policies.md → 17-row-policies.md} +137 -44
- package/docs/GUIDE/18-labels.md +86 -0
- package/docs/GUIDE/19-managed-transactions.md +179 -0
- package/docs/GUIDE/20-transaction-outcomes.md +207 -0
- package/docs/GUIDE/21-schema-and-migrations.md +634 -0
- package/docs/GUIDE/22-version-field-migrations.md +208 -0
- package/docs/GUIDE/23-fastify.md +179 -0
- package/docs/GUIDE/24-cors.md +172 -0
- package/docs/GUIDE/{GUIDE_X_URL_Management.md → 25-url-management.md} +7 -1
- package/docs/GUIDE/26-file-uploads.md +278 -0
- package/docs/GUIDE/27-bulk-operations.md +277 -0
- package/docs/GUIDE/28-socketio.md +325 -0
- package/docs/GUIDE/29-writing-plugins.md +278 -0
- package/docs/GUIDE/30-backend-capabilities.md +100 -0
- package/docs/GUIDE/31-positioning.md +181 -0
- package/docs/GUIDE/{GUIDE_Y_Appendices.md → 32-date-and-time.md} +74 -30
- package/docs/GUIDE/33-migrating-to-v2.md +3827 -0
- package/docs/GUIDE/index.md +51 -59
- package/docs/QUICKSTART.md +120 -287
- package/docs/architecture.md +75 -0
- package/docs/contributing.md +178 -0
- package/index.d.ts +36 -0
- package/index.js +1 -22
- package/lib/enhanced-logger.js +91 -85
- package/lib/error-context.js +445 -5
- package/lib/error-formatter.js +219 -59
- package/lib/knex-transaction.js +64 -0
- package/lib/logger-types.d.ts +22 -0
- package/lib/missing-package.js +4 -0
- package/lib/rest-api-errors.js +103 -8
- package/lib/runtime/json-rest-api.js +172 -0
- package/lib/transaction-types.d.ts +59 -0
- package/package.json +97 -27
- package/plugins/core/bulk-operations-plugin.js +191 -184
- package/plugins/core/connectors/express-plugin.js +148 -219
- package/plugins/core/connectors/fastify-plugin.js +125 -82
- package/plugins/core/connectors/lib/busboy-detector.js +74 -156
- package/plugins/core/connectors/lib/connector-core.js +144 -20
- package/plugins/core/connectors/lib/formidable-detector.js +63 -120
- package/plugins/core/connectors/lib/http-validators.js +77 -0
- package/plugins/core/connectors/lib/multipart-helpers.js +18 -0
- package/plugins/core/connectors/lib/request-helpers.js +10 -1
- package/plugins/core/connectors/lib/transport-http-helpers.js +89 -8
- package/plugins/core/connectors/lib/transport-route-schemas.js +7 -4
- package/plugins/core/file-handling-plugin.js +87 -177
- package/plugins/core/lib/anyapi/anyapi-registry.js +213 -164
- package/plugins/core/lib/anyapi/canonical-link-store.js +507 -0
- package/plugins/core/lib/anyapi/canonical-relationship-reader.js +598 -0
- package/plugins/core/lib/anyapi/schema-utils.js +25 -16
- package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +0 -64
- package/plugins/core/lib/dbIntrospection.js +228 -21
- package/plugins/core/lib/dbTablesOperations.js +409 -92
- package/plugins/core/lib/fractional-positioning.js +12 -48
- package/plugins/core/lib/querying/identifier-query.js +19 -0
- package/plugins/core/lib/querying/include-query-helpers.js +364 -0
- package/plugins/core/lib/querying/include-to-many.js +428 -0
- package/plugins/core/lib/querying/include-to-one.js +579 -0
- package/plugins/core/lib/querying/knex-cross-table-search.js +31 -307
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +99 -401
- package/plugins/core/lib/querying/knex-pagination-helpers.js +48 -317
- package/plugins/core/lib/querying/knex-process-includes.js +5 -110
- package/plugins/core/lib/querying/knex-query-helpers-base.js +21 -56
- package/plugins/core/lib/querying/knex-query-helpers.js +298 -448
- package/plugins/core/lib/querying/knex-relationship-includes.js +120 -2138
- package/plugins/core/lib/querying/knex-window-queries.js +62 -253
- package/plugins/core/lib/querying/query-builder-utils.js +44 -4
- package/plugins/core/lib/querying/query-constraint.d.ts +12 -0
- package/plugins/core/lib/querying/query-constraint.js +27 -0
- package/plugins/core/lib/querying/query-field-sort-helpers.js +134 -37
- package/plugins/core/lib/querying/relationship-identifiers.js +208 -0
- package/plugins/core/lib/querying/sort-helpers.js +13 -1
- package/plugins/core/lib/querying/storage-adapter-utils.js +13 -5
- package/plugins/core/lib/querying/url-helpers.js +11 -4
- package/plugins/core/lib/querying-writing/compile-schemas.js +148 -249
- package/plugins/core/lib/querying-writing/connectors-query-parser.js +33 -118
- package/plugins/core/lib/querying-writing/database-capabilities.js +149 -223
- package/plugins/core/lib/querying-writing/database-value-normalizers.js +179 -194
- package/plugins/core/lib/querying-writing/default-data-helpers.js +15 -45
- package/plugins/core/lib/querying-writing/field-selection-types.d.ts +43 -0
- package/plugins/core/lib/querying-writing/field-utils.js +113 -92
- package/plugins/core/lib/querying-writing/knex-constants.d.ts +18 -0
- package/plugins/core/lib/querying-writing/knex-constants.js +2 -26
- package/plugins/core/lib/querying-writing/knex-field-helpers.js +100 -290
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +31 -120
- package/plugins/core/lib/querying-writing/query-field-helpers.js +108 -32
- package/plugins/core/lib/querying-writing/query-field-types.d.ts +60 -0
- package/plugins/core/lib/querying-writing/relationship-contracts.js +33 -13
- package/plugins/core/lib/querying-writing/request-contracts.js +93 -38
- package/plugins/core/lib/querying-writing/resource-id-normalization.js +23 -5
- package/plugins/core/lib/querying-writing/response-options.js +35 -0
- package/plugins/core/lib/querying-writing/schema-helpers.js +203 -324
- package/plugins/core/lib/querying-writing/scope-validations.js +92 -290
- package/plugins/core/lib/querying-writing/simplified-helpers.js +113 -712
- package/plugins/core/lib/storage/canonical-data-helpers.js +683 -0
- package/plugins/core/lib/storage/canonical-storage-mapping.js +51 -20
- package/plugins/core/lib/storage/ordinary-data-helpers.js +710 -0
- package/plugins/core/lib/storage/storage-adapter.js +128 -62
- package/plugins/core/lib/storage/storage-mapping.js +78 -28
- package/plugins/core/lib/storage/storage-types.d.ts +502 -0
- package/plugins/core/lib/writing/many-to-many-manipulations.js +114 -284
- package/plugins/core/lib/writing/relationship-processor.js +94 -158
- package/plugins/core/lib/writing/resource-version.js +172 -0
- package/plugins/core/lib/writing/reverse-relationship-manipulations.js +110 -0
- package/plugins/core/rest-api-anyapi-knex-plugin.js +116 -2485
- package/plugins/core/rest-api-autofilter-plugin.js +17 -19
- package/plugins/core/rest-api-cors-plugin.js +52 -205
- package/plugins/core/rest-api-knex-plugin.js +45 -1077
- package/plugins/core/rest-api-label-plugin.js +25 -38
- package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +5 -5
- package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +9 -6
- package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +7 -26
- package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +20 -26
- package/plugins/core/rest-api-plugin-methods/add-route.js +4 -6
- package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +42 -13
- package/plugins/core/rest-api-plugin-methods/check-permissions.js +11 -5
- package/plugins/core/rest-api-plugin-methods/common.js +291 -395
- package/plugins/core/rest-api-plugin-methods/delete-relationship.js +49 -47
- package/plugins/core/rest-api-plugin-methods/delete.js +31 -25
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +108 -148
- package/plugins/core/rest-api-plugin-methods/get-related.js +113 -305
- package/plugins/core/rest-api-plugin-methods/get-relationship.js +25 -18
- package/plugins/core/rest-api-plugin-methods/get.js +36 -68
- package/plugins/core/rest-api-plugin-methods/lifecycle-types.d.ts +225 -0
- package/plugins/core/rest-api-plugin-methods/patch-relationship.js +28 -25
- package/plugins/core/rest-api-plugin-methods/patch.js +46 -99
- package/plugins/core/rest-api-plugin-methods/post-relationship.js +49 -53
- package/plugins/core/rest-api-plugin-methods/post.js +51 -51
- package/plugins/core/rest-api-plugin-methods/put.js +69 -148
- package/plugins/core/rest-api-plugin-methods/query.js +37 -81
- package/plugins/core/rest-api-plugin-methods/release.js +2 -1
- package/plugins/core/rest-api-plugin.js +32 -46
- package/plugins/core/rest-api-positioning-plugin.js +183 -628
- package/plugins/core/rest-api-query-projections-plugin.js +19 -102
- package/plugins/core/rest-api-row-policy-plugin.js +3 -3
- package/plugins/core/socketio-plugin.js +364 -344
- package/plugins/storage/local-storage.js +40 -36
- package/types/autofilter.d.ts +57 -0
- package/types/bulk-methods.d.ts +63 -0
- package/types/errors.d.ts +118 -0
- package/types/fastify-options.d.ts +19 -0
- package/types/file-detectors.d.ts +33 -0
- package/types/file-storage.d.ts +48 -0
- package/types/hook-context.d.ts +180 -0
- package/types/plugin-options.d.ts +53 -0
- package/types/relationship-methods.d.ts +74 -0
- package/types/representations.d.ts +50 -0
- package/types/resource-methods.d.ts +95 -0
- package/types/resource-schema.d.ts +157 -0
- package/types/row-policy.d.ts +31 -0
- package/types/runtime.d.ts +132 -0
- package/types/transactions.d.ts +21 -0
- package/.claude/agents/library-guide-expert.md +0 -30
- package/.claude/agents/rest-api-internals-expert.md +0 -54
- package/.claude/settings.local.json +0 -70
- package/AGENTS.md +0 -34
- package/CLAUDE.md +0 -160
- package/CRUSH.md +0 -55
- package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +0 -180
- package/SINGLE_VALIDATION_CONTRACT_TODO.txt +0 -172
- package/TODO.todo +0 -13
- package/connectors_ideas/fastify.js +0 -217
- package/connectors_ideas/koa-plugin.js +0 -112
- package/docs/.bundle/config +0 -2
- package/docs/COMPARISON.md +0 -433
- package/docs/GUIDE/GUIDE_1_Initial_Setup.md +0 -1057
- package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +0 -88
- package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +0 -808
- package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +0 -491
- package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +0 -398
- package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +0 -898
- package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +0 -712
- package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +0 -1247
- package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +0 -748
- package/docs/GUIDE/GUIDE_2_9_Relationships_Urls.md +0 -517
- package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +0 -66
- package/docs/GUIDE/GUIDE_3_Field_Transformations.md +0 -1252
- package/docs/GUIDE/GUIDE_5_Non-Db_Resources.md +0 -1107
- package/docs/GUIDE/GUIDE_6_Positioning.md +0 -692
- package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +0 -1491
- package/docs/GUIDE/GUIDE_X_Bulk_Operations.md +0 -732
- package/docs/GUIDE/GUIDE_X_Cors.md +0 -1957
- package/docs/GUIDE/GUIDE_X_Fastify.md +0 -78
- package/docs/GUIDE/GUIDE_X_File_Uploads.md +0 -729
- package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +0 -370
- package/docs/GUIDE/GUIDE_X_Query_Projections.md +0 -159
- package/docs/GUIDE/GUIDE_X_SocketIO.md +0 -1040
- package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +0 -201
- package/docs/Gemfile +0 -6
- package/docs/Gemfile.lock +0 -308
- package/docs/ONBOARDING.md +0 -315
- package/docs/_config.yml +0 -36
- package/docs/_layouts/default.html +0 -123
- package/docs/apple-touch-icon.svg +0 -4
- package/docs/assets/css/style.scss +0 -392
- package/docs/favicon.svg +0 -4
- package/docs/index.md +0 -164
- package/docs/mini-guides/file-uploads-complete.md +0 -161
- package/eslint.config.js +0 -13
- package/examples/url-override-example.js +0 -137
- package/fixing_plan.md +0 -112
- package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +0 -657
- package/plugins/core/lib/querying-writing/return-record-settings.js +0 -47
- package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +0 -110
- package/plugins/storage/s3-storage.js +0 -93
- package/quickTest.js +0 -136
- package/scripts/debug-anyapi-review-filter.mjs +0 -63
- package/scripts/inspect-anyapi-descriptor-country.mjs +0 -19
- package/scripts/inspect-anyapi-descriptor.mjs +0 -19
- package/scripts/run-docs.js +0 -133
- package/test-debug.js +0 -59
- package/tests/README.md +0 -202
- package/tests/TEST_TEMPLATE.test.js +0 -178
- package/tests/anyapi-basic.test.js +0 -105
- package/tests/anyapi-cursor-pagination.test.js +0 -148
- package/tests/anyapi-custom-idproperty.test.js +0 -163
- package/tests/autofilter.test.js +0 -463
- package/tests/bulk-operations.test.js +0 -657
- package/tests/computed-fields.test.js +0 -586
- package/tests/cors.test.js +0 -834
- package/tests/curl.test.js +0 -467
- package/tests/custom-id-mysql-returning-fallback.test.js +0 -128
- package/tests/custom-id-simplified-post.test.js +0 -75
- package/tests/custom-idproperty-relationships.test.js +0 -932
- package/tests/db-introspection.test.js +0 -521
- package/tests/db-table-operations.test.js +0 -798
- package/tests/fastify-plugin.test.js +0 -184
- package/tests/field-getters.test.js +0 -370
- package/tests/field-setters.test.js +0 -306
- package/tests/file-handling.test.js +0 -155
- package/tests/fixtures/api-configs.js +0 -2566
- package/tests/full-jsonapi-belongsto-linkage.test.js +0 -150
- package/tests/helpers/fake-fastify.js +0 -99
- package/tests/helpers/storage-mode.js +0 -65
- package/tests/helpers/test-utils.js +0 -203
- package/tests/http-connectors-parity.test.js +0 -395
- package/tests/id-normalization.test.js +0 -444
- package/tests/include-depth-validation.test.js +0 -331
- package/tests/includes.test.js +0 -427
- package/tests/local-storage.test.js +0 -96
- package/tests/logical-id-write-validation.test.js +0 -151
- package/tests/nested-includes.test.js +0 -617
- package/tests/pagination-boundaries.test.js +0 -154
- package/tests/pagination-contract.test.js +0 -122
- package/tests/pagination-cursor-multifield.test.js +0 -641
- package/tests/pagination-enhanced.test.js +0 -563
- package/tests/polymorphic-relationships.test.js +0 -347
- package/tests/polymorphic-simple.test.js +0 -148
- package/tests/positioning-advanced.test.js +0 -692
- package/tests/positioning-extended.test.js +0 -915
- package/tests/positioning-multifilter.test.js +0 -699
- package/tests/positioning.test.js +0 -560
- package/tests/post-commit-hooks.test.js +0 -120
- package/tests/projected-fields.test.js +0 -319
- package/tests/public-exports.test.js +0 -15
- package/tests/put-create-parity.test.js +0 -216
- package/tests/queries.test.js +0 -521
- package/tests/query-limits.test.js +0 -584
- package/tests/relationship-endpoints.test.js +0 -620
- package/tests/relationship-include-storage-adapter.test.js +0 -56
- package/tests/relationships.test.js +0 -681
- package/tests/rest-api.test.js +0 -337
- package/tests/return-record-settings.test.js +0 -184
- package/tests/row-policy.test.js +0 -489
- package/tests/s3-storage.test.js +0 -38
- package/tests/searchschema-knex-behavior.test.js +0 -187
- package/tests/searchschema-merge.test.js +0 -151
- package/tests/socketio.test.js +0 -860
- package/tests/storage-mapping.test.js +0 -362
- package/tests/temporal-boundaries.test.js +0 -208
- package/tests/test-development-guide.md +0 -370
- package/tests/virtual-fields.test.js +0 -404
|
@@ -1,59 +1,54 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/** @import {
|
|
3
|
+
* IdentityContext, LifecycleArguments, ReadContext, Resource
|
|
4
|
+
* } from './lifecycle-types.js' */
|
|
5
|
+
import { enrichIncludedAttributes } from './enrich-attributes.js'
|
|
6
|
+
import { rejectRemovedOptions, resolveFormat } from '../lib/querying-writing/response-options.js'
|
|
1
7
|
import { RestApiResourceError } from '../../../lib/rest-api-errors.js'
|
|
2
8
|
import { normalizeRecordAttributes } from '../lib/querying-writing/database-value-normalizers.js'
|
|
9
|
+
import { filterResponseFields, getResourceFieldset } from '../lib/querying-writing/field-utils.js'
|
|
3
10
|
import { getRequestedComputedFields, validateRequestedFieldsets } from '../lib/querying-writing/knex-field-helpers.js'
|
|
4
11
|
import { transformJsonApiToSimplified } from '../lib/querying-writing/simplified-helpers.js'
|
|
5
12
|
import { getRequestContracts, validateRequestContractOrThrow } from '../lib/querying-writing/request-contracts.js'
|
|
6
13
|
import { requireExistingResourceId } from '../lib/querying-writing/resource-id-normalization.js'
|
|
7
|
-
import {
|
|
14
|
+
import { validateRequestedIncludes, filterToOneResponseLinkage } from './common.js'
|
|
8
15
|
|
|
9
16
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* @param {
|
|
13
|
-
* @param {object} [queryParams={}] - Optional. An object to customize the query.
|
|
14
|
-
* @param {string} [queryParams.include] - A comma-separated string of relationship paths to sideload (e.g., "authors,publisher").
|
|
15
|
-
* @param {object} [queryParams.fields] - An object to request only specific fields (sparse fieldsets). Keys are resource types, values are comma-separated field names.
|
|
16
|
-
* @returns {Promise<object>} A Promise that resolves to the JSON:API response document.
|
|
17
|
+
* Read one resource using params.id and params.queryParams. Missing resources
|
|
18
|
+
* reject; the final resource uses the selected plain or JSON:API representation.
|
|
19
|
+
* @param {LifecycleArguments<Resource>} args
|
|
17
20
|
*/
|
|
18
21
|
export default async function getMethod ({
|
|
19
22
|
params,
|
|
20
|
-
context,
|
|
23
|
+
context: callerContext,
|
|
21
24
|
vars,
|
|
22
25
|
helpers,
|
|
23
26
|
scope,
|
|
24
27
|
scopes,
|
|
25
28
|
runHooks,
|
|
26
|
-
apiOptions,
|
|
27
|
-
pluginOptions,
|
|
28
29
|
scopeOptions,
|
|
29
30
|
scopeName,
|
|
30
31
|
api
|
|
31
32
|
}) {
|
|
32
|
-
|
|
33
|
+
callerContext.method = 'get'
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
rejectRemovedOptions(params)
|
|
36
|
+
callerContext.format = resolveFormat(params.format, vars.format)
|
|
37
|
+
callerContext.simplified = callerContext.format === 'plain'
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
const defaultSimplified = isTransport ? vars.simplifiedTransport : vars.simplifiedApi
|
|
39
|
-
|
|
40
|
-
// Get simplified setting - from params only (per-call override) or use default
|
|
41
|
-
context.simplified = params.simplified !== undefined ? params.simplified : defaultSimplified
|
|
42
|
-
|
|
43
|
-
// Assign common context properties
|
|
44
|
-
context.schemaInfo = scopes[scopeName].vars.schemaInfo
|
|
39
|
+
callerContext.schemaInfo = scope.vars.schemaInfo
|
|
45
40
|
const rawQueryParams = params.queryParams || {}
|
|
46
|
-
|
|
47
|
-
fields:
|
|
48
|
-
include:
|
|
41
|
+
callerContext.queryParams = {
|
|
42
|
+
fields: rawQueryParams?.fields ?? {},
|
|
43
|
+
...(rawQueryParams.include == null ? {} : { include: rawQueryParams.include })
|
|
49
44
|
}
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
callerContext.transaction = params.transaction
|
|
47
|
+
callerContext.db = callerContext.transaction || api.knex.instance
|
|
53
48
|
|
|
54
|
-
|
|
49
|
+
callerContext.scopeName = scopeName
|
|
50
|
+
const context = /** @type {ReadContext<Resource>} */ (callerContext)
|
|
55
51
|
|
|
56
|
-
// These are just shortcuts used in this function and will be returned
|
|
57
52
|
const schemaStructure = context.schemaInfo.schemaInstance.structure
|
|
58
53
|
const schemaRelationships = context.schemaInfo.schemaRelationships
|
|
59
54
|
|
|
@@ -80,10 +75,12 @@ export default async function getMethod ({
|
|
|
80
75
|
context.id = validatedRequest.id
|
|
81
76
|
context.queryParams = validatedRequest.queryParams || {}
|
|
82
77
|
|
|
78
|
+
const identityContext = /** @type {IdentityContext} */ (context)
|
|
79
|
+
|
|
83
80
|
// Fetch minimal record for authorization checks
|
|
84
81
|
const minimalRecord = await helpers.dataGetMinimal({
|
|
85
82
|
scopeName,
|
|
86
|
-
context,
|
|
83
|
+
context: identityContext,
|
|
87
84
|
runHooks
|
|
88
85
|
})
|
|
89
86
|
|
|
@@ -100,24 +97,22 @@ export default async function getMethod ({
|
|
|
100
97
|
|
|
101
98
|
context.minimalRecord = minimalRecord
|
|
102
99
|
|
|
103
|
-
// Centralised checkPermissions function
|
|
104
100
|
await scope.checkPermissions({
|
|
105
101
|
method: 'get',
|
|
106
102
|
originalContext: context,
|
|
107
103
|
})
|
|
108
104
|
|
|
109
105
|
await validateRequestedFieldsets(context, scopes)
|
|
106
|
+
validateRequestedIncludes(context, scopes)
|
|
110
107
|
await runHooks('beforeData')
|
|
111
108
|
await runHooks('beforeDataGet')
|
|
112
109
|
|
|
113
110
|
context.record = await helpers.dataGet({
|
|
114
111
|
scopeName,
|
|
115
|
-
context,
|
|
112
|
+
context: identityContext,
|
|
116
113
|
runHooks
|
|
117
114
|
})
|
|
118
115
|
|
|
119
|
-
// Check if record was found - storage layer returns null/undefined for non-existent records.
|
|
120
|
-
// This generates a proper 404 error with JSON:API error format instead of returning empty data.
|
|
121
116
|
if (!context.record || !context.record.data) {
|
|
122
117
|
throw new RestApiResourceError(
|
|
123
118
|
'Resource not found',
|
|
@@ -129,73 +124,46 @@ export default async function getMethod ({
|
|
|
129
124
|
)
|
|
130
125
|
}
|
|
131
126
|
|
|
132
|
-
// Normalize database values (e.g., convert 1/0 to true/false for booleans)
|
|
133
127
|
context.record = normalizeRecordAttributes(context.record, scopes)
|
|
134
128
|
|
|
135
129
|
await runHooks('checkDataPermissions')
|
|
136
130
|
await runHooks('checkDataPermissionsGet')
|
|
137
131
|
|
|
138
|
-
// Make a backup
|
|
139
132
|
context.originalRecord = structuredClone(context.record)
|
|
140
133
|
|
|
141
|
-
// This will enhance record, which is the WHOLE JSON:API record
|
|
142
134
|
await runHooks('enrichRecord')
|
|
143
135
|
|
|
144
|
-
// Get computed field information for main resource
|
|
145
136
|
const computedFields = scope.vars.schemaInfo?.computed || {}
|
|
146
|
-
const requestedFields = context.queryParams.fields
|
|
137
|
+
const requestedFields = getResourceFieldset(context.queryParams.fields, scopeName)
|
|
147
138
|
const requestedComputedFields = getRequestedComputedFields(scopeName, requestedFields, computedFields)
|
|
148
139
|
|
|
149
|
-
// Enrich attributes for the main resource
|
|
150
|
-
// Pass computedDependencies from context (set by dataGet in Knex plugin)
|
|
151
|
-
// This tells enrichAttributes which fields to remove after computation
|
|
152
140
|
context.record.data.attributes = await scope.enrichAttributes({
|
|
141
|
+
id: context.record.data.id,
|
|
153
142
|
attributes: context.record.data.attributes,
|
|
154
143
|
parentContext: context,
|
|
155
144
|
requestedComputedFields,
|
|
156
145
|
isMainResource: true,
|
|
157
|
-
computedDependencies: context.computedDependencies
|
|
146
|
+
computedDependencies: context.computedDependencies
|
|
158
147
|
})
|
|
159
148
|
|
|
160
|
-
|
|
161
|
-
const entryScope = scopes[entry.type]
|
|
162
|
-
const entryComputed = entryScope.vars.schemaInfo?.computed || {}
|
|
163
|
-
const entryRequestedFields = context.queryParams.fields?.[entry.type]
|
|
164
|
-
const entryRequestedComputed = getRequestedComputedFields(
|
|
165
|
-
entry.type,
|
|
166
|
-
entryRequestedFields,
|
|
167
|
-
entryComputed
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
entry.attributes = await entryScope.enrichAttributes({
|
|
171
|
-
attributes: entry.attributes,
|
|
172
|
-
parentContext: context,
|
|
173
|
-
requestedComputedFields: entryRequestedComputed,
|
|
174
|
-
isMainResource: false,
|
|
175
|
-
computedDependencies: entry.__$jsonrestapi_computed_deps$__
|
|
176
|
-
})
|
|
177
|
-
}
|
|
149
|
+
await enrichIncludedAttributes(context, scopes)
|
|
178
150
|
|
|
179
151
|
await runHooks('enrichRecordWithRelationships')
|
|
180
152
|
|
|
181
|
-
// The called hooks should NOT change context.record
|
|
182
153
|
await runHooks('finish')
|
|
183
154
|
await runHooks('finishGet')
|
|
155
|
+
await filterToOneResponseLinkage(context, scopes)
|
|
184
156
|
|
|
185
157
|
// Enrichers and finish hooks run after the database pass and may introduce
|
|
186
158
|
// native values. Reassert the public temporal contract at the return boundary.
|
|
187
159
|
context.record = normalizeRecordAttributes(context.record, scopes, {
|
|
188
160
|
source: 'response'
|
|
189
161
|
})
|
|
162
|
+
filterResponseFields(context.record, context.queryParams.fields)
|
|
190
163
|
|
|
191
|
-
|
|
192
|
-
context.schemaInfo = scopes[scopeName].vars.schemaInfo
|
|
164
|
+
context.schemaInfo = scope.vars.schemaInfo
|
|
193
165
|
|
|
194
|
-
// Transform output if in simplified mode
|
|
195
166
|
if (context.simplified) {
|
|
196
|
-
// Convert JSON:API response back to simplified format
|
|
197
|
-
// Example: {data: {type: 'posts', id: '1', attributes: {title: 'My Post'}, relationships: {author: {data: {type: 'users', id: '123'}}}}}
|
|
198
|
-
// becomes: {id: '1', title: 'My Post', author_id: '123'} - flattens structure and restores foreign keys
|
|
199
167
|
return transformJsonApiToSimplified(
|
|
200
168
|
{ record: context.record },
|
|
201
169
|
{ context: { schemaStructure, schemaRelationships, scopes } }
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import type { TransactionContext, TransactionFactory, OwnedTransaction } from '../../../lib/transaction-types.js'
|
|
2
|
+
import type { ResourceFormat, WriteReturning, JsonApiDocument, JsonApiResource, ResourceIdentifier } from '../../../types/representations.js'
|
|
3
|
+
import type { RestApiPluginOptions } from '../../../types/plugin-options.js'
|
|
4
|
+
import type { RuntimeLogger } from '../../../types/runtime.js'
|
|
5
|
+
import type { QueryParams } from '../../../types/resource-methods.js'
|
|
6
|
+
import type {
|
|
7
|
+
StorageDatabase, StorageSchemaInfo, StorageFieldDefinition, StorageRow, QueryFilteringState,
|
|
8
|
+
DataWriteHelpers, CanonicalDataReadHelpers, DataRelatedIdsQuery, CanonicalLinkHelpers, QueryBuilderResult
|
|
9
|
+
} from '../lib/storage/storage-types.js'
|
|
10
|
+
import type { MandatoryQueryConstraint } from '../lib/querying/query-constraint.js'
|
|
11
|
+
import { queryConstraint } from '../lib/querying/query-constraint.js'
|
|
12
|
+
import { writePrecondition } from './common.js'
|
|
13
|
+
|
|
14
|
+
export type Identifier = ResourceIdentifier
|
|
15
|
+
export type RelationshipData = Identifier | Identifier[] | null
|
|
16
|
+
export interface Relationship { data?: RelationshipData }
|
|
17
|
+
export interface InputDocument {
|
|
18
|
+
data: {
|
|
19
|
+
type?: string
|
|
20
|
+
id?: string | number
|
|
21
|
+
attributes?: StorageRow
|
|
22
|
+
relationships?: Record<string, Relationship>
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export interface Resource extends JsonApiResource { __$jsonrestapi_computed_deps$__?: string[] }
|
|
26
|
+
export type Document<Data = Resource | Resource[] | null> = Omit<JsonApiDocument<Data>, 'included'> & { included?: Resource[] }
|
|
27
|
+
export interface Field extends StorageFieldDefinition {
|
|
28
|
+
compute?: (args: {
|
|
29
|
+
id?: string | number
|
|
30
|
+
attributes: StorageRow
|
|
31
|
+
record: StorageRow
|
|
32
|
+
context?: LifecycleContext
|
|
33
|
+
helpers: LifecycleHelpers
|
|
34
|
+
api: LifecycleApi
|
|
35
|
+
}) => unknown
|
|
36
|
+
belongsToPolymorphic?: { typeField: string; idField: string; types: string[] }
|
|
37
|
+
through?: string
|
|
38
|
+
foreignKey?: string
|
|
39
|
+
otherKey?: string
|
|
40
|
+
target?: string
|
|
41
|
+
via?: string
|
|
42
|
+
}
|
|
43
|
+
export interface Schema {
|
|
44
|
+
structure: Record<string, Field>
|
|
45
|
+
create(attributes: StorageRow): Promise<ValidationResult>
|
|
46
|
+
patch(attributes: StorageRow): Promise<ValidationResult>
|
|
47
|
+
replace(attributes: StorageRow): Promise<ValidationResult>
|
|
48
|
+
}
|
|
49
|
+
export interface ValidationResult {
|
|
50
|
+
validatedObject: StorageRow & { id?: string | number }
|
|
51
|
+
errors: Record<string, { code?: string; message: string }>
|
|
52
|
+
}
|
|
53
|
+
export interface FieldCallbackContext {
|
|
54
|
+
id?: string | number
|
|
55
|
+
attributes: StorageRow
|
|
56
|
+
fieldName: string
|
|
57
|
+
originalValue: unknown
|
|
58
|
+
originalAttributes: StorageRow
|
|
59
|
+
record?: StorageRow
|
|
60
|
+
parentContext?: LifecycleContext
|
|
61
|
+
scopeName: string
|
|
62
|
+
method?: string
|
|
63
|
+
api: LifecycleApi
|
|
64
|
+
helpers: LifecycleHelpers
|
|
65
|
+
auth?: unknown
|
|
66
|
+
isMainResource?: boolean
|
|
67
|
+
}
|
|
68
|
+
export interface LifecycleSchema extends StorageSchemaInfo {
|
|
69
|
+
idProperty: string
|
|
70
|
+
readDependencies?: Record<string, readonly string[]>
|
|
71
|
+
schemaInstance: Schema
|
|
72
|
+
schemaStructure: Record<string, Field>
|
|
73
|
+
schemaRelationships: Record<string, Field>
|
|
74
|
+
outputRelationships: Record<string, Field>
|
|
75
|
+
outputFields: Record<string, Field>
|
|
76
|
+
computed: Record<string, Field>
|
|
77
|
+
foreignKeyFields: Set<string>
|
|
78
|
+
fieldSetters: Record<string, {
|
|
79
|
+
setter(value: unknown, context: FieldCallbackContext): unknown
|
|
80
|
+
runSetterAfter?: string[]
|
|
81
|
+
}>
|
|
82
|
+
fieldGetters: Record<string, { getter(value: unknown, context: FieldCallbackContext): unknown }>
|
|
83
|
+
sortedSetterFields: string[]
|
|
84
|
+
sortedGetterFields: string[]
|
|
85
|
+
sortedComputedFields: string[]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Library-owned mutable working state, populated by each method before its stage runs.
|
|
89
|
+
// Attribute values stay unknown until a schema or user callback interprets them.
|
|
90
|
+
export interface LifecycleContext<Data = Resource | Resource[] | null, Input = InputDocument> extends TransactionContext {
|
|
91
|
+
method?: string
|
|
92
|
+
scopeName?: string
|
|
93
|
+
schemaInfo?: LifecycleSchema
|
|
94
|
+
db?: StorageDatabase
|
|
95
|
+
inputRecord?: Input
|
|
96
|
+
record?: Document<Data> | Record<string, never> | null
|
|
97
|
+
queryParams?: QueryParams
|
|
98
|
+
params?: LifecycleParams
|
|
99
|
+
id?: string | number
|
|
100
|
+
relationshipName?: string
|
|
101
|
+
format?: ResourceFormat
|
|
102
|
+
simplified?: boolean
|
|
103
|
+
returning?: WriteReturning
|
|
104
|
+
minimalRecord?: (Omit<Resource, 'id'> & { id?: string }) | null
|
|
105
|
+
originalMinimalRecord?: Omit<Resource, 'id'> & { id?: string }
|
|
106
|
+
originalInputAttributes?: StorageRow
|
|
107
|
+
originalRecord?: Document
|
|
108
|
+
responseRecord?: Document | StorageRow
|
|
109
|
+
isCreate?: boolean
|
|
110
|
+
isUpdate?: boolean
|
|
111
|
+
exists?: boolean
|
|
112
|
+
computedDependencies?: string[]
|
|
113
|
+
returnMeta?: Record<string, unknown>
|
|
114
|
+
sortableFields?: string[]
|
|
115
|
+
auth?: unknown
|
|
116
|
+
[customProperty: string]: unknown
|
|
117
|
+
knexQuery?: QueryFilteringState | null
|
|
118
|
+
[queryConstraint]?: MandatoryQueryConstraint
|
|
119
|
+
}
|
|
120
|
+
export interface LifecycleParams extends Record<string, unknown> {
|
|
121
|
+
id?: unknown
|
|
122
|
+
data?: StorageRow
|
|
123
|
+
document?: InputDocument
|
|
124
|
+
format?: ResourceFormat
|
|
125
|
+
returning?: WriteReturning
|
|
126
|
+
transaction?: OwnedTransaction
|
|
127
|
+
queryParams?: QueryParams
|
|
128
|
+
expectedVersion?: unknown
|
|
129
|
+
relationshipName?: string
|
|
130
|
+
relationshipData?: RelationshipData
|
|
131
|
+
[queryConstraint]?: MandatoryQueryConstraint
|
|
132
|
+
[writePrecondition]?: () => unknown
|
|
133
|
+
}
|
|
134
|
+
export interface LifecycleVars extends RestApiPluginOptions {
|
|
135
|
+
schemaInfo: LifecycleSchema
|
|
136
|
+
defaultSort?: string | string[]
|
|
137
|
+
sortableFields?: string[]
|
|
138
|
+
}
|
|
139
|
+
export interface LifecycleResource {
|
|
140
|
+
scopeOptions: RestApiPluginOptions
|
|
141
|
+
vars: LifecycleVars
|
|
142
|
+
get(params: { id?: string | number; queryParams?: QueryParams; transaction?: OwnedTransaction; format: 'jsonapi' }, context?: LifecycleContext): Promise<Document<Resource>>
|
|
143
|
+
get(params: { id?: string | number; queryParams?: QueryParams; transaction?: OwnedTransaction; format: ResourceFormat }, context?: LifecycleContext): Promise<Document<Resource> | StorageRow>
|
|
144
|
+
query(params: { queryParams?: QueryParams; transaction?: OwnedTransaction; format: 'jsonapi'; [queryConstraint]?: MandatoryQueryConstraint }, context?: LifecycleContext): Promise<Document<Resource[]>>
|
|
145
|
+
patch(params: { id?: string | number; document: InputDocument; transaction?: OwnedTransaction; expectedVersion?: unknown; format: 'jsonapi'; returning: 'none' }, context?: LifecycleContext): Promise<void>
|
|
146
|
+
checkPermissions(params: { method: string; originalContext: LifecycleContext }): Promise<unknown>
|
|
147
|
+
applyQueryFilters(params: QueryFilteringState, context: LifecycleContext): Promise<{ query: QueryBuilderResult } | null | undefined>
|
|
148
|
+
enrichAttributes(params: EnrichmentParams): Promise<StorageRow>
|
|
149
|
+
}
|
|
150
|
+
export interface LifecycleApi {
|
|
151
|
+
resources: Record<string, LifecycleResource>
|
|
152
|
+
knex: { instance: StorageDatabase }
|
|
153
|
+
anyapi?: { links?: CanonicalLinkHelpers }
|
|
154
|
+
}
|
|
155
|
+
export interface LifecycleHelpers extends DataWriteHelpers<unknown, unknown>, CanonicalDataReadHelpers {
|
|
156
|
+
newTransaction: TransactionFactory
|
|
157
|
+
db: StorageDatabase
|
|
158
|
+
dataRelatedIdsQuery: DataRelatedIdsQuery
|
|
159
|
+
}
|
|
160
|
+
export type RunHooks = (event: string) => Promise<unknown>
|
|
161
|
+
export interface LifecycleArguments<Data = Resource | Resource[] | null> {
|
|
162
|
+
params: LifecycleParams
|
|
163
|
+
context: LifecycleContext<Data>
|
|
164
|
+
vars: LifecycleVars
|
|
165
|
+
helpers: LifecycleHelpers
|
|
166
|
+
scope: LifecycleResource
|
|
167
|
+
scopes: Record<string, LifecycleResource>
|
|
168
|
+
runHooks: RunHooks
|
|
169
|
+
scopeOptions: RestApiPluginOptions
|
|
170
|
+
scopeName: string
|
|
171
|
+
api: LifecycleApi
|
|
172
|
+
log: RuntimeLogger
|
|
173
|
+
}
|
|
174
|
+
export interface EnrichmentParams {
|
|
175
|
+
id?: string | number
|
|
176
|
+
attributes?: StorageRow
|
|
177
|
+
parentContext?: LifecycleContext
|
|
178
|
+
requestedComputedFields?: string[]
|
|
179
|
+
isMainResource?: boolean
|
|
180
|
+
computedDependencies?: string[]
|
|
181
|
+
}
|
|
182
|
+
export interface EnrichmentArguments extends Omit<LifecycleArguments, 'params' | 'context'> {
|
|
183
|
+
params: EnrichmentParams
|
|
184
|
+
context: Record<string, unknown> & { attributes?: StorageRow }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export interface RelationshipPlan {
|
|
188
|
+
belongsToUpdates: StorageRow
|
|
189
|
+
belongsToTargets: Identifier[]
|
|
190
|
+
manyToManyRelationships: Array<{ relName: string; relDef: PivotField; relData: Identifier[] }>
|
|
191
|
+
reverseRelationships: Array<{ relName: string; relDef: Field; relData: RelationshipData }>
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// These are type views of the same object after the named stage establishes its fields.
|
|
195
|
+
export type ResourceContext<Input = InputDocument> = LifecycleContext<Resource | Resource[] | null, Input> & {
|
|
196
|
+
method: string
|
|
197
|
+
scopeName: string
|
|
198
|
+
schemaInfo: LifecycleSchema
|
|
199
|
+
db: StorageDatabase
|
|
200
|
+
}
|
|
201
|
+
export type IdentityContext = ResourceContext & { id: string | number }
|
|
202
|
+
export type RelationshipContext = IdentityContext & { id: string; relationshipName: string }
|
|
203
|
+
export type WriteRelationshipContext = RelationshipContext & { transaction: OwnedTransaction }
|
|
204
|
+
export type ReadContext<Data = Resource | Resource[] | null> = ResourceContext & {
|
|
205
|
+
format: ResourceFormat
|
|
206
|
+
simplified: boolean
|
|
207
|
+
queryParams: QueryParams
|
|
208
|
+
record?: Document<Data> | null
|
|
209
|
+
}
|
|
210
|
+
export type QueryContext = ReadContext<Resource[]> & { queryParams: QueryParams & { sort: string[] } }
|
|
211
|
+
export type ProcessingContext = ResourceContext<unknown> & {
|
|
212
|
+
inputRecord: object
|
|
213
|
+
params: LifecycleParams
|
|
214
|
+
queryParams: QueryParams
|
|
215
|
+
format: ResourceFormat
|
|
216
|
+
simplified: boolean
|
|
217
|
+
returning: WriteReturning
|
|
218
|
+
transaction: OwnedTransaction
|
|
219
|
+
}
|
|
220
|
+
export type WriteContext = ProcessingContext & { inputRecord: InputDocument }
|
|
221
|
+
export type CompletedWriteContext = WriteContext & { id: string | number }
|
|
222
|
+
export type PivotField = Field & { through: string; foreignKey: string; otherKey: string }
|
|
223
|
+
|
|
224
|
+
export type RelationshipArguments = LifecycleArguments & { params: LifecycleParams & { relationshipName: string } }
|
|
225
|
+
export type RelationshipWriteArguments<Data = RelationshipData> = RelationshipArguments & { params: LifecycleParams & { relationshipData: Data } }
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/** @import {
|
|
3
|
+
* RelationshipWriteArguments, WriteRelationshipContext
|
|
4
|
+
* } from './lifecycle-types.js' */
|
|
5
|
+
import { beginWriteTransaction } from '../../../lib/error-context.js'
|
|
6
|
+
import { rejectRemovedOptions, resolveFormat } from '../lib/querying-writing/response-options.js'
|
|
1
7
|
import {
|
|
2
8
|
commitOwnedTransaction,
|
|
3
9
|
handleWriteMethodError,
|
|
@@ -6,30 +12,28 @@ import {
|
|
|
6
12
|
import { requireExistingResourceId } from '../lib/querying-writing/resource-id-normalization.js'
|
|
7
13
|
|
|
8
14
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* PATCH
|
|
12
|
-
*
|
|
13
|
-
* @param {
|
|
14
|
-
* @param {string} relationshipName - The name of the relationship
|
|
15
|
-
* @param {object|array} relationshipData - New relationship data
|
|
16
|
-
* @returns {Promise<void>} 204 No Content
|
|
15
|
+
* Replace the selected relationship with params.relationshipData.
|
|
16
|
+
* Validate relationship permission/cardinality, then delegate the data mutation
|
|
17
|
+
* and version handling to PATCH using the same transaction. This outer method
|
|
18
|
+
* runs its own finish hooks and completes the transaction only when it owns it.
|
|
19
|
+
* @param {RelationshipWriteArguments} args
|
|
17
20
|
*/
|
|
18
|
-
export default async function patchRelationshipMethod ({ params, context, vars, helpers, scope, scopes, runHooks, scopeOptions, scopeName, api, log }) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
export default async function patchRelationshipMethod ({ params, context: callerContext, vars, helpers, scope, scopes, runHooks, scopeOptions, scopeName, api, log }) {
|
|
22
|
+
rejectRemovedOptions(params)
|
|
23
|
+
if (params.format !== undefined) resolveFormat(params.format)
|
|
24
|
+
callerContext.method = 'patchRelationship'
|
|
25
|
+
callerContext.scopeName = scopeName
|
|
26
|
+
callerContext.id = requireExistingResourceId(params.id, {
|
|
21
27
|
scopeOptions,
|
|
22
28
|
vars,
|
|
23
29
|
scopeName
|
|
24
30
|
})
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
callerContext.relationshipName = params.relationshipName
|
|
32
|
+
callerContext.schemaInfo = scope.vars.schemaInfo
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
context.shouldCommit = !params.transaction && !!context.transaction
|
|
32
|
-
context.db = context.transaction || api.knex.instance
|
|
34
|
+
await beginWriteTransaction(callerContext, params.transaction, helpers.newTransaction, runHooks)
|
|
35
|
+
callerContext.db = callerContext.transaction || api.knex.instance
|
|
36
|
+
const context = /** @type {WriteRelationshipContext} */ (callerContext)
|
|
33
37
|
|
|
34
38
|
try {
|
|
35
39
|
// Check permissions
|
|
@@ -47,7 +51,7 @@ export default async function patchRelationshipMethod ({ params, context, vars,
|
|
|
47
51
|
// Reuse existing patch with relationship data
|
|
48
52
|
await scope.patch({
|
|
49
53
|
id: context.id,
|
|
50
|
-
|
|
54
|
+
document: {
|
|
51
55
|
data: {
|
|
52
56
|
type: scopeName,
|
|
53
57
|
id: context.id,
|
|
@@ -57,17 +61,16 @@ export default async function patchRelationshipMethod ({ params, context, vars,
|
|
|
57
61
|
}
|
|
58
62
|
},
|
|
59
63
|
transaction: context.transaction,
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
expectedVersion: params.expectedVersion,
|
|
65
|
+
format: 'jsonapi',
|
|
66
|
+
returning: 'none',
|
|
62
67
|
}, { ...context })
|
|
63
68
|
|
|
64
69
|
await runHooks('finish')
|
|
65
70
|
await runHooks('finishPatchRelationship')
|
|
66
71
|
|
|
67
|
-
await commitOwnedTransaction(context
|
|
68
|
-
|
|
69
|
-
// 204 No Content
|
|
72
|
+
await commitOwnedTransaction(context)
|
|
70
73
|
} catch (error) {
|
|
71
|
-
await handleWriteMethodError(error, context, 'PATCH_RELATIONSHIP', scopeName, log
|
|
74
|
+
await handleWriteMethodError(error, context, 'PATCH_RELATIONSHIP', scopeName, log)
|
|
72
75
|
}
|
|
73
76
|
}
|