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,681 +0,0 @@
|
|
|
1
|
-
import { describe, it, before, beforeEach, after } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import knexLib from 'knex'
|
|
4
|
-
import { createBasicApi, createExtendedApi } from './fixtures/api-configs.js'
|
|
5
|
-
import {
|
|
6
|
-
validateJsonApiStructure,
|
|
7
|
-
resourceIdentifier,
|
|
8
|
-
cleanTables,
|
|
9
|
-
countRecords,
|
|
10
|
-
createJsonApiDocument,
|
|
11
|
-
createRelationship,
|
|
12
|
-
createToManyRelationship,
|
|
13
|
-
assertResourceAttributes,
|
|
14
|
-
assertResourceRelationship
|
|
15
|
-
} from './helpers/test-utils.js'
|
|
16
|
-
|
|
17
|
-
// Create Knex instance for tests
|
|
18
|
-
const knex = knexLib({
|
|
19
|
-
client: 'better-sqlite3',
|
|
20
|
-
connection: {
|
|
21
|
-
filename: ':memory:'
|
|
22
|
-
},
|
|
23
|
-
useNullAsDefault: true
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
// API instances that persist across tests
|
|
27
|
-
let basicApi
|
|
28
|
-
let extendedApi
|
|
29
|
-
|
|
30
|
-
describe('Relationship Operations', () => {
|
|
31
|
-
before(async () => {
|
|
32
|
-
// Initialize APIs once
|
|
33
|
-
basicApi = await createBasicApi(knex)
|
|
34
|
-
extendedApi = await createExtendedApi(knex)
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
after(async () => {
|
|
38
|
-
// Close database connection to allow tests to exit
|
|
39
|
-
await knex.destroy()
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
describe('One-to-One (belongsTo) Relationships', () => {
|
|
43
|
-
beforeEach(async () => {
|
|
44
|
-
// Clean all tables before each test
|
|
45
|
-
await cleanTables(knex, [
|
|
46
|
-
'basic_countries', 'basic_publishers', 'basic_authors', 'basic_books', 'basic_book_authors'
|
|
47
|
-
])
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('should create resource with belongsTo relationship', async () => {
|
|
51
|
-
// Create a country first
|
|
52
|
-
const countryDoc = createJsonApiDocument('countries', {
|
|
53
|
-
name: 'United States',
|
|
54
|
-
code: 'US'
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const countryResult = await basicApi.resources.countries.post({
|
|
58
|
-
inputRecord: countryDoc,
|
|
59
|
-
simplified: false
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
// Create publisher with country relationship
|
|
63
|
-
const publisherDoc = createJsonApiDocument('publishers',
|
|
64
|
-
{
|
|
65
|
-
name: 'Random House'
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
country: createRelationship(resourceIdentifier('countries', countryResult.data.id))
|
|
69
|
-
}
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
const publisherResult = await basicApi.resources.publishers.post({
|
|
73
|
-
inputRecord: publisherDoc,
|
|
74
|
-
simplified: false
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
// Verify response
|
|
78
|
-
validateJsonApiStructure(publisherResult, false)
|
|
79
|
-
assert.equal(publisherResult.data.type, 'publishers')
|
|
80
|
-
assertResourceRelationship(publisherResult.data, 'country',
|
|
81
|
-
resourceIdentifier('countries', countryResult.data.id))
|
|
82
|
-
|
|
83
|
-
// Verify through GET with include
|
|
84
|
-
const getResult = await basicApi.resources.publishers.get({
|
|
85
|
-
id: publisherResult.data.id,
|
|
86
|
-
queryParams: {
|
|
87
|
-
include: ['country']
|
|
88
|
-
},
|
|
89
|
-
simplified: false
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
validateJsonApiStructure(getResult, false)
|
|
93
|
-
assert(getResult.included, 'Should have included data')
|
|
94
|
-
assert.equal(getResult.included.length, 1)
|
|
95
|
-
assert.equal(getResult.included[0].type, 'countries')
|
|
96
|
-
assert.equal(getResult.included[0].attributes.name, 'United States')
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('should update belongsTo relationship via PATCH', async () => {
|
|
100
|
-
// Create two countries
|
|
101
|
-
const usDoc = createJsonApiDocument('countries', { name: 'United States', code: 'US' })
|
|
102
|
-
const ukDoc = createJsonApiDocument('countries', { name: 'United Kingdom', code: 'UK' })
|
|
103
|
-
|
|
104
|
-
const usResult = await basicApi.resources.countries.post({
|
|
105
|
-
inputRecord: usDoc,
|
|
106
|
-
simplified: false
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
const ukResult = await basicApi.resources.countries.post({
|
|
110
|
-
inputRecord: ukDoc,
|
|
111
|
-
simplified: false
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
// Create publisher with US country
|
|
115
|
-
const publisherDoc = createJsonApiDocument('publishers',
|
|
116
|
-
{ name: 'Test Publisher' },
|
|
117
|
-
{ country: createRelationship(resourceIdentifier('countries', usResult.data.id)) }
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
const publisherResult = await basicApi.resources.publishers.post({
|
|
121
|
-
inputRecord: publisherDoc,
|
|
122
|
-
simplified: false
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
// Update publisher to UK country
|
|
126
|
-
const patchDoc = {
|
|
127
|
-
data: {
|
|
128
|
-
type: 'publishers',
|
|
129
|
-
id: String(publisherResult.data.id),
|
|
130
|
-
relationships: {
|
|
131
|
-
country: createRelationship(resourceIdentifier('countries', ukResult.data.id))
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
await basicApi.resources.publishers.patch({
|
|
137
|
-
id: publisherResult.data.id,
|
|
138
|
-
inputRecord: patchDoc,
|
|
139
|
-
simplified: false
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
// Verify update
|
|
143
|
-
const getResult = await basicApi.resources.publishers.get({
|
|
144
|
-
id: publisherResult.data.id,
|
|
145
|
-
queryParams: { include: ['country'] },
|
|
146
|
-
simplified: false
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
assertResourceRelationship(getResult.data, 'country',
|
|
150
|
-
resourceIdentifier('countries', ukResult.data.id))
|
|
151
|
-
assert.equal(getResult.included[0].attributes.code, 'UK')
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
it('should clear belongsTo relationship with null', async () => {
|
|
155
|
-
// Create country and publisher
|
|
156
|
-
const countryDoc = createJsonApiDocument('countries', { name: 'France', code: 'FR' })
|
|
157
|
-
const countryResult = await basicApi.resources.countries.post({
|
|
158
|
-
inputRecord: countryDoc,
|
|
159
|
-
simplified: false
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
const publisherDoc = createJsonApiDocument('publishers',
|
|
163
|
-
{ name: 'French Publisher' },
|
|
164
|
-
{ country: createRelationship(resourceIdentifier('countries', countryResult.data.id)) }
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
const publisherResult = await basicApi.resources.publishers.post({
|
|
168
|
-
inputRecord: publisherDoc,
|
|
169
|
-
simplified: false
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
// Clear the relationship
|
|
173
|
-
const patchDoc = {
|
|
174
|
-
data: {
|
|
175
|
-
type: 'publishers',
|
|
176
|
-
id: String(publisherResult.data.id),
|
|
177
|
-
relationships: {
|
|
178
|
-
country: { data: null }
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
await basicApi.resources.publishers.patch({
|
|
184
|
-
id: publisherResult.data.id,
|
|
185
|
-
inputRecord: patchDoc,
|
|
186
|
-
simplified: false
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
// Verify relationship cleared
|
|
190
|
-
const getResult = await basicApi.resources.publishers.get({
|
|
191
|
-
id: publisherResult.data.id,
|
|
192
|
-
simplified: false
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
assert(getResult.data.relationships)
|
|
196
|
-
assert.equal(getResult.data.relationships.country.data, null)
|
|
197
|
-
})
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
describe('Many-to-Many Relationships', () => {
|
|
201
|
-
beforeEach(async () => {
|
|
202
|
-
await cleanTables(knex, [
|
|
203
|
-
'basic_countries', 'basic_publishers', 'basic_authors', 'basic_books', 'basic_book_authors'
|
|
204
|
-
])
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
it('should create book with multiple authors (many-to-many)', async () => {
|
|
208
|
-
// Create country for book
|
|
209
|
-
const countryDoc = createJsonApiDocument('countries', { name: 'USA', code: 'US' })
|
|
210
|
-
const countryResult = await basicApi.resources.countries.post({
|
|
211
|
-
inputRecord: countryDoc,
|
|
212
|
-
simplified: false
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
// Create authors
|
|
216
|
-
const author1Doc = createJsonApiDocument('authors', { name: 'Stephen King' })
|
|
217
|
-
const author2Doc = createJsonApiDocument('authors', { name: 'Peter Straub' })
|
|
218
|
-
|
|
219
|
-
const author1Result = await basicApi.resources.authors.post({
|
|
220
|
-
inputRecord: author1Doc,
|
|
221
|
-
simplified: false
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
const author2Result = await basicApi.resources.authors.post({
|
|
225
|
-
inputRecord: author2Doc,
|
|
226
|
-
simplified: false
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
// Create book with authors
|
|
230
|
-
const bookDoc = createJsonApiDocument('books',
|
|
231
|
-
{
|
|
232
|
-
title: 'The Talisman'
|
|
233
|
-
},
|
|
234
|
-
{
|
|
235
|
-
country: createRelationship(resourceIdentifier('countries', countryResult.data.id)),
|
|
236
|
-
authors: createToManyRelationship([
|
|
237
|
-
resourceIdentifier('authors', author1Result.data.id),
|
|
238
|
-
resourceIdentifier('authors', author2Result.data.id)
|
|
239
|
-
])
|
|
240
|
-
}
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
const bookResult = await basicApi.resources.books.post({
|
|
244
|
-
inputRecord: bookDoc,
|
|
245
|
-
simplified: false
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
// Verify response
|
|
249
|
-
validateJsonApiStructure(bookResult, false)
|
|
250
|
-
assert.equal(bookResult.data.type, 'books')
|
|
251
|
-
|
|
252
|
-
// Verify pivot table records created
|
|
253
|
-
const pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
254
|
-
assert.equal(pivotCount, 2, 'Should have created 2 pivot records')
|
|
255
|
-
|
|
256
|
-
// GET book with authors included
|
|
257
|
-
const getResult = await basicApi.resources.books.get({
|
|
258
|
-
id: bookResult.data.id,
|
|
259
|
-
queryParams: { include: ['authors'] },
|
|
260
|
-
simplified: false
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
validateJsonApiStructure(getResult, false)
|
|
264
|
-
assert(getResult.data.relationships.authors)
|
|
265
|
-
assert.equal(getResult.data.relationships.authors.data.length, 2)
|
|
266
|
-
|
|
267
|
-
// Verify included authors
|
|
268
|
-
assert(getResult.included)
|
|
269
|
-
assert.equal(getResult.included.length, 2)
|
|
270
|
-
const authorNames = getResult.included.map(a => a.attributes.name).sort()
|
|
271
|
-
assert.deepEqual(authorNames, ['Peter Straub', 'Stephen King'])
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
it('should update many-to-many relationships via PUT (complete replacement)', async () => {
|
|
275
|
-
// Create country
|
|
276
|
-
const countryDoc = createJsonApiDocument('countries', { name: 'USA', code: 'US' })
|
|
277
|
-
const countryResult = await basicApi.resources.countries.post({
|
|
278
|
-
inputRecord: countryDoc,
|
|
279
|
-
simplified: false
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
// Create 4 authors
|
|
283
|
-
const authors = []
|
|
284
|
-
for (const name of ['Author 1', 'Author 2', 'Author 3', 'Author 4']) {
|
|
285
|
-
const doc = createJsonApiDocument('authors', { name })
|
|
286
|
-
const result = await basicApi.resources.authors.post({
|
|
287
|
-
inputRecord: doc,
|
|
288
|
-
simplified: false
|
|
289
|
-
})
|
|
290
|
-
authors.push(result.data)
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Create book with first 2 authors
|
|
294
|
-
const bookDoc = createJsonApiDocument('books',
|
|
295
|
-
{
|
|
296
|
-
title: 'Test Book'
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
country: createRelationship(resourceIdentifier('countries', countryResult.data.id)),
|
|
300
|
-
authors: createToManyRelationship([
|
|
301
|
-
resourceIdentifier('authors', authors[0].id),
|
|
302
|
-
resourceIdentifier('authors', authors[1].id)
|
|
303
|
-
])
|
|
304
|
-
}
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
const bookResult = await basicApi.resources.books.post({
|
|
308
|
-
inputRecord: bookDoc,
|
|
309
|
-
simplified: false
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
// Verify initial state
|
|
313
|
-
let pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
314
|
-
assert.equal(pivotCount, 2)
|
|
315
|
-
|
|
316
|
-
// PUT to replace with different 2 authors
|
|
317
|
-
const putDoc = {
|
|
318
|
-
data: {
|
|
319
|
-
type: 'books',
|
|
320
|
-
id: String(bookResult.data.id),
|
|
321
|
-
attributes: {
|
|
322
|
-
title: 'Test Book'
|
|
323
|
-
},
|
|
324
|
-
relationships: {
|
|
325
|
-
country: createRelationship(resourceIdentifier('countries', countryResult.data.id)),
|
|
326
|
-
authors: createToManyRelationship([
|
|
327
|
-
resourceIdentifier('authors', authors[2].id),
|
|
328
|
-
resourceIdentifier('authors', authors[3].id)
|
|
329
|
-
])
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
await basicApi.resources.books.put({
|
|
334
|
-
id: bookResult.data.id,
|
|
335
|
-
inputRecord: putDoc,
|
|
336
|
-
simplified: false,
|
|
337
|
-
returnFullRecord: false
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
// Verify old pivot records removed, new ones created
|
|
341
|
-
pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
342
|
-
assert.equal(pivotCount, 2)
|
|
343
|
-
|
|
344
|
-
// GET to verify new relationships
|
|
345
|
-
const getResult = await basicApi.resources.books.get({
|
|
346
|
-
id: bookResult.data.id,
|
|
347
|
-
queryParams: { include: ['authors'] },
|
|
348
|
-
simplified: false
|
|
349
|
-
})
|
|
350
|
-
|
|
351
|
-
const authorIds = getResult.data.relationships.authors.data.map(a => a.id).sort()
|
|
352
|
-
assert.deepEqual(authorIds, [authors[2].id, authors[3].id].sort())
|
|
353
|
-
})
|
|
354
|
-
|
|
355
|
-
it('should update many-to-many relationships via PATCH (partial update)', async () => {
|
|
356
|
-
// Create country
|
|
357
|
-
const countryDoc = createJsonApiDocument('countries', { name: 'USA', code: 'US' })
|
|
358
|
-
const countryResult = await basicApi.resources.countries.post({
|
|
359
|
-
inputRecord: countryDoc,
|
|
360
|
-
simplified: false
|
|
361
|
-
})
|
|
362
|
-
|
|
363
|
-
// Create 4 authors
|
|
364
|
-
const authors = []
|
|
365
|
-
for (const name of ['Author A', 'Author B', 'Author C', 'Author D']) {
|
|
366
|
-
const doc = createJsonApiDocument('authors', { name })
|
|
367
|
-
const result = await basicApi.resources.authors.post({
|
|
368
|
-
inputRecord: doc,
|
|
369
|
-
simplified: false
|
|
370
|
-
})
|
|
371
|
-
authors.push(result.data)
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// Create book with first 3 authors
|
|
375
|
-
const bookDoc = createJsonApiDocument('books',
|
|
376
|
-
{
|
|
377
|
-
title: 'Multi-Author Book'
|
|
378
|
-
},
|
|
379
|
-
{
|
|
380
|
-
country: createRelationship(resourceIdentifier('countries', countryResult.data.id)),
|
|
381
|
-
authors: createToManyRelationship([
|
|
382
|
-
resourceIdentifier('authors', authors[0].id),
|
|
383
|
-
resourceIdentifier('authors', authors[1].id),
|
|
384
|
-
resourceIdentifier('authors', authors[2].id)
|
|
385
|
-
])
|
|
386
|
-
}
|
|
387
|
-
)
|
|
388
|
-
|
|
389
|
-
const bookResult = await basicApi.resources.books.post({
|
|
390
|
-
inputRecord: bookDoc,
|
|
391
|
-
simplified: false
|
|
392
|
-
})
|
|
393
|
-
|
|
394
|
-
// PATCH to change to [B, C, D] - should remove A, keep B&C, add D
|
|
395
|
-
const patchDoc = {
|
|
396
|
-
data: {
|
|
397
|
-
type: 'books',
|
|
398
|
-
id: String(bookResult.data.id),
|
|
399
|
-
relationships: {
|
|
400
|
-
authors: createToManyRelationship([
|
|
401
|
-
resourceIdentifier('authors', authors[1].id),
|
|
402
|
-
resourceIdentifier('authors', authors[2].id),
|
|
403
|
-
resourceIdentifier('authors', authors[3].id)
|
|
404
|
-
])
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
await basicApi.resources.books.patch({
|
|
410
|
-
id: bookResult.data.id,
|
|
411
|
-
inputRecord: patchDoc,
|
|
412
|
-
simplified: false
|
|
413
|
-
})
|
|
414
|
-
|
|
415
|
-
// Verify the update
|
|
416
|
-
const getResult = await basicApi.resources.books.get({
|
|
417
|
-
id: bookResult.data.id,
|
|
418
|
-
queryParams: { include: ['authors'] },
|
|
419
|
-
simplified: false
|
|
420
|
-
})
|
|
421
|
-
|
|
422
|
-
const authorIds = getResult.data.relationships.authors.data.map(a => a.id).sort()
|
|
423
|
-
const expectedIds = [authors[1].id, authors[2].id, authors[3].id].sort()
|
|
424
|
-
assert.deepEqual(authorIds, expectedIds)
|
|
425
|
-
|
|
426
|
-
// Verify correct number of pivot records
|
|
427
|
-
const pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
428
|
-
assert.equal(pivotCount, 3)
|
|
429
|
-
})
|
|
430
|
-
|
|
431
|
-
it('should clear all many-to-many relationships with empty array', async () => {
|
|
432
|
-
// Create country
|
|
433
|
-
const countryDoc = createJsonApiDocument('countries', { name: 'USA', code: 'US' })
|
|
434
|
-
const countryResult = await basicApi.resources.countries.post({
|
|
435
|
-
inputRecord: countryDoc,
|
|
436
|
-
simplified: false
|
|
437
|
-
})
|
|
438
|
-
|
|
439
|
-
// Create authors
|
|
440
|
-
const author1Doc = createJsonApiDocument('authors', { name: 'Author One' })
|
|
441
|
-
const author2Doc = createJsonApiDocument('authors', { name: 'Author Two' })
|
|
442
|
-
|
|
443
|
-
const author1Result = await basicApi.resources.authors.post({
|
|
444
|
-
inputRecord: author1Doc,
|
|
445
|
-
simplified: false
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
const author2Result = await basicApi.resources.authors.post({
|
|
449
|
-
inputRecord: author2Doc,
|
|
450
|
-
simplified: false
|
|
451
|
-
})
|
|
452
|
-
|
|
453
|
-
// Create book with authors
|
|
454
|
-
const bookDoc = createJsonApiDocument('books',
|
|
455
|
-
{
|
|
456
|
-
title: 'Book to Clear'
|
|
457
|
-
},
|
|
458
|
-
{
|
|
459
|
-
country: createRelationship(resourceIdentifier('countries', countryResult.data.id)),
|
|
460
|
-
authors: createToManyRelationship([
|
|
461
|
-
resourceIdentifier('authors', author1Result.data.id),
|
|
462
|
-
resourceIdentifier('authors', author2Result.data.id)
|
|
463
|
-
])
|
|
464
|
-
}
|
|
465
|
-
)
|
|
466
|
-
|
|
467
|
-
const bookResult = await basicApi.resources.books.post({
|
|
468
|
-
inputRecord: bookDoc,
|
|
469
|
-
simplified: false
|
|
470
|
-
})
|
|
471
|
-
|
|
472
|
-
// Verify initial state
|
|
473
|
-
let pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
474
|
-
assert.equal(pivotCount, 2)
|
|
475
|
-
|
|
476
|
-
// Clear all relationships
|
|
477
|
-
const patchDoc = {
|
|
478
|
-
data: {
|
|
479
|
-
type: 'books',
|
|
480
|
-
id: String(bookResult.data.id),
|
|
481
|
-
relationships: {
|
|
482
|
-
authors: { data: [] }
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
await basicApi.resources.books.patch({
|
|
488
|
-
id: bookResult.data.id,
|
|
489
|
-
inputRecord: patchDoc,
|
|
490
|
-
simplified: false
|
|
491
|
-
})
|
|
492
|
-
|
|
493
|
-
// Verify all pivot records removed
|
|
494
|
-
pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
495
|
-
assert.equal(pivotCount, 0)
|
|
496
|
-
|
|
497
|
-
// Verify through GET
|
|
498
|
-
const getResult = await basicApi.resources.books.get({
|
|
499
|
-
id: bookResult.data.id,
|
|
500
|
-
queryParams: { include: ['authors'] },
|
|
501
|
-
simplified: false
|
|
502
|
-
})
|
|
503
|
-
|
|
504
|
-
assert(getResult.data.relationships.authors)
|
|
505
|
-
assert.equal(getResult.data.relationships.authors.data.length, 0)
|
|
506
|
-
})
|
|
507
|
-
})
|
|
508
|
-
|
|
509
|
-
describe('Complex Relationship Scenarios', () => {
|
|
510
|
-
beforeEach(async () => {
|
|
511
|
-
await cleanTables(knex, [
|
|
512
|
-
'basic_countries', 'basic_publishers', 'basic_authors', 'basic_books', 'basic_book_authors'
|
|
513
|
-
])
|
|
514
|
-
})
|
|
515
|
-
|
|
516
|
-
it('should create resource with multiple relationship types', async () => {
|
|
517
|
-
// Create supporting resources
|
|
518
|
-
const countryDoc = createJsonApiDocument('countries', { name: 'USA', code: 'US' })
|
|
519
|
-
const countryResult = await basicApi.resources.countries.post({
|
|
520
|
-
inputRecord: countryDoc,
|
|
521
|
-
simplified: false
|
|
522
|
-
})
|
|
523
|
-
|
|
524
|
-
const publisherDoc = createJsonApiDocument('publishers',
|
|
525
|
-
{ name: 'Big Publisher' },
|
|
526
|
-
{ country: createRelationship(resourceIdentifier('countries', countryResult.data.id)) }
|
|
527
|
-
)
|
|
528
|
-
const publisherResult = await basicApi.resources.publishers.post({
|
|
529
|
-
inputRecord: publisherDoc,
|
|
530
|
-
simplified: false
|
|
531
|
-
})
|
|
532
|
-
|
|
533
|
-
const author1Doc = createJsonApiDocument('authors', { name: 'Main Author' })
|
|
534
|
-
const author2Doc = createJsonApiDocument('authors', { name: 'Co-Author' })
|
|
535
|
-
const author1Result = await basicApi.resources.authors.post({
|
|
536
|
-
inputRecord: author1Doc,
|
|
537
|
-
simplified: false
|
|
538
|
-
})
|
|
539
|
-
const author2Result = await basicApi.resources.authors.post({
|
|
540
|
-
inputRecord: author2Doc,
|
|
541
|
-
simplified: false
|
|
542
|
-
})
|
|
543
|
-
|
|
544
|
-
// Create book with all relationships
|
|
545
|
-
const bookDoc = createJsonApiDocument('books',
|
|
546
|
-
{
|
|
547
|
-
title: 'Complex Book'
|
|
548
|
-
},
|
|
549
|
-
{
|
|
550
|
-
country: createRelationship(resourceIdentifier('countries', countryResult.data.id)),
|
|
551
|
-
publisher: createRelationship(resourceIdentifier('publishers', publisherResult.data.id)),
|
|
552
|
-
authors: createToManyRelationship([
|
|
553
|
-
resourceIdentifier('authors', author1Result.data.id),
|
|
554
|
-
resourceIdentifier('authors', author2Result.data.id)
|
|
555
|
-
])
|
|
556
|
-
}
|
|
557
|
-
)
|
|
558
|
-
|
|
559
|
-
const bookResult = await basicApi.resources.books.post({
|
|
560
|
-
inputRecord: bookDoc,
|
|
561
|
-
simplified: false
|
|
562
|
-
})
|
|
563
|
-
|
|
564
|
-
// Verify with full includes
|
|
565
|
-
const getResult = await basicApi.resources.books.get({
|
|
566
|
-
id: bookResult.data.id,
|
|
567
|
-
queryParams: {
|
|
568
|
-
include: ['publisher', 'publisher.country', 'authors', 'country']
|
|
569
|
-
},
|
|
570
|
-
simplified: false
|
|
571
|
-
})
|
|
572
|
-
|
|
573
|
-
validateJsonApiStructure(getResult, false)
|
|
574
|
-
|
|
575
|
-
// Verify all relationships
|
|
576
|
-
assert(getResult.data.relationships.publisher)
|
|
577
|
-
assert(getResult.data.relationships.authors)
|
|
578
|
-
assert(getResult.data.relationships.country)
|
|
579
|
-
|
|
580
|
-
// Verify included resources
|
|
581
|
-
assert(getResult.included)
|
|
582
|
-
const includedTypes = getResult.included.map(r => r.type)
|
|
583
|
-
assert(includedTypes.includes('publishers'))
|
|
584
|
-
assert(includedTypes.includes('authors'))
|
|
585
|
-
assert(includedTypes.includes('countries'))
|
|
586
|
-
|
|
587
|
-
// Verify nested relationship (publisher.country)
|
|
588
|
-
const publisher = getResult.included.find(r => r.type === 'publishers')
|
|
589
|
-
assert(publisher.relationships.country)
|
|
590
|
-
})
|
|
591
|
-
|
|
592
|
-
it('should clear omitted relationships when PUT provides a relationships object', async () => {
|
|
593
|
-
// Create resources
|
|
594
|
-
const countryDoc = createJsonApiDocument('countries', { name: 'USA', code: 'US' })
|
|
595
|
-
const countryResult = await basicApi.resources.countries.post({
|
|
596
|
-
inputRecord: countryDoc,
|
|
597
|
-
simplified: false
|
|
598
|
-
})
|
|
599
|
-
|
|
600
|
-
const publisherDoc = createJsonApiDocument('publishers',
|
|
601
|
-
{ name: 'Original Publisher' },
|
|
602
|
-
{ country: createRelationship(resourceIdentifier('countries', countryResult.data.id)) }
|
|
603
|
-
)
|
|
604
|
-
const publisherResult = await basicApi.resources.publishers.post({
|
|
605
|
-
inputRecord: publisherDoc,
|
|
606
|
-
simplified: false
|
|
607
|
-
})
|
|
608
|
-
|
|
609
|
-
const authorDoc = createJsonApiDocument('authors', { name: 'Original Author' })
|
|
610
|
-
const authorResult = await basicApi.resources.authors.post({
|
|
611
|
-
inputRecord: authorDoc,
|
|
612
|
-
simplified: false
|
|
613
|
-
})
|
|
614
|
-
|
|
615
|
-
// Create book with all relationships
|
|
616
|
-
const bookDoc = createJsonApiDocument('books',
|
|
617
|
-
{
|
|
618
|
-
title: 'Book to Update'
|
|
619
|
-
},
|
|
620
|
-
{
|
|
621
|
-
country: createRelationship(resourceIdentifier('countries', countryResult.data.id)),
|
|
622
|
-
publisher: createRelationship(resourceIdentifier('publishers', publisherResult.data.id)),
|
|
623
|
-
authors: createToManyRelationship([resourceIdentifier('authors', authorResult.data.id)])
|
|
624
|
-
}
|
|
625
|
-
)
|
|
626
|
-
|
|
627
|
-
const bookResult = await basicApi.resources.books.post({
|
|
628
|
-
inputRecord: bookDoc,
|
|
629
|
-
simplified: false
|
|
630
|
-
})
|
|
631
|
-
|
|
632
|
-
// Create new publisher for update
|
|
633
|
-
const newPublisherDoc = createJsonApiDocument('publishers',
|
|
634
|
-
{ name: 'New Publisher' },
|
|
635
|
-
{ country: createRelationship(resourceIdentifier('countries', countryResult.data.id)) }
|
|
636
|
-
)
|
|
637
|
-
const newPublisherResult = await basicApi.resources.publishers.post({
|
|
638
|
-
inputRecord: newPublisherDoc,
|
|
639
|
-
simplified: false
|
|
640
|
-
})
|
|
641
|
-
|
|
642
|
-
// PUT with only publisher relationship. Omitted relationships are cleared
|
|
643
|
-
// once a relationships object is provided for a complete replacement.
|
|
644
|
-
const putDoc = {
|
|
645
|
-
data: {
|
|
646
|
-
type: 'books',
|
|
647
|
-
id: String(bookResult.data.id),
|
|
648
|
-
attributes: {
|
|
649
|
-
title: 'Book to Update'
|
|
650
|
-
},
|
|
651
|
-
relationships: {
|
|
652
|
-
country: createRelationship(resourceIdentifier('countries', countryResult.data.id)),
|
|
653
|
-
publisher: createRelationship(resourceIdentifier('publishers', newPublisherResult.data.id))
|
|
654
|
-
// Note: authors not mentioned, but relationships object is present
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
await basicApi.resources.books.put({
|
|
660
|
-
id: bookResult.data.id,
|
|
661
|
-
inputRecord: putDoc,
|
|
662
|
-
simplified: false,
|
|
663
|
-
returnFullRecord: false
|
|
664
|
-
})
|
|
665
|
-
|
|
666
|
-
// Verify update
|
|
667
|
-
const getResult = await basicApi.resources.books.get({
|
|
668
|
-
id: bookResult.data.id,
|
|
669
|
-
queryParams: { include: ['publisher', 'authors'] },
|
|
670
|
-
simplified: false
|
|
671
|
-
})
|
|
672
|
-
|
|
673
|
-
// Publisher should be updated
|
|
674
|
-
assert.equal(getResult.data.relationships.publisher.data.id, newPublisherResult.data.id)
|
|
675
|
-
|
|
676
|
-
// Authors should be cleared (PUT with relationships object clears unmentioned relationships)
|
|
677
|
-
assert(getResult.data.relationships.authors)
|
|
678
|
-
assert.equal(getResult.data.relationships.authors.data.length, 0)
|
|
679
|
-
})
|
|
680
|
-
})
|
|
681
|
-
})
|