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,331 +0,0 @@
|
|
|
1
|
-
import { describe, it, before, after } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import knexLib from 'knex'
|
|
4
|
-
import { createBasicApi, createLimitedDepthApi } from './fixtures/api-configs.js'
|
|
5
|
-
import { cleanTables } from './helpers/test-utils.js'
|
|
6
|
-
|
|
7
|
-
// Create Knex instance for tests
|
|
8
|
-
const knex = knexLib({
|
|
9
|
-
client: 'better-sqlite3',
|
|
10
|
-
connection: {
|
|
11
|
-
filename: ':memory:'
|
|
12
|
-
},
|
|
13
|
-
useNullAsDefault: true
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
describe('Include Depth Validation', () => {
|
|
17
|
-
let api
|
|
18
|
-
|
|
19
|
-
before(async () => {
|
|
20
|
-
// Initialize API with custom includeDepthLimit
|
|
21
|
-
api = await createBasicApi(knex, {
|
|
22
|
-
'rest-api': {
|
|
23
|
-
includeDepthLimit: 3 // Default is 3, but being explicit
|
|
24
|
-
}
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
// Set up test data with nested relationships
|
|
28
|
-
// Country -> Publisher -> Author -> Book
|
|
29
|
-
await cleanTables(knex, [
|
|
30
|
-
'basic_countries', 'basic_publishers', 'basic_authors', 'basic_books'
|
|
31
|
-
])
|
|
32
|
-
|
|
33
|
-
const country = await api.resources.countries.post({
|
|
34
|
-
inputRecord: {
|
|
35
|
-
data: {
|
|
36
|
-
type: 'countries',
|
|
37
|
-
attributes: { name: 'USA', code: 'US' }
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
const publisher = await api.resources.publishers.post({
|
|
43
|
-
inputRecord: {
|
|
44
|
-
data: {
|
|
45
|
-
type: 'publishers',
|
|
46
|
-
attributes: { name: 'Test Publisher' },
|
|
47
|
-
relationships: {
|
|
48
|
-
country: { data: { type: 'countries', id: country.data.id } }
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
const author = await api.resources.authors.post({
|
|
55
|
-
inputRecord: {
|
|
56
|
-
data: {
|
|
57
|
-
type: 'authors',
|
|
58
|
-
attributes: { name: 'Test Author' },
|
|
59
|
-
relationships: {
|
|
60
|
-
publisher: { data: { type: 'publishers', id: publisher.data.id } }
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
await api.resources.books.post({
|
|
67
|
-
inputRecord: {
|
|
68
|
-
data: {
|
|
69
|
-
type: 'books',
|
|
70
|
-
attributes: { title: 'Test Book' },
|
|
71
|
-
relationships: {
|
|
72
|
-
country: { data: { type: 'countries', id: country.data.id } },
|
|
73
|
-
publisher: { data: { type: 'publishers', id: publisher.data.id } }
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
})
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
after(async () => {
|
|
81
|
-
await knex.destroy()
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
describe('Query endpoint validation', () => {
|
|
85
|
-
it('should allow includes within depth limit', async () => {
|
|
86
|
-
// Depth 1
|
|
87
|
-
const depth1 = await api.resources.publishers.query({
|
|
88
|
-
queryParams: { include: ['country'] }
|
|
89
|
-
})
|
|
90
|
-
assert(depth1.data.length > 0, 'Should return publishers')
|
|
91
|
-
assert(depth1.included, 'Should have included resources')
|
|
92
|
-
|
|
93
|
-
// Depth 2
|
|
94
|
-
const depth2 = await api.resources.authors.query({
|
|
95
|
-
queryParams: { include: ['publisher.country'] }
|
|
96
|
-
})
|
|
97
|
-
assert(depth2.data.length > 0, 'Should return authors')
|
|
98
|
-
// The included array may or may not be present depending on whether relationships exist
|
|
99
|
-
|
|
100
|
-
// Depth 3 (at limit)
|
|
101
|
-
const depth3 = await api.resources.books.query({
|
|
102
|
-
queryParams: { include: ['publisher.country.publishers'] }
|
|
103
|
-
})
|
|
104
|
-
assert(depth3.data.length > 0, 'Should return books')
|
|
105
|
-
// Note: The included array might be present even if some relationships don't have data
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('should reject includes exceeding depth limit', async () => {
|
|
109
|
-
await assert.rejects(
|
|
110
|
-
async () => {
|
|
111
|
-
await api.resources.books.query({
|
|
112
|
-
queryParams: { include: ['publisher.country.publishers.books'] }
|
|
113
|
-
})
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
name: 'RestApiValidationError',
|
|
117
|
-
message: /Include path.*exceeds maximum depth of 3/
|
|
118
|
-
},
|
|
119
|
-
'Should reject depth 4'
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
await assert.rejects(
|
|
123
|
-
async () => {
|
|
124
|
-
await api.resources.books.query({
|
|
125
|
-
queryParams: { include: ['publisher.country.publishers.books.authors'] }
|
|
126
|
-
})
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
name: 'RestApiValidationError',
|
|
130
|
-
message: /Include path.*exceeds maximum depth of 3/
|
|
131
|
-
},
|
|
132
|
-
'Should reject depth 5'
|
|
133
|
-
)
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
it('should validate each include path independently', async () => {
|
|
137
|
-
await assert.rejects(
|
|
138
|
-
async () => {
|
|
139
|
-
await api.resources.books.query({
|
|
140
|
-
queryParams: {
|
|
141
|
-
include: [
|
|
142
|
-
'publisher', // Valid (depth 1)
|
|
143
|
-
'publisher.country', // Valid (depth 2)
|
|
144
|
-
'publisher.country.publishers.books' // Invalid (depth 4)
|
|
145
|
-
]
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
name: 'RestApiValidationError',
|
|
151
|
-
message: /publisher\.country\.publishers\.books.*exceeds maximum depth of 3/
|
|
152
|
-
},
|
|
153
|
-
'Should reject when any path exceeds limit'
|
|
154
|
-
)
|
|
155
|
-
})
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
describe('Get endpoint validation', () => {
|
|
159
|
-
it('should allow includes within depth limit', async () => {
|
|
160
|
-
const books = await api.resources.books.query()
|
|
161
|
-
const bookId = books.data[0].id
|
|
162
|
-
|
|
163
|
-
// Depth 1
|
|
164
|
-
const depth1 = await api.resources.books.get({
|
|
165
|
-
id: bookId,
|
|
166
|
-
queryParams: { include: ['publisher'] }
|
|
167
|
-
})
|
|
168
|
-
assert(depth1.data.id === bookId, 'Should return correct book')
|
|
169
|
-
assert(depth1.included, 'Should have included resources')
|
|
170
|
-
|
|
171
|
-
// Depth 3 (at limit)
|
|
172
|
-
const depth3 = await api.resources.books.get({
|
|
173
|
-
id: bookId,
|
|
174
|
-
queryParams: { include: ['publisher.country.publishers'] }
|
|
175
|
-
})
|
|
176
|
-
assert(depth3.data.id === bookId, 'Should return correct book')
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
it('should reject includes exceeding depth limit', async () => {
|
|
180
|
-
const books = await api.resources.books.query()
|
|
181
|
-
const bookId = books.data[0].id
|
|
182
|
-
|
|
183
|
-
await assert.rejects(
|
|
184
|
-
async () => {
|
|
185
|
-
await api.resources.books.get({
|
|
186
|
-
id: bookId,
|
|
187
|
-
queryParams: { include: ['publisher.country.publishers.books'] }
|
|
188
|
-
})
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
name: 'RestApiValidationError',
|
|
192
|
-
message: /Include path.*exceeds maximum depth of 3/
|
|
193
|
-
},
|
|
194
|
-
'Should reject depth 4 on GET'
|
|
195
|
-
)
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
describe('Custom depth limits', () => {
|
|
200
|
-
it('should respect custom includeDepthLimit configuration', async () => {
|
|
201
|
-
// Create API with depth limit of 2 and different table names
|
|
202
|
-
const limitedApi = await createLimitedDepthApi(knex)
|
|
203
|
-
|
|
204
|
-
// Set up test data for limited API
|
|
205
|
-
await cleanTables(knex, [
|
|
206
|
-
'limited_countries', 'limited_publishers', 'limited_authors', 'limited_books', 'limited_book_authors'
|
|
207
|
-
])
|
|
208
|
-
|
|
209
|
-
const country = await limitedApi.resources.countries.post({
|
|
210
|
-
inputRecord: {
|
|
211
|
-
data: {
|
|
212
|
-
type: 'countries',
|
|
213
|
-
attributes: { name: 'Limited Country', code: 'LC' }
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
const publisher = await limitedApi.resources.publishers.post({
|
|
219
|
-
inputRecord: {
|
|
220
|
-
data: {
|
|
221
|
-
type: 'publishers',
|
|
222
|
-
attributes: { name: 'Limited Publisher' },
|
|
223
|
-
relationships: {
|
|
224
|
-
country: { data: { type: 'countries', id: country.data.id } }
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
const author = await limitedApi.resources.authors.post({
|
|
231
|
-
inputRecord: {
|
|
232
|
-
data: {
|
|
233
|
-
type: 'authors',
|
|
234
|
-
attributes: { name: 'Limited Author' },
|
|
235
|
-
relationships: {
|
|
236
|
-
publisher: { data: { type: 'publishers', id: publisher.data.id } }
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
// Depth 2 should work
|
|
243
|
-
const depth2 = await limitedApi.resources.authors.query({
|
|
244
|
-
queryParams: { include: ['publisher.country'] }
|
|
245
|
-
})
|
|
246
|
-
assert(depth2.data.length > 0, 'Should return authors')
|
|
247
|
-
|
|
248
|
-
// Depth 3 should fail
|
|
249
|
-
await assert.rejects(
|
|
250
|
-
async () => {
|
|
251
|
-
await limitedApi.resources.books.query({
|
|
252
|
-
queryParams: { include: ['publisher.country.publishers'] }
|
|
253
|
-
})
|
|
254
|
-
},
|
|
255
|
-
{
|
|
256
|
-
name: 'RestApiValidationError',
|
|
257
|
-
message: /Include path.*exceeds maximum depth of 2/
|
|
258
|
-
},
|
|
259
|
-
'Should reject depth 3 with custom limit of 2'
|
|
260
|
-
)
|
|
261
|
-
})
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
describe('Edge cases', () => {
|
|
265
|
-
it('should handle empty include arrays', async () => {
|
|
266
|
-
const result = await api.resources.books.query({
|
|
267
|
-
queryParams: { include: [] }
|
|
268
|
-
})
|
|
269
|
-
assert(result.data.length > 0, 'Should return books')
|
|
270
|
-
assert(!result.included, 'Should not have included resources')
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
it('should handle single-level paths correctly', async () => {
|
|
274
|
-
const country = await api.resources.countries.post({
|
|
275
|
-
inputRecord: {
|
|
276
|
-
data: {
|
|
277
|
-
type: 'countries',
|
|
278
|
-
attributes: { name: 'Single Level Country', code: `S${Date.now().toString().slice(-2)}` }
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
const publisher = await api.resources.publishers.post({
|
|
284
|
-
inputRecord: {
|
|
285
|
-
data: {
|
|
286
|
-
type: 'publishers',
|
|
287
|
-
attributes: { name: 'Single Level Publisher' },
|
|
288
|
-
relationships: {
|
|
289
|
-
country: { data: { type: 'countries', id: country.data.id } }
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
await api.resources.books.post({
|
|
296
|
-
inputRecord: {
|
|
297
|
-
data: {
|
|
298
|
-
type: 'books',
|
|
299
|
-
attributes: { title: 'Single Level Book' },
|
|
300
|
-
relationships: {
|
|
301
|
-
country: { data: { type: 'countries', id: country.data.id } },
|
|
302
|
-
publisher: { data: { type: 'publishers', id: publisher.data.id } }
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
const result = await api.resources.books.query({
|
|
309
|
-
queryParams: { include: ['publisher'] }
|
|
310
|
-
})
|
|
311
|
-
assert(result.data.length > 0, 'Should return books')
|
|
312
|
-
assert(result.included, 'Should have included resources')
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
it('should count depth correctly with dots in path', async () => {
|
|
316
|
-
// "a.b.c" = depth 3
|
|
317
|
-
await assert.rejects(
|
|
318
|
-
async () => {
|
|
319
|
-
await api.resources.books.query({
|
|
320
|
-
queryParams: { include: ['a.b.c.d'] }
|
|
321
|
-
})
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
name: 'RestApiValidationError',
|
|
325
|
-
message: /Include path 'a\.b\.c\.d' exceeds maximum depth of 3/
|
|
326
|
-
},
|
|
327
|
-
'Should count dots correctly'
|
|
328
|
-
)
|
|
329
|
-
})
|
|
330
|
-
})
|
|
331
|
-
})
|