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
package/tests/rest-api.test.js
DELETED
|
@@ -1,337 +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('REST API Tests', () => {
|
|
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('Basic CRUD Operations', () => {
|
|
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
|
-
describe('POST - Create Resources', () => {
|
|
51
|
-
it('should create a country', async () => {
|
|
52
|
-
const countryDoc = createJsonApiDocument('countries', {
|
|
53
|
-
name: 'United States',
|
|
54
|
-
code: 'US'
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const result = await basicApi.resources.countries.post({
|
|
58
|
-
inputRecord: countryDoc,
|
|
59
|
-
simplified: false
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
// Verify response structure
|
|
63
|
-
validateJsonApiStructure(result, false)
|
|
64
|
-
assert.equal(result.data.type, 'countries')
|
|
65
|
-
assert(result.data.id, 'Should have an ID')
|
|
66
|
-
assertResourceAttributes(result.data, {
|
|
67
|
-
name: 'United States',
|
|
68
|
-
code: 'US'
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
// Verify record was created in database
|
|
72
|
-
const count = await countRecords(knex, 'basic_countries')
|
|
73
|
-
assert.equal(count, 1, 'Should have created one country')
|
|
74
|
-
|
|
75
|
-
// Verify data through API GET
|
|
76
|
-
const getResult = await basicApi.resources.countries.get({
|
|
77
|
-
id: result.data.id,
|
|
78
|
-
simplified: false
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
validateJsonApiStructure(getResult, false)
|
|
82
|
-
assert.equal(getResult.data.type, 'countries')
|
|
83
|
-
assert.equal(getResult.data.id, result.data.id)
|
|
84
|
-
assertResourceAttributes(getResult.data, {
|
|
85
|
-
name: 'United States',
|
|
86
|
-
code: 'US'
|
|
87
|
-
})
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('should create a publisher with country relationship', async () => {
|
|
91
|
-
// First create a country
|
|
92
|
-
const countryDoc = createJsonApiDocument('countries', {
|
|
93
|
-
name: 'United Kingdom',
|
|
94
|
-
code: 'UK'
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
const countryResult = await basicApi.resources.countries.post({
|
|
98
|
-
inputRecord: countryDoc,
|
|
99
|
-
simplified: false
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
// Create publisher with country relationship
|
|
103
|
-
const publisherDoc = createJsonApiDocument('publishers',
|
|
104
|
-
{
|
|
105
|
-
name: 'Penguin Random House'
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
country: createRelationship(resourceIdentifier('countries', countryResult.data.id))
|
|
109
|
-
}
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
const publisherResult = await basicApi.resources.publishers.post({
|
|
113
|
-
inputRecord: publisherDoc,
|
|
114
|
-
simplified: false
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
// Verify response
|
|
118
|
-
validateJsonApiStructure(publisherResult, false)
|
|
119
|
-
assert.equal(publisherResult.data.type, 'publishers')
|
|
120
|
-
assert(publisherResult.data.id, 'Should have an ID')
|
|
121
|
-
|
|
122
|
-
// Verify records in database
|
|
123
|
-
const publisherCount = await countRecords(knex, 'basic_publishers')
|
|
124
|
-
assert.equal(publisherCount, 1, 'Should have created one publisher')
|
|
125
|
-
|
|
126
|
-
// Verify data and relationship through API
|
|
127
|
-
const getResult = await basicApi.resources.publishers.get({
|
|
128
|
-
id: publisherResult.data.id,
|
|
129
|
-
queryParams: {
|
|
130
|
-
include: ['country']
|
|
131
|
-
},
|
|
132
|
-
simplified: false
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
validateJsonApiStructure(getResult, false)
|
|
136
|
-
assertResourceAttributes(getResult.data, {
|
|
137
|
-
name: 'Penguin Random House'
|
|
138
|
-
})
|
|
139
|
-
assertResourceRelationship(getResult.data, 'country',
|
|
140
|
-
resourceIdentifier('countries', countryResult.data.id))
|
|
141
|
-
|
|
142
|
-
// Verify included data
|
|
143
|
-
assert(getResult.included, 'Should have included data')
|
|
144
|
-
assert.equal(getResult.included.length, 1, 'Should include one resource')
|
|
145
|
-
assert.equal(getResult.included[0].type, 'countries')
|
|
146
|
-
assert.equal(getResult.included[0].attributes.name, 'United Kingdom')
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it('should fail to create resource with missing required field', async () => {
|
|
150
|
-
const invalidDoc = createJsonApiDocument('countries', {
|
|
151
|
-
code: 'FR'
|
|
152
|
-
// Missing required 'name' field
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
await assert.rejects(
|
|
156
|
-
async () => {
|
|
157
|
-
await basicApi.resources.countries.post({
|
|
158
|
-
inputRecord: invalidDoc,
|
|
159
|
-
simplified: false,
|
|
160
|
-
returnFullRecord: false
|
|
161
|
-
})
|
|
162
|
-
},
|
|
163
|
-
(err) => {
|
|
164
|
-
return err.code === 'REST_API_VALIDATION'
|
|
165
|
-
},
|
|
166
|
-
'Should throw validation error for missing required field'
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
// Verify no record was created
|
|
170
|
-
const count = await countRecords(knex, 'basic_countries')
|
|
171
|
-
assert.equal(count, 0, 'Should not have created any countries')
|
|
172
|
-
})
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
describe('GET - Retrieve Resources', () => {
|
|
176
|
-
it('should retrieve a single resource', async () => {
|
|
177
|
-
// Create test data
|
|
178
|
-
const countryDoc = createJsonApiDocument('countries', {
|
|
179
|
-
name: 'Canada',
|
|
180
|
-
code: 'CA'
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
const createResult = await basicApi.resources.countries.post({
|
|
184
|
-
inputRecord: countryDoc,
|
|
185
|
-
simplified: false
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
// Get the resource
|
|
189
|
-
const getResult = await basicApi.resources.countries.get({
|
|
190
|
-
id: createResult.data.id,
|
|
191
|
-
simplified: false
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
validateJsonApiStructure(getResult, false)
|
|
195
|
-
assert.equal(getResult.data.type, 'countries')
|
|
196
|
-
assert.equal(getResult.data.id, createResult.data.id)
|
|
197
|
-
assertResourceAttributes(getResult.data, {
|
|
198
|
-
name: 'Canada',
|
|
199
|
-
code: 'CA'
|
|
200
|
-
})
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
it('should fail to retrieve non-existent resource', async () => {
|
|
204
|
-
await assert.rejects(
|
|
205
|
-
async () => {
|
|
206
|
-
await basicApi.resources.countries.get({
|
|
207
|
-
id: 99999,
|
|
208
|
-
simplified: false
|
|
209
|
-
})
|
|
210
|
-
},
|
|
211
|
-
(err) => {
|
|
212
|
-
return err.code === 'REST_API_RESOURCE' && err.subtype === 'not_found'
|
|
213
|
-
},
|
|
214
|
-
'Should throw not found error'
|
|
215
|
-
)
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
it('should retrieve collection of resources', async () => {
|
|
219
|
-
// Create multiple countries
|
|
220
|
-
const countries = [
|
|
221
|
-
{ name: 'France', code: 'FR' },
|
|
222
|
-
{ name: 'Germany', code: 'DE' },
|
|
223
|
-
{ name: 'Italy', code: 'IT' }
|
|
224
|
-
]
|
|
225
|
-
|
|
226
|
-
for (const country of countries) {
|
|
227
|
-
const doc = createJsonApiDocument('countries', country)
|
|
228
|
-
await basicApi.resources.countries.post({
|
|
229
|
-
inputRecord: doc,
|
|
230
|
-
simplified: false
|
|
231
|
-
})
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Query all countries
|
|
235
|
-
const queryResult = await basicApi.resources.countries.query({
|
|
236
|
-
simplified: false
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
validateJsonApiStructure(queryResult, true)
|
|
240
|
-
assert.equal(queryResult.data.length, 3, 'Should return 3 countries')
|
|
241
|
-
|
|
242
|
-
// Verify all countries are present
|
|
243
|
-
const names = queryResult.data.map(c => c.attributes.name).sort()
|
|
244
|
-
assert.deepEqual(names, ['France', 'Germany', 'Italy'])
|
|
245
|
-
})
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
describe('PATCH - Update Resources', () => {
|
|
249
|
-
it('should partially update a resource', async () => {
|
|
250
|
-
// Create a country
|
|
251
|
-
const countryDoc = createJsonApiDocument('countries', {
|
|
252
|
-
name: 'Spain',
|
|
253
|
-
code: 'ES'
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
const createResult = await basicApi.resources.countries.post({
|
|
257
|
-
inputRecord: countryDoc,
|
|
258
|
-
simplified: false
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
// Update only the name
|
|
262
|
-
const updateDoc = {
|
|
263
|
-
data: {
|
|
264
|
-
type: 'countries',
|
|
265
|
-
id: String(createResult.data.id),
|
|
266
|
-
attributes: {
|
|
267
|
-
name: 'España'
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
await basicApi.resources.countries.patch({
|
|
273
|
-
id: createResult.data.id,
|
|
274
|
-
inputRecord: updateDoc,
|
|
275
|
-
simplified: false
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
// Verify the update
|
|
279
|
-
const getResult = await basicApi.resources.countries.get({
|
|
280
|
-
id: createResult.data.id,
|
|
281
|
-
simplified: false
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
assertResourceAttributes(getResult.data, {
|
|
285
|
-
name: 'España',
|
|
286
|
-
code: 'ES' // Should remain unchanged
|
|
287
|
-
})
|
|
288
|
-
})
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
describe('DELETE - Remove Resources', () => {
|
|
292
|
-
it('should delete an existing resource', async () => {
|
|
293
|
-
// Create a country
|
|
294
|
-
const countryDoc = createJsonApiDocument('countries', {
|
|
295
|
-
name: 'Portugal',
|
|
296
|
-
code: 'PT'
|
|
297
|
-
})
|
|
298
|
-
|
|
299
|
-
const createResult = await basicApi.resources.countries.post({
|
|
300
|
-
inputRecord: countryDoc,
|
|
301
|
-
simplified: false
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
// Verify it exists
|
|
305
|
-
let count = await countRecords(knex, 'basic_countries')
|
|
306
|
-
assert.equal(count, 1, 'Should have one country before delete')
|
|
307
|
-
|
|
308
|
-
// Delete the resource
|
|
309
|
-
const deleteResult = await basicApi.resources.countries.delete({
|
|
310
|
-
id: createResult.data.id,
|
|
311
|
-
simplified: false
|
|
312
|
-
})
|
|
313
|
-
|
|
314
|
-
// DELETE returns undefined (204 No Content)
|
|
315
|
-
assert.equal(deleteResult, undefined, 'Delete should return undefined')
|
|
316
|
-
|
|
317
|
-
// Verify it's gone
|
|
318
|
-
count = await countRecords(knex, 'basic_countries')
|
|
319
|
-
assert.equal(count, 0, 'Should have no countries after delete')
|
|
320
|
-
|
|
321
|
-
// Verify GET fails
|
|
322
|
-
await assert.rejects(
|
|
323
|
-
async () => {
|
|
324
|
-
await basicApi.resources.countries.get({
|
|
325
|
-
id: createResult.data.id,
|
|
326
|
-
simplified: false
|
|
327
|
-
})
|
|
328
|
-
},
|
|
329
|
-
(err) => {
|
|
330
|
-
return err.code === 'REST_API_RESOURCE' && err.subtype === 'not_found'
|
|
331
|
-
},
|
|
332
|
-
'Should throw not found error after delete'
|
|
333
|
-
)
|
|
334
|
-
})
|
|
335
|
-
})
|
|
336
|
-
})
|
|
337
|
-
})
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import { describe, it, before, after, beforeEach } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import knexLib from 'knex'
|
|
4
|
-
import { createReturnRecordApi } from './fixtures/api-configs.js'
|
|
5
|
-
import { cleanTables } from './helpers/test-utils.js'
|
|
6
|
-
|
|
7
|
-
const knex = knexLib({
|
|
8
|
-
client: 'better-sqlite3',
|
|
9
|
-
connection: {
|
|
10
|
-
filename: ':memory:'
|
|
11
|
-
},
|
|
12
|
-
useNullAsDefault: true
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
let api
|
|
16
|
-
|
|
17
|
-
function itemDocument (type, name, id) {
|
|
18
|
-
const doc = {
|
|
19
|
-
data: {
|
|
20
|
-
type,
|
|
21
|
-
attributes: { name }
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (id !== undefined) {
|
|
26
|
-
doc.data.id = String(id)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return doc
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
describe('Return record settings', () => {
|
|
33
|
-
before(async () => {
|
|
34
|
-
api = await createReturnRecordApi(knex)
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
after(async () => {
|
|
38
|
-
await knex.destroy()
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
beforeEach(async () => {
|
|
42
|
-
await cleanTables(knex, [
|
|
43
|
-
'return_global_items',
|
|
44
|
-
'return_scope_items'
|
|
45
|
-
])
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('normalizes plugin-level boolean return settings', async () => {
|
|
49
|
-
const created = await api.resources.global_items.post({
|
|
50
|
-
inputRecord: itemDocument('global_items', 'Global Created'),
|
|
51
|
-
simplified: false
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
assert.equal(created.data.type, 'global_items')
|
|
55
|
-
assert.equal(created.data.attributes.name, 'Global Created')
|
|
56
|
-
|
|
57
|
-
const replaced = await api.resources.global_items.put({
|
|
58
|
-
id: created.data.id,
|
|
59
|
-
inputRecord: itemDocument('global_items', 'Global Replaced', created.data.id),
|
|
60
|
-
simplified: false
|
|
61
|
-
})
|
|
62
|
-
assert.equal(replaced, undefined)
|
|
63
|
-
|
|
64
|
-
const patched = await api.resources.global_items.patch({
|
|
65
|
-
id: created.data.id,
|
|
66
|
-
inputRecord: itemDocument('global_items', 'Global Patched', created.data.id),
|
|
67
|
-
simplified: false
|
|
68
|
-
})
|
|
69
|
-
assert.equal(patched, undefined)
|
|
70
|
-
|
|
71
|
-
const fetched = await api.resources.global_items.get({
|
|
72
|
-
id: created.data.id,
|
|
73
|
-
simplified: false
|
|
74
|
-
})
|
|
75
|
-
assert.equal(fetched.data.attributes.name, 'Global Patched')
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('normalizes per-call boolean return settings', async () => {
|
|
79
|
-
const created = await api.resources.global_items.post({
|
|
80
|
-
inputRecord: itemDocument('global_items', 'Per-call Created'),
|
|
81
|
-
simplified: false,
|
|
82
|
-
returnFullRecord: false
|
|
83
|
-
})
|
|
84
|
-
assert.equal(created, undefined)
|
|
85
|
-
|
|
86
|
-
const queryResult = await api.resources.global_items.query({
|
|
87
|
-
simplified: false
|
|
88
|
-
})
|
|
89
|
-
assert.equal(queryResult.data.length, 1)
|
|
90
|
-
const fetched = queryResult.data[0]
|
|
91
|
-
assert.equal(fetched.attributes.name, 'Per-call Created')
|
|
92
|
-
|
|
93
|
-
const patched = await api.resources.global_items.patch({
|
|
94
|
-
id: fetched.id,
|
|
95
|
-
inputRecord: itemDocument('global_items', 'Per-call Patched', fetched.id),
|
|
96
|
-
simplified: false,
|
|
97
|
-
returnFullRecord: true
|
|
98
|
-
})
|
|
99
|
-
assert.equal(patched.data.type, 'global_items')
|
|
100
|
-
assert.equal(patched.data.id, fetched.id)
|
|
101
|
-
assert.equal(patched.data.attributes.name, 'Per-call Patched')
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('normalizes resource-level boolean return settings', async () => {
|
|
105
|
-
const created = await api.resources.scope_items.post({
|
|
106
|
-
inputRecord: itemDocument('scope_items', 'Scoped Created'),
|
|
107
|
-
simplified: false
|
|
108
|
-
})
|
|
109
|
-
assert.equal(created, undefined)
|
|
110
|
-
|
|
111
|
-
const queryResult = await api.resources.scope_items.query({
|
|
112
|
-
simplified: false
|
|
113
|
-
})
|
|
114
|
-
assert.equal(queryResult.data.length, 1)
|
|
115
|
-
const fetched = queryResult.data[0]
|
|
116
|
-
|
|
117
|
-
const replaced = await api.resources.scope_items.put({
|
|
118
|
-
id: fetched.id,
|
|
119
|
-
inputRecord: itemDocument('scope_items', 'Scoped Replaced', fetched.id),
|
|
120
|
-
simplified: false
|
|
121
|
-
})
|
|
122
|
-
assert.equal(replaced.data.type, 'scope_items')
|
|
123
|
-
assert.equal(replaced.data.id, fetched.id)
|
|
124
|
-
assert.equal(replaced.data.attributes.name, 'Scoped Replaced')
|
|
125
|
-
|
|
126
|
-
const patched = await api.resources.scope_items.patch({
|
|
127
|
-
id: fetched.id,
|
|
128
|
-
inputRecord: itemDocument('scope_items', 'Scoped Patched', fetched.id),
|
|
129
|
-
simplified: false
|
|
130
|
-
})
|
|
131
|
-
assert.deepEqual(patched, {
|
|
132
|
-
data: {
|
|
133
|
-
type: 'scope_items',
|
|
134
|
-
id: fetched.id
|
|
135
|
-
}
|
|
136
|
-
})
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
it('normalizes transport boolean return settings', async () => {
|
|
140
|
-
const created = await api.resources.global_items.post({
|
|
141
|
-
inputRecord: itemDocument('global_items', 'Transport Created'),
|
|
142
|
-
simplified: false,
|
|
143
|
-
isTransport: true
|
|
144
|
-
})
|
|
145
|
-
assert.equal(created, undefined)
|
|
146
|
-
|
|
147
|
-
const queryResult = await api.resources.global_items.query({
|
|
148
|
-
simplified: false
|
|
149
|
-
})
|
|
150
|
-
assert.equal(queryResult.data.length, 1)
|
|
151
|
-
const fetched = queryResult.data[0]
|
|
152
|
-
|
|
153
|
-
const replaced = await api.resources.global_items.put({
|
|
154
|
-
id: fetched.id,
|
|
155
|
-
inputRecord: itemDocument('global_items', 'Transport Replaced', fetched.id),
|
|
156
|
-
simplified: false,
|
|
157
|
-
isTransport: true
|
|
158
|
-
})
|
|
159
|
-
assert.equal(replaced.data.type, 'global_items')
|
|
160
|
-
assert.equal(replaced.data.id, fetched.id)
|
|
161
|
-
assert.equal(replaced.data.attributes.name, 'Transport Replaced')
|
|
162
|
-
|
|
163
|
-
const patched = await api.resources.global_items.patch({
|
|
164
|
-
id: fetched.id,
|
|
165
|
-
inputRecord: itemDocument('global_items', 'Transport Patched', fetched.id),
|
|
166
|
-
simplified: false,
|
|
167
|
-
isTransport: true
|
|
168
|
-
})
|
|
169
|
-
assert.deepEqual(patched, {
|
|
170
|
-
data: {
|
|
171
|
-
type: 'global_items',
|
|
172
|
-
id: fetched.id
|
|
173
|
-
}
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
const scoped = await api.resources.scope_items.post({
|
|
177
|
-
inputRecord: itemDocument('scope_items', 'Scoped Transport Created'),
|
|
178
|
-
simplified: false,
|
|
179
|
-
isTransport: true
|
|
180
|
-
})
|
|
181
|
-
assert.equal(scoped.data.type, 'scope_items')
|
|
182
|
-
assert.equal(scoped.data.attributes.name, 'Scoped Transport Created')
|
|
183
|
-
})
|
|
184
|
-
})
|