json-rest-api 1.0.13 → 1.0.15
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/connectors_ideas/fastify.js +66 -68
- package/connectors_ideas/koa-plugin.js +50 -51
- package/eslint.config.js +13 -0
- package/examples/url-override-example.js +63 -63
- package/index.js +13 -13
- package/lib/enhanced-logger.js +51 -51
- package/lib/error-formatter.js +61 -61
- package/lib/rest-api-errors.js +28 -28
- package/package.json +9 -5
- package/plugins/core/bulk-operations-plugin.js +142 -145
- package/plugins/core/connectors/express-plugin.js +210 -217
- package/plugins/core/connectors/lib/busboy-detector.js +84 -84
- package/plugins/core/connectors/lib/formidable-detector.js +50 -50
- package/plugins/core/connectors/lib/request-helpers.js +74 -74
- package/plugins/core/file-handling-plugin.js +125 -125
- package/plugins/core/lib/anyapi/anyapi-registry.js +254 -254
- package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +287 -287
- package/plugins/core/lib/anyapi/schema-utils.js +101 -101
- package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +41 -41
- package/plugins/core/lib/dbTablesOperations.js +134 -135
- package/plugins/core/lib/fractional-positioning.js +74 -76
- package/plugins/core/lib/querying/knex-cross-table-search.js +320 -320
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +144 -144
- package/plugins/core/lib/querying/knex-pagination-helpers.js +164 -164
- package/plugins/core/lib/querying/knex-process-includes.js +50 -50
- package/plugins/core/lib/querying/knex-query-helpers-base.js +31 -31
- package/plugins/core/lib/querying/knex-query-helpers.js +451 -452
- package/plugins/core/lib/querying/knex-relationship-includes.js +1160 -1170
- package/plugins/core/lib/querying/knex-window-queries.js +89 -89
- package/plugins/core/lib/querying/url-helpers.js +35 -35
- package/plugins/core/lib/querying-writing/compile-schemas.js +97 -98
- package/plugins/core/lib/querying-writing/connectors-query-parser.js +31 -31
- package/plugins/core/lib/querying-writing/database-capabilities.js +94 -94
- package/plugins/core/lib/querying-writing/database-value-normalizers.js +76 -76
- package/plugins/core/lib/querying-writing/default-data-helpers.js +27 -27
- package/plugins/core/lib/querying-writing/field-utils.js +44 -42
- package/plugins/core/lib/querying-writing/knex-constants.js +15 -16
- package/plugins/core/lib/querying-writing/knex-field-helpers.js +117 -115
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +41 -41
- package/plugins/core/lib/querying-writing/payload-validators.js +275 -275
- package/plugins/core/lib/querying-writing/schema-helpers.js +129 -129
- package/plugins/core/lib/querying-writing/scope-validations.js +143 -146
- package/plugins/core/lib/querying-writing/simplified-helpers.js +133 -129
- package/plugins/core/lib/storage/storage-adapter.js +68 -68
- package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +31 -31
- package/plugins/core/lib/writing/many-to-many-manipulations.js +81 -81
- package/plugins/core/lib/writing/relationship-processor.js +58 -58
- package/plugins/core/rest-api-access.js +8 -8
- package/plugins/core/rest-api-anyapi-knex-plugin.js +920 -920
- package/plugins/core/rest-api-cors-plugin.js +170 -171
- package/plugins/core/rest-api-knex-plugin.js +482 -493
- package/plugins/core/rest-api-multihome-plugin.js +247 -251
- package/plugins/core/rest-api-plugin-hooks/compile-resource-schemas.js +5 -5
- package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +20 -21
- package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +44 -44
- package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +23 -23
- package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +15 -15
- package/plugins/core/rest-api-plugin-methods/add-route.js +9 -9
- package/plugins/core/rest-api-plugin-methods/check-permissions.js +5 -6
- package/plugins/core/rest-api-plugin-methods/common.js +295 -297
- package/plugins/core/rest-api-plugin-methods/delete-relationship.js +35 -37
- package/plugins/core/rest-api-plugin-methods/delete.js +57 -59
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +172 -171
- package/plugins/core/rest-api-plugin-methods/get-related.js +96 -96
- package/plugins/core/rest-api-plugin-methods/get-relationship.js +16 -18
- package/plugins/core/rest-api-plugin-methods/get.js +86 -86
- package/plugins/core/rest-api-plugin-methods/patch-relationship.js +16 -17
- package/plugins/core/rest-api-plugin-methods/patch.js +63 -64
- package/plugins/core/rest-api-plugin-methods/post-relationship.js +54 -55
- package/plugins/core/rest-api-plugin-methods/post.js +61 -62
- package/plugins/core/rest-api-plugin-methods/put.js +86 -87
- package/plugins/core/rest-api-plugin-methods/query.js +88 -88
- package/plugins/core/rest-api-plugin-methods/release.js +5 -6
- package/plugins/core/rest-api-plugin.js +76 -78
- package/plugins/core/rest-api-positioning-plugin.js +251 -252
- package/plugins/core/socketio-plugin.js +240 -236
- package/plugins/storage/local-storage.js +103 -103
- package/plugins/storage/s3-storage.js +41 -42
- package/quickTest.js +37 -37
- package/scripts/debug-anyapi-review-filter.mjs +18 -18
- package/scripts/inspect-anyapi-descriptor-country.mjs +10 -10
- package/scripts/inspect-anyapi-descriptor.mjs +10 -10
- package/scripts/run-docs.js +112 -113
- package/test-debug.js +28 -28
- package/tests/TEST_TEMPLATE.test.js +51 -51
- package/tests/anyapi-basic.test.js +42 -42
- package/tests/anyapi-cursor-pagination.test.js +48 -48
- package/tests/bulk-operations.test.js +208 -208
- package/tests/computed-fields.test.js +204 -204
- package/tests/cors.test.js +412 -413
- package/tests/curl.test.js +275 -276
- package/tests/custom-idproperty-relationships.test.js +266 -266
- package/tests/field-getters.test.js +132 -132
- package/tests/field-setters.test.js +110 -110
- package/tests/fixtures/api-configs.js +699 -699
- package/tests/helpers/storage-mode.js +32 -32
- package/tests/helpers/test-utils.js +76 -76
- package/tests/include-depth-validation.test.js +76 -76
- package/tests/includes.test.js +145 -145
- package/tests/multihome.test.js +175 -175
- package/tests/nested-includes.test.js +255 -255
- package/tests/pagination-cursor-multifield.test.js +164 -164
- package/tests/pagination-enhanced.test.js +225 -225
- package/tests/polymorphic-relationships.test.js +96 -96
- package/tests/polymorphic-simple.test.js +44 -44
- package/tests/positioning-advanced.test.js +226 -226
- package/tests/positioning-extended.test.js +399 -399
- package/tests/positioning-multifilter.test.js +204 -204
- package/tests/positioning.test.js +266 -266
- package/tests/queries.test.js +184 -184
- package/tests/query-limits.test.js +154 -155
- package/tests/relationship-endpoints.test.js +127 -127
- package/tests/relationships.test.js +178 -179
- package/tests/rest-api-access.test.js +86 -86
- package/tests/rest-api.test.js +103 -103
- package/tests/searchschema-merge.test.js +93 -93
- package/tests/socketio.test.js +326 -326
- package/tests/virtual-fields.test.js +164 -164
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { describe, it, before, after, beforeEach } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import knexLib from 'knex'
|
|
4
|
-
import {
|
|
5
|
-
validateJsonApiStructure,
|
|
6
|
-
cleanTables,
|
|
7
|
-
createJsonApiDocument,
|
|
1
|
+
import { describe, it, before, after, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import knexLib from 'knex'
|
|
4
|
+
import {
|
|
5
|
+
validateJsonApiStructure,
|
|
6
|
+
cleanTables,
|
|
7
|
+
createJsonApiDocument,
|
|
8
8
|
createRelationship,
|
|
9
9
|
createToManyRelationship,
|
|
10
10
|
resourceIdentifier,
|
|
11
11
|
countRecords
|
|
12
|
-
} from './helpers/test-utils.js'
|
|
13
|
-
import { createBasicApi } from './fixtures/api-configs.js'
|
|
12
|
+
} from './helpers/test-utils.js'
|
|
13
|
+
import { createBasicApi } from './fixtures/api-configs.js'
|
|
14
14
|
|
|
15
15
|
// Create Knex instance for tests
|
|
16
16
|
const knex = knexLib({
|
|
@@ -19,10 +19,10 @@ const knex = knexLib({
|
|
|
19
19
|
filename: ':memory:'
|
|
20
20
|
},
|
|
21
21
|
useNullAsDefault: true
|
|
22
|
-
})
|
|
22
|
+
})
|
|
23
23
|
|
|
24
24
|
// API instance that persists across tests
|
|
25
|
-
let api
|
|
25
|
+
let api
|
|
26
26
|
|
|
27
27
|
describe('Relationship Endpoints Plugin', () => {
|
|
28
28
|
before(async () => {
|
|
@@ -32,80 +32,80 @@ describe('Relationship Endpoints Plugin', () => {
|
|
|
32
32
|
mountPath: '' // No mount path for this test
|
|
33
33
|
},
|
|
34
34
|
includeExpress: true
|
|
35
|
-
})
|
|
36
|
-
})
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
37
|
|
|
38
38
|
after(async () => {
|
|
39
39
|
// Close database connection
|
|
40
|
-
await knex.destroy()
|
|
41
|
-
})
|
|
40
|
+
await knex.destroy()
|
|
41
|
+
})
|
|
42
42
|
|
|
43
43
|
describe('Many-to-Many Relationship Endpoints', () => {
|
|
44
|
-
let countryId
|
|
45
|
-
let book1Id
|
|
46
|
-
let author1Id
|
|
47
|
-
let author2Id
|
|
48
|
-
let author3Id
|
|
44
|
+
let countryId
|
|
45
|
+
let book1Id
|
|
46
|
+
let author1Id
|
|
47
|
+
let author2Id
|
|
48
|
+
let author3Id
|
|
49
49
|
|
|
50
50
|
beforeEach(async () => {
|
|
51
51
|
await cleanTables(knex, [
|
|
52
52
|
'basic_countries', 'basic_publishers', 'basic_authors', 'basic_books', 'basic_book_authors'
|
|
53
|
-
])
|
|
53
|
+
])
|
|
54
54
|
|
|
55
55
|
// Create test data
|
|
56
|
-
const countryDoc = createJsonApiDocument('countries', { name: 'USA', code: 'US' })
|
|
56
|
+
const countryDoc = createJsonApiDocument('countries', { name: 'USA', code: 'US' })
|
|
57
57
|
const countryResult = await api.resources.countries.post({
|
|
58
58
|
inputRecord: countryDoc,
|
|
59
59
|
simplified: false
|
|
60
|
-
})
|
|
61
|
-
countryId = countryResult.data.id
|
|
60
|
+
})
|
|
61
|
+
countryId = countryResult.data.id
|
|
62
62
|
|
|
63
63
|
// Create a book
|
|
64
64
|
const bookDoc = createJsonApiDocument('books',
|
|
65
65
|
{ title: 'Test Book' },
|
|
66
66
|
{ country: createRelationship(resourceIdentifier('countries', countryId)) }
|
|
67
|
-
)
|
|
67
|
+
)
|
|
68
68
|
const bookResult = await api.resources.books.post({
|
|
69
69
|
inputRecord: bookDoc,
|
|
70
70
|
simplified: false
|
|
71
|
-
})
|
|
72
|
-
book1Id = bookResult.data.id
|
|
71
|
+
})
|
|
72
|
+
book1Id = bookResult.data.id
|
|
73
73
|
|
|
74
74
|
// Create authors
|
|
75
|
-
const author1Doc = createJsonApiDocument('authors', { name: 'Author One' })
|
|
75
|
+
const author1Doc = createJsonApiDocument('authors', { name: 'Author One' })
|
|
76
76
|
const author1Result = await api.resources.authors.post({
|
|
77
77
|
inputRecord: author1Doc,
|
|
78
78
|
simplified: false
|
|
79
|
-
})
|
|
80
|
-
author1Id = author1Result.data.id
|
|
79
|
+
})
|
|
80
|
+
author1Id = author1Result.data.id
|
|
81
81
|
|
|
82
|
-
const author2Doc = createJsonApiDocument('authors', { name: 'Author Two' })
|
|
82
|
+
const author2Doc = createJsonApiDocument('authors', { name: 'Author Two' })
|
|
83
83
|
const author2Result = await api.resources.authors.post({
|
|
84
84
|
inputRecord: author2Doc,
|
|
85
85
|
simplified: false
|
|
86
|
-
})
|
|
87
|
-
author2Id = author2Result.data.id
|
|
86
|
+
})
|
|
87
|
+
author2Id = author2Result.data.id
|
|
88
88
|
|
|
89
|
-
const author3Doc = createJsonApiDocument('authors', { name: 'Author Three' })
|
|
89
|
+
const author3Doc = createJsonApiDocument('authors', { name: 'Author Three' })
|
|
90
90
|
const author3Result = await api.resources.authors.post({
|
|
91
91
|
inputRecord: author3Doc,
|
|
92
92
|
simplified: false
|
|
93
|
-
})
|
|
94
|
-
author3Id = author3Result.data.id
|
|
95
|
-
})
|
|
93
|
+
})
|
|
94
|
+
author3Id = author3Result.data.id
|
|
95
|
+
})
|
|
96
96
|
|
|
97
97
|
it('should get empty relationship data for new book', async () => {
|
|
98
98
|
const result = await api.resources.books.getRelationship({
|
|
99
99
|
id: book1Id,
|
|
100
100
|
relationshipName: 'authors'
|
|
101
|
-
})
|
|
101
|
+
})
|
|
102
102
|
|
|
103
|
-
assert(result.links, 'Should have links')
|
|
104
|
-
assert.equal(result.links.self, `/books/${book1Id}/relationships/authors`)
|
|
105
|
-
assert.equal(result.links.related, `/books/${book1Id}/authors`)
|
|
106
|
-
assert(Array.isArray(result.data), 'Data should be an array')
|
|
107
|
-
assert.equal(result.data.length, 0, 'Should have no authors')
|
|
108
|
-
})
|
|
103
|
+
assert(result.links, 'Should have links')
|
|
104
|
+
assert.equal(result.links.self, `/books/${book1Id}/relationships/authors`)
|
|
105
|
+
assert.equal(result.links.related, `/books/${book1Id}/authors`)
|
|
106
|
+
assert(Array.isArray(result.data), 'Data should be an array')
|
|
107
|
+
assert.equal(result.data.length, 0, 'Should have no authors')
|
|
108
|
+
})
|
|
109
109
|
|
|
110
110
|
it('should add authors to book via POST relationship endpoint', async () => {
|
|
111
111
|
// Add authors via relationship endpoint
|
|
@@ -116,21 +116,21 @@ describe('Relationship Endpoints Plugin', () => {
|
|
|
116
116
|
resourceIdentifier('authors', author1Id),
|
|
117
117
|
resourceIdentifier('authors', author2Id)
|
|
118
118
|
]
|
|
119
|
-
})
|
|
119
|
+
})
|
|
120
120
|
|
|
121
121
|
// Verify authors were added via getRelationship
|
|
122
122
|
const relResult = await api.resources.books.getRelationship({
|
|
123
123
|
id: book1Id,
|
|
124
124
|
relationshipName: 'authors'
|
|
125
|
-
})
|
|
125
|
+
})
|
|
126
126
|
|
|
127
|
-
assert.equal(relResult.data.length, 2, 'Should have 2 authors')
|
|
128
|
-
assert.deepEqual(relResult.data[0], resourceIdentifier('authors', author1Id))
|
|
129
|
-
assert.deepEqual(relResult.data[1], resourceIdentifier('authors', author2Id))
|
|
127
|
+
assert.equal(relResult.data.length, 2, 'Should have 2 authors')
|
|
128
|
+
assert.deepEqual(relResult.data[0], resourceIdentifier('authors', author1Id))
|
|
129
|
+
assert.deepEqual(relResult.data[1], resourceIdentifier('authors', author2Id))
|
|
130
130
|
|
|
131
131
|
// Verify pivot table records
|
|
132
|
-
const pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
133
|
-
assert.equal(pivotCount, 2, 'Should have 2 pivot records')
|
|
132
|
+
const pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
133
|
+
assert.equal(pivotCount, 2, 'Should have 2 pivot records')
|
|
134
134
|
|
|
135
135
|
// Verify through GET with include
|
|
136
136
|
const getResult = await api.resources.books.get({
|
|
@@ -139,13 +139,13 @@ describe('Relationship Endpoints Plugin', () => {
|
|
|
139
139
|
include: ['authors']
|
|
140
140
|
},
|
|
141
141
|
simplified: false
|
|
142
|
-
})
|
|
142
|
+
})
|
|
143
143
|
|
|
144
|
-
assert(getResult.included, 'Should have included data')
|
|
145
|
-
assert.equal(getResult.included.length, 2, 'Should include 2 authors')
|
|
146
|
-
assert(getResult.data.relationships.authors, 'Should have authors relationship')
|
|
147
|
-
assert.equal(getResult.data.relationships.authors.data.length, 2)
|
|
148
|
-
})
|
|
144
|
+
assert(getResult.included, 'Should have included data')
|
|
145
|
+
assert.equal(getResult.included.length, 2, 'Should include 2 authors')
|
|
146
|
+
assert(getResult.data.relationships.authors, 'Should have authors relationship')
|
|
147
|
+
assert.equal(getResult.data.relationships.authors.data.length, 2)
|
|
148
|
+
})
|
|
149
149
|
|
|
150
150
|
it('should get related resources via GET related endpoint', async () => {
|
|
151
151
|
// Add authors first
|
|
@@ -156,23 +156,23 @@ describe('Relationship Endpoints Plugin', () => {
|
|
|
156
156
|
resourceIdentifier('authors', author1Id),
|
|
157
157
|
resourceIdentifier('authors', author2Id)
|
|
158
158
|
]
|
|
159
|
-
})
|
|
159
|
+
})
|
|
160
160
|
|
|
161
161
|
// Get related resources
|
|
162
162
|
const result = await api.resources.books.getRelated({
|
|
163
163
|
id: book1Id,
|
|
164
164
|
relationshipName: 'authors'
|
|
165
|
-
})
|
|
165
|
+
})
|
|
166
166
|
|
|
167
167
|
// Verify response
|
|
168
|
-
validateJsonApiStructure(result, true)
|
|
169
|
-
assert.equal(result.data.length, 2, 'Should have 2 authors')
|
|
170
|
-
assert.equal(result.data[0].type, 'authors')
|
|
171
|
-
assert.equal(result.data[1].type, 'authors')
|
|
172
|
-
|
|
173
|
-
const authorNames = result.data.map(a => a.attributes.name).sort()
|
|
174
|
-
assert.deepEqual(authorNames, ['Author One', 'Author Two'])
|
|
175
|
-
})
|
|
168
|
+
validateJsonApiStructure(result, true) // true for collection
|
|
169
|
+
assert.equal(result.data.length, 2, 'Should have 2 authors')
|
|
170
|
+
assert.equal(result.data[0].type, 'authors')
|
|
171
|
+
assert.equal(result.data[1].type, 'authors')
|
|
172
|
+
|
|
173
|
+
const authorNames = result.data.map(a => a.attributes.name).sort()
|
|
174
|
+
assert.deepEqual(authorNames, ['Author One', 'Author Two'])
|
|
175
|
+
})
|
|
176
176
|
|
|
177
177
|
it('should remove specific authors via DELETE relationship endpoint', async () => {
|
|
178
178
|
// Add all authors first
|
|
@@ -184,11 +184,11 @@ describe('Relationship Endpoints Plugin', () => {
|
|
|
184
184
|
resourceIdentifier('authors', author2Id),
|
|
185
185
|
resourceIdentifier('authors', author3Id)
|
|
186
186
|
]
|
|
187
|
-
})
|
|
187
|
+
})
|
|
188
188
|
|
|
189
189
|
// Verify all were added
|
|
190
|
-
let pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
191
|
-
assert.equal(pivotCount, 3, 'Should have 3 pivot records')
|
|
190
|
+
let pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
191
|
+
assert.equal(pivotCount, 3, 'Should have 3 pivot records')
|
|
192
192
|
|
|
193
193
|
// Remove one author
|
|
194
194
|
await api.resources.books.deleteRelationship({
|
|
@@ -197,22 +197,22 @@ describe('Relationship Endpoints Plugin', () => {
|
|
|
197
197
|
relationshipData: [
|
|
198
198
|
resourceIdentifier('authors', author2Id)
|
|
199
199
|
]
|
|
200
|
-
})
|
|
200
|
+
})
|
|
201
201
|
|
|
202
202
|
// Verify only two authors remain
|
|
203
203
|
const relResult = await api.resources.books.getRelationship({
|
|
204
204
|
id: book1Id,
|
|
205
205
|
relationshipName: 'authors'
|
|
206
|
-
})
|
|
206
|
+
})
|
|
207
207
|
|
|
208
|
-
assert.equal(relResult.data.length, 2, 'Should have 2 authors remaining')
|
|
209
|
-
assert.deepEqual(relResult.data[0], resourceIdentifier('authors', author1Id))
|
|
210
|
-
assert.deepEqual(relResult.data[1], resourceIdentifier('authors', author3Id))
|
|
208
|
+
assert.equal(relResult.data.length, 2, 'Should have 2 authors remaining')
|
|
209
|
+
assert.deepEqual(relResult.data[0], resourceIdentifier('authors', author1Id))
|
|
210
|
+
assert.deepEqual(relResult.data[1], resourceIdentifier('authors', author3Id))
|
|
211
211
|
|
|
212
212
|
// Verify pivot table
|
|
213
|
-
pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
214
|
-
assert.equal(pivotCount, 2, 'Should have 2 pivot records')
|
|
215
|
-
})
|
|
213
|
+
pivotCount = await countRecords(knex, 'basic_book_authors')
|
|
214
|
+
assert.equal(pivotCount, 2, 'Should have 2 pivot records')
|
|
215
|
+
})
|
|
216
216
|
|
|
217
217
|
it('should handle errors when relationship does not exist', async () => {
|
|
218
218
|
await assert.rejects(
|
|
@@ -223,8 +223,8 @@ describe('Relationship Endpoints Plugin', () => {
|
|
|
223
223
|
{
|
|
224
224
|
message: /Relationship 'nonexistent' not found/
|
|
225
225
|
}
|
|
226
|
-
)
|
|
227
|
-
})
|
|
226
|
+
)
|
|
227
|
+
})
|
|
228
228
|
|
|
229
229
|
it('should handle errors when posting to belongsTo relationship', async () => {
|
|
230
230
|
await assert.rejects(
|
|
@@ -238,8 +238,8 @@ describe('Relationship Endpoints Plugin', () => {
|
|
|
238
238
|
{
|
|
239
239
|
message: /Cannot POST to to-one relationship/
|
|
240
240
|
}
|
|
241
|
-
)
|
|
242
|
-
})
|
|
241
|
+
)
|
|
242
|
+
})
|
|
243
243
|
|
|
244
244
|
it('should work with inverse relationship (authors to books)', async () => {
|
|
245
245
|
// First add the relationship from book side
|
|
@@ -249,113 +249,113 @@ describe('Relationship Endpoints Plugin', () => {
|
|
|
249
249
|
relationshipData: [
|
|
250
250
|
resourceIdentifier('authors', author1Id)
|
|
251
251
|
]
|
|
252
|
-
})
|
|
252
|
+
})
|
|
253
253
|
|
|
254
254
|
// Now check from author side
|
|
255
255
|
const relResult = await api.resources.authors.getRelationship({
|
|
256
256
|
id: author1Id,
|
|
257
257
|
relationshipName: 'books'
|
|
258
|
-
})
|
|
258
|
+
})
|
|
259
259
|
|
|
260
|
-
assert.equal(relResult.data.length, 1, 'Author should have 1 book')
|
|
261
|
-
assert.deepEqual(relResult.data[0], resourceIdentifier('books', book1Id))
|
|
260
|
+
assert.equal(relResult.data.length, 1, 'Author should have 1 book')
|
|
261
|
+
assert.deepEqual(relResult.data[0], resourceIdentifier('books', book1Id))
|
|
262
262
|
|
|
263
263
|
// Get related books
|
|
264
264
|
const booksResult = await api.resources.authors.getRelated({
|
|
265
265
|
id: author1Id,
|
|
266
266
|
relationshipName: 'books'
|
|
267
|
-
})
|
|
267
|
+
})
|
|
268
268
|
|
|
269
|
-
validateJsonApiStructure(booksResult, true)
|
|
270
|
-
assert.equal(booksResult.data.length, 1)
|
|
271
|
-
assert.equal(booksResult.data[0].attributes.title, 'Test Book')
|
|
272
|
-
})
|
|
273
|
-
})
|
|
269
|
+
validateJsonApiStructure(booksResult, true)
|
|
270
|
+
assert.equal(booksResult.data.length, 1)
|
|
271
|
+
assert.equal(booksResult.data[0].attributes.title, 'Test Book')
|
|
272
|
+
})
|
|
273
|
+
})
|
|
274
274
|
|
|
275
275
|
describe('One-to-Many Relationship Endpoints', () => {
|
|
276
|
-
let countryId
|
|
277
|
-
let publisherId
|
|
278
|
-
let book1Id
|
|
279
|
-
let book2Id
|
|
276
|
+
let countryId
|
|
277
|
+
let publisherId
|
|
278
|
+
let book1Id
|
|
279
|
+
let book2Id
|
|
280
280
|
|
|
281
281
|
beforeEach(async () => {
|
|
282
282
|
await cleanTables(knex, [
|
|
283
283
|
'basic_countries', 'basic_publishers', 'basic_authors', 'basic_books', 'basic_book_authors'
|
|
284
|
-
])
|
|
284
|
+
])
|
|
285
285
|
|
|
286
286
|
// Create test data
|
|
287
|
-
const countryDoc = createJsonApiDocument('countries', { name: 'USA', code: 'US' })
|
|
287
|
+
const countryDoc = createJsonApiDocument('countries', { name: 'USA', code: 'US' })
|
|
288
288
|
const countryResult = await api.resources.countries.post({
|
|
289
289
|
inputRecord: countryDoc,
|
|
290
290
|
simplified: false
|
|
291
|
-
})
|
|
292
|
-
countryId = countryResult.data.id
|
|
291
|
+
})
|
|
292
|
+
countryId = countryResult.data.id
|
|
293
293
|
|
|
294
294
|
// Create a publisher
|
|
295
295
|
const publisherDoc = createJsonApiDocument('publishers',
|
|
296
296
|
{ name: 'Test Publisher' },
|
|
297
297
|
{ country: createRelationship(resourceIdentifier('countries', countryId)) }
|
|
298
|
-
)
|
|
298
|
+
)
|
|
299
299
|
const publisherResult = await api.resources.publishers.post({
|
|
300
300
|
inputRecord: publisherDoc,
|
|
301
301
|
simplified: false
|
|
302
|
-
})
|
|
303
|
-
publisherId = publisherResult.data.id
|
|
302
|
+
})
|
|
303
|
+
publisherId = publisherResult.data.id
|
|
304
304
|
|
|
305
305
|
// Create books
|
|
306
306
|
const book1Doc = createJsonApiDocument('books',
|
|
307
307
|
{ title: 'Book One' },
|
|
308
|
-
{
|
|
308
|
+
{
|
|
309
309
|
country: createRelationship(resourceIdentifier('countries', countryId)),
|
|
310
310
|
publisher: createRelationship(resourceIdentifier('publishers', publisherId))
|
|
311
311
|
}
|
|
312
|
-
)
|
|
312
|
+
)
|
|
313
313
|
const book1Result = await api.resources.books.post({
|
|
314
314
|
inputRecord: book1Doc,
|
|
315
315
|
simplified: false
|
|
316
|
-
})
|
|
317
|
-
book1Id = book1Result.data.id
|
|
316
|
+
})
|
|
317
|
+
book1Id = book1Result.data.id
|
|
318
318
|
|
|
319
319
|
const book2Doc = createJsonApiDocument('books',
|
|
320
320
|
{ title: 'Book Two' },
|
|
321
|
-
{
|
|
321
|
+
{
|
|
322
322
|
country: createRelationship(resourceIdentifier('countries', countryId)),
|
|
323
323
|
publisher: createRelationship(resourceIdentifier('publishers', publisherId))
|
|
324
324
|
}
|
|
325
|
-
)
|
|
325
|
+
)
|
|
326
326
|
const book2Result = await api.resources.books.post({
|
|
327
327
|
inputRecord: book2Doc,
|
|
328
328
|
simplified: false
|
|
329
|
-
})
|
|
330
|
-
book2Id = book2Result.data.id
|
|
331
|
-
})
|
|
329
|
+
})
|
|
330
|
+
book2Id = book2Result.data.id
|
|
331
|
+
})
|
|
332
332
|
|
|
333
333
|
it('should get one-to-many relationship data', async () => {
|
|
334
334
|
const result = await api.resources.publishers.getRelationship({
|
|
335
335
|
id: publisherId,
|
|
336
336
|
relationshipName: 'books'
|
|
337
|
-
})
|
|
337
|
+
})
|
|
338
338
|
|
|
339
|
-
assert(result.links, 'Should have links')
|
|
340
|
-
assert.equal(result.links.self, `/publishers/${publisherId}/relationships/books`)
|
|
341
|
-
assert.equal(result.links.related, `/publishers/${publisherId}/books`)
|
|
342
|
-
assert(Array.isArray(result.data), 'Data should be an array')
|
|
343
|
-
assert.equal(result.data.length, 2, 'Should have 2 books')
|
|
344
|
-
})
|
|
339
|
+
assert(result.links, 'Should have links')
|
|
340
|
+
assert.equal(result.links.self, `/publishers/${publisherId}/relationships/books`)
|
|
341
|
+
assert.equal(result.links.related, `/publishers/${publisherId}/books`)
|
|
342
|
+
assert(Array.isArray(result.data), 'Data should be an array')
|
|
343
|
+
assert.equal(result.data.length, 2, 'Should have 2 books')
|
|
344
|
+
})
|
|
345
345
|
|
|
346
346
|
it('should get related resources for one-to-many', async () => {
|
|
347
347
|
const result = await api.resources.publishers.getRelated({
|
|
348
348
|
id: publisherId,
|
|
349
349
|
relationshipName: 'books'
|
|
350
|
-
})
|
|
350
|
+
})
|
|
351
351
|
|
|
352
|
-
validateJsonApiStructure(result, true)
|
|
353
|
-
assert.equal(result.data.length, 2)
|
|
354
|
-
const titles = result.data.map(b => b.attributes.title).sort()
|
|
355
|
-
assert.deepEqual(titles, ['Book One', 'Book Two'])
|
|
356
|
-
})
|
|
352
|
+
validateJsonApiStructure(result, true)
|
|
353
|
+
assert.equal(result.data.length, 2)
|
|
354
|
+
const titles = result.data.map(b => b.attributes.title).sort()
|
|
355
|
+
assert.deepEqual(titles, ['Book One', 'Book Two'])
|
|
356
|
+
})
|
|
357
357
|
|
|
358
358
|
// Note: belongsTo relationships (like 'publisher') are not accessible via relationship endpoints
|
|
359
359
|
// They are foreign key fields, not true JSON:API relationships
|
|
360
|
-
})
|
|
361
|
-
})
|
|
360
|
+
})
|
|
361
|
+
})
|