json-rest-api 1.0.28 → 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 +746 -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} +90 -31
- 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 -174
- 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 +67 -330
- 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 +205 -30
- package/plugins/core/lib/querying/relationship-identifiers.js +208 -0
- package/plugins/core/lib/querying/sort-helpers.js +20 -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 +238 -187
- 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 +111 -287
- 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 +109 -44
- 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 +142 -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 -2509
- 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 -1069
- 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 -385
- 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 -145
- 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 +38 -69
- 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 +39 -82
- 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 -1238
- 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 -150
- 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 -118
- package/tests/fixtures/api-configs.js +0 -2509
- 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-cursor-multifield.test.js +0 -643
- 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/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'
|
|
3
|
-
import {
|
|
9
|
+
import { filterResponseFields, getResourceFieldset } from '../lib/querying-writing/field-utils.js'
|
|
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,23 +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
|
|
|
105
|
+
await validateRequestedFieldsets(context, scopes)
|
|
106
|
+
validateRequestedIncludes(context, scopes)
|
|
109
107
|
await runHooks('beforeData')
|
|
110
108
|
await runHooks('beforeDataGet')
|
|
111
109
|
|
|
112
110
|
context.record = await helpers.dataGet({
|
|
113
111
|
scopeName,
|
|
114
|
-
context,
|
|
112
|
+
context: identityContext,
|
|
115
113
|
runHooks
|
|
116
114
|
})
|
|
117
115
|
|
|
118
|
-
// Check if record was found - storage layer returns null/undefined for non-existent records.
|
|
119
|
-
// This generates a proper 404 error with JSON:API error format instead of returning empty data.
|
|
120
116
|
if (!context.record || !context.record.data) {
|
|
121
117
|
throw new RestApiResourceError(
|
|
122
118
|
'Resource not found',
|
|
@@ -128,73 +124,46 @@ export default async function getMethod ({
|
|
|
128
124
|
)
|
|
129
125
|
}
|
|
130
126
|
|
|
131
|
-
// Normalize database values (e.g., convert 1/0 to true/false for booleans)
|
|
132
127
|
context.record = normalizeRecordAttributes(context.record, scopes)
|
|
133
128
|
|
|
134
129
|
await runHooks('checkDataPermissions')
|
|
135
130
|
await runHooks('checkDataPermissionsGet')
|
|
136
131
|
|
|
137
|
-
// Make a backup
|
|
138
132
|
context.originalRecord = structuredClone(context.record)
|
|
139
133
|
|
|
140
|
-
// This will enhance record, which is the WHOLE JSON:API record
|
|
141
134
|
await runHooks('enrichRecord')
|
|
142
135
|
|
|
143
|
-
// Get computed field information for main resource
|
|
144
136
|
const computedFields = scope.vars.schemaInfo?.computed || {}
|
|
145
|
-
const requestedFields = context.queryParams.fields
|
|
137
|
+
const requestedFields = getResourceFieldset(context.queryParams.fields, scopeName)
|
|
146
138
|
const requestedComputedFields = getRequestedComputedFields(scopeName, requestedFields, computedFields)
|
|
147
139
|
|
|
148
|
-
// Enrich attributes for the main resource
|
|
149
|
-
// Pass computedDependencies from context (set by dataGet in Knex plugin)
|
|
150
|
-
// This tells enrichAttributes which fields to remove after computation
|
|
151
140
|
context.record.data.attributes = await scope.enrichAttributes({
|
|
141
|
+
id: context.record.data.id,
|
|
152
142
|
attributes: context.record.data.attributes,
|
|
153
143
|
parentContext: context,
|
|
154
144
|
requestedComputedFields,
|
|
155
145
|
isMainResource: true,
|
|
156
|
-
computedDependencies: context.computedDependencies
|
|
146
|
+
computedDependencies: context.computedDependencies
|
|
157
147
|
})
|
|
158
148
|
|
|
159
|
-
|
|
160
|
-
const entryScope = scopes[entry.type]
|
|
161
|
-
const entryComputed = entryScope.vars.schemaInfo?.computed || {}
|
|
162
|
-
const entryRequestedFields = context.queryParams.fields?.[entry.type]
|
|
163
|
-
const entryRequestedComputed = getRequestedComputedFields(
|
|
164
|
-
entry.type,
|
|
165
|
-
entryRequestedFields,
|
|
166
|
-
entryComputed
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
entry.attributes = await entryScope.enrichAttributes({
|
|
170
|
-
attributes: entry.attributes,
|
|
171
|
-
parentContext: context,
|
|
172
|
-
requestedComputedFields: entryRequestedComputed,
|
|
173
|
-
isMainResource: false,
|
|
174
|
-
computedDependencies: entry.__$jsonrestapi_computed_deps$__
|
|
175
|
-
})
|
|
176
|
-
}
|
|
149
|
+
await enrichIncludedAttributes(context, scopes)
|
|
177
150
|
|
|
178
151
|
await runHooks('enrichRecordWithRelationships')
|
|
179
152
|
|
|
180
|
-
// The called hooks should NOT change context.record
|
|
181
153
|
await runHooks('finish')
|
|
182
154
|
await runHooks('finishGet')
|
|
155
|
+
await filterToOneResponseLinkage(context, scopes)
|
|
183
156
|
|
|
184
157
|
// Enrichers and finish hooks run after the database pass and may introduce
|
|
185
158
|
// native values. Reassert the public temporal contract at the return boundary.
|
|
186
159
|
context.record = normalizeRecordAttributes(context.record, scopes, {
|
|
187
160
|
source: 'response'
|
|
188
161
|
})
|
|
162
|
+
filterResponseFields(context.record, context.queryParams.fields)
|
|
189
163
|
|
|
190
|
-
|
|
191
|
-
context.schemaInfo = scopes[scopeName].vars.schemaInfo
|
|
164
|
+
context.schemaInfo = scope.vars.schemaInfo
|
|
192
165
|
|
|
193
|
-
// Transform output if in simplified mode
|
|
194
166
|
if (context.simplified) {
|
|
195
|
-
// Convert JSON:API response back to simplified format
|
|
196
|
-
// Example: {data: {type: 'posts', id: '1', attributes: {title: 'My Post'}, relationships: {author: {data: {type: 'users', id: '123'}}}}}
|
|
197
|
-
// becomes: {id: '1', title: 'My Post', author_id: '123'} - flattens structure and restores foreign keys
|
|
198
167
|
return transformJsonApiToSimplified(
|
|
199
168
|
{ record: context.record },
|
|
200
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
|
}
|