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,34 +1,34 @@
|
|
|
1
|
-
const STORAGE_MODE = process.env.JSON_REST_API_STORAGE === 'anyapi' ? 'anyapi' : 'knex'
|
|
2
|
-
const DEFAULT_TENANT = 'default'
|
|
1
|
+
const STORAGE_MODE = process.env.JSON_REST_API_STORAGE === 'anyapi' ? 'anyapi' : 'knex'
|
|
2
|
+
const DEFAULT_TENANT = 'default'
|
|
3
3
|
|
|
4
|
-
const tableMetadata = new Map()
|
|
5
|
-
let resetMetadataFn = null
|
|
6
|
-
const linkMappings = new Map()
|
|
7
|
-
let currentTenant = DEFAULT_TENANT
|
|
4
|
+
const tableMetadata = new Map()
|
|
5
|
+
let resetMetadataFn = null
|
|
6
|
+
const linkMappings = new Map()
|
|
7
|
+
let currentTenant = DEFAULT_TENANT
|
|
8
8
|
|
|
9
9
|
export const storageMode = {
|
|
10
10
|
mode: STORAGE_MODE,
|
|
11
|
-
isAnyApi() {
|
|
12
|
-
return STORAGE_MODE === 'anyapi'
|
|
11
|
+
isAnyApi () {
|
|
12
|
+
return STORAGE_MODE === 'anyapi'
|
|
13
13
|
},
|
|
14
14
|
defaultTenant: DEFAULT_TENANT,
|
|
15
|
-
get currentTenant() {
|
|
16
|
-
return currentTenant
|
|
15
|
+
get currentTenant () {
|
|
16
|
+
return currentTenant
|
|
17
17
|
},
|
|
18
|
-
registerReset(fn) {
|
|
19
|
-
resetMetadataFn = fn
|
|
18
|
+
registerReset (fn) {
|
|
19
|
+
resetMetadataFn = fn
|
|
20
20
|
},
|
|
21
|
-
setCurrentTenant(tenantId) {
|
|
22
|
-
currentTenant = tenantId || DEFAULT_TENANT
|
|
21
|
+
setCurrentTenant (tenantId) {
|
|
22
|
+
currentTenant = tenantId || DEFAULT_TENANT
|
|
23
23
|
},
|
|
24
|
-
registerTable(tableName, resourceName, tenantId = currentTenant || DEFAULT_TENANT) {
|
|
25
|
-
if (!tableName || !resourceName) return
|
|
24
|
+
registerTable (tableName, resourceName, tenantId = currentTenant || DEFAULT_TENANT) {
|
|
25
|
+
if (!tableName || !resourceName) return
|
|
26
26
|
tableMetadata.set(tableName, {
|
|
27
27
|
resource: resourceName,
|
|
28
28
|
tenantId: tenantId || DEFAULT_TENANT,
|
|
29
|
-
})
|
|
29
|
+
})
|
|
30
30
|
},
|
|
31
|
-
registerLink(
|
|
31
|
+
registerLink (
|
|
32
32
|
tableName,
|
|
33
33
|
ownerResource,
|
|
34
34
|
relationshipName,
|
|
@@ -36,30 +36,30 @@ export const storageMode = {
|
|
|
36
36
|
inverseRelationshipKey,
|
|
37
37
|
tenantId = currentTenant || DEFAULT_TENANT
|
|
38
38
|
) {
|
|
39
|
-
if (!tableName || !ownerResource || !relationshipName) return
|
|
39
|
+
if (!tableName || !ownerResource || !relationshipName) return
|
|
40
40
|
linkMappings.set(tableName, {
|
|
41
41
|
ownerResource,
|
|
42
42
|
relationshipName,
|
|
43
43
|
relationshipKey,
|
|
44
44
|
inverseRelationshipKey,
|
|
45
45
|
tenantId: tenantId || DEFAULT_TENANT,
|
|
46
|
-
})
|
|
46
|
+
})
|
|
47
47
|
},
|
|
48
|
-
getResourceForTable(tableName) {
|
|
49
|
-
return tableMetadata.get(tableName)?.resource
|
|
48
|
+
getResourceForTable (tableName) {
|
|
49
|
+
return tableMetadata.get(tableName)?.resource
|
|
50
50
|
},
|
|
51
|
-
getTenantForTable(tableName) {
|
|
52
|
-
return tableMetadata.get(tableName)?.tenantId || DEFAULT_TENANT
|
|
51
|
+
getTenantForTable (tableName) {
|
|
52
|
+
return tableMetadata.get(tableName)?.tenantId || DEFAULT_TENANT
|
|
53
53
|
},
|
|
54
|
-
getLinkInfo(tableName) {
|
|
55
|
-
return linkMappings.get(tableName)
|
|
54
|
+
getLinkInfo (tableName) {
|
|
55
|
+
return linkMappings.get(tableName)
|
|
56
56
|
},
|
|
57
|
-
clearRegistry() {
|
|
58
|
-
tableMetadata.clear()
|
|
59
|
-
linkMappings.clear()
|
|
60
|
-
currentTenant = DEFAULT_TENANT
|
|
57
|
+
clearRegistry () {
|
|
58
|
+
tableMetadata.clear()
|
|
59
|
+
linkMappings.clear()
|
|
60
|
+
currentTenant = DEFAULT_TENANT
|
|
61
61
|
if (resetMetadataFn) {
|
|
62
|
-
resetMetadataFn()
|
|
62
|
+
resetMetadataFn()
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
|
-
}
|
|
65
|
+
}
|
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
import assert from 'node:assert/strict'
|
|
2
|
-
import { storageMode } from './storage-mode.js'
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { storageMode } from './storage-mode.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Validates that a response follows JSON:API structure
|
|
6
6
|
*/
|
|
7
|
-
export function validateJsonApiStructure(response, isCollection = false) {
|
|
8
|
-
assert(response, 'Response should exist')
|
|
9
|
-
|
|
7
|
+
export function validateJsonApiStructure (response, isCollection = false) {
|
|
8
|
+
assert(response, 'Response should exist')
|
|
9
|
+
|
|
10
10
|
if (isCollection) {
|
|
11
|
-
assert(Array.isArray(response.data), 'Response data should be an array for collections')
|
|
11
|
+
assert(Array.isArray(response.data), 'Response data should be an array for collections')
|
|
12
12
|
} else {
|
|
13
|
-
assert(response.data && typeof response.data === 'object', 'Response data should be an object')
|
|
14
|
-
assert(!Array.isArray(response.data), 'Response data should not be an array for single resources')
|
|
13
|
+
assert(response.data && typeof response.data === 'object', 'Response data should be an object')
|
|
14
|
+
assert(!Array.isArray(response.data), 'Response data should not be an array for single resources')
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
// Validate each resource object
|
|
18
|
-
const resources = isCollection ? response.data : [response.data]
|
|
18
|
+
const resources = isCollection ? response.data : [response.data]
|
|
19
19
|
resources.forEach(resource => {
|
|
20
|
-
assert(resource.type, 'Resource should have a type')
|
|
21
|
-
assert(resource.id, 'Resource should have an id')
|
|
22
|
-
assert(resource.attributes && typeof resource.attributes === 'object', 'Resource should have attributes object')
|
|
23
|
-
|
|
20
|
+
assert(resource.type, 'Resource should have a type')
|
|
21
|
+
assert(resource.id, 'Resource should have an id')
|
|
22
|
+
assert(resource.attributes && typeof resource.attributes === 'object', 'Resource should have attributes object')
|
|
23
|
+
|
|
24
24
|
if (resource.relationships) {
|
|
25
|
-
assert(typeof resource.relationships === 'object', 'Relationships should be an object')
|
|
25
|
+
assert(typeof resource.relationships === 'object', 'Relationships should be an object')
|
|
26
26
|
Object.values(resource.relationships).forEach(rel => {
|
|
27
|
-
assert(rel.data !== undefined || rel.links !== undefined || rel.meta !== undefined,
|
|
28
|
-
'Relationship should have data, links, or meta')
|
|
29
|
-
})
|
|
27
|
+
assert(rel.data !== undefined || rel.links !== undefined || rel.meta !== undefined,
|
|
28
|
+
'Relationship should have data, links, or meta')
|
|
29
|
+
})
|
|
30
30
|
}
|
|
31
|
-
})
|
|
32
|
-
|
|
31
|
+
})
|
|
32
|
+
|
|
33
33
|
if (response.included) {
|
|
34
|
-
assert(Array.isArray(response.included), 'Included should be an array')
|
|
34
|
+
assert(Array.isArray(response.included), 'Included should be an array')
|
|
35
35
|
response.included.forEach(resource => {
|
|
36
|
-
assert(resource.type && resource.id, 'Included resources should have type and id')
|
|
37
|
-
})
|
|
36
|
+
assert(resource.type && resource.id, 'Included resources should have type and id')
|
|
37
|
+
})
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* Creates a resource identifier for JSON:API relationships
|
|
43
43
|
*/
|
|
44
|
-
export function resourceIdentifier(type, id) {
|
|
45
|
-
return { type, id: String(id) }
|
|
44
|
+
export function resourceIdentifier (type, id) {
|
|
45
|
+
return { type, id: String(id) }
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Cleans all records from the given tables
|
|
50
50
|
*/
|
|
51
|
-
export async function cleanTables(knex, tableNames) {
|
|
51
|
+
export async function cleanTables (knex, tableNames) {
|
|
52
52
|
if (storageMode.isAnyApi()) {
|
|
53
|
-
const resourceGroups = new Map()
|
|
53
|
+
const resourceGroups = new Map()
|
|
54
54
|
|
|
55
55
|
for (const table of tableNames) {
|
|
56
|
-
const resource = storageMode.getResourceForTable(table)
|
|
57
|
-
if (!resource) continue
|
|
58
|
-
const tenantId = storageMode.getTenantForTable(table)
|
|
59
|
-
const key = `${tenantId}::${resource}
|
|
56
|
+
const resource = storageMode.getResourceForTable(table)
|
|
57
|
+
if (!resource) continue
|
|
58
|
+
const tenantId = storageMode.getTenantForTable(table)
|
|
59
|
+
const key = `${tenantId}::${resource}`
|
|
60
60
|
if (!resourceGroups.has(key)) {
|
|
61
|
-
resourceGroups.set(key, { resource, tenantId })
|
|
61
|
+
resourceGroups.set(key, { resource, tenantId })
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
for (const { resource, tenantId } of resourceGroups.values()) {
|
|
66
66
|
await knex('any_records')
|
|
67
67
|
.where({ tenant_id: tenantId, resource })
|
|
68
|
-
.delete()
|
|
68
|
+
.delete()
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
for (const table of tableNames) {
|
|
72
|
-
const linkInfo = storageMode.getLinkInfo(table)
|
|
72
|
+
const linkInfo = storageMode.getLinkInfo(table)
|
|
73
73
|
if (linkInfo) {
|
|
74
|
-
const tenantId = linkInfo.tenantId || storageMode.defaultTenant
|
|
75
|
-
const relationshipKey = linkInfo.relationshipKey
|
|
76
|
-
|
|
74
|
+
const tenantId = linkInfo.tenantId || storageMode.defaultTenant
|
|
75
|
+
const relationshipKey = linkInfo.relationshipKey ||
|
|
76
|
+
`${tenantId}:${linkInfo.ownerResource}:${linkInfo.relationshipName}`
|
|
77
77
|
await knex('any_links')
|
|
78
78
|
.where({ tenant_id: tenantId })
|
|
79
79
|
.andWhere((builder) => {
|
|
80
80
|
builder.where('relationship', relationshipKey)
|
|
81
|
-
.orWhere('inverse_relationship', relationshipKey)
|
|
81
|
+
.orWhere('inverse_relationship', relationshipKey)
|
|
82
82
|
if (linkInfo.inverseRelationshipKey) {
|
|
83
83
|
builder.orWhere('relationship', linkInfo.inverseRelationshipKey)
|
|
84
|
-
.orWhere('inverse_relationship', linkInfo.inverseRelationshipKey)
|
|
84
|
+
.orWhere('inverse_relationship', linkInfo.inverseRelationshipKey)
|
|
85
85
|
}
|
|
86
86
|
})
|
|
87
87
|
.delete()
|
|
88
|
-
.catch(() => {})
|
|
88
|
+
.catch(() => {})
|
|
89
89
|
}
|
|
90
90
|
try {
|
|
91
|
-
await knex(table).delete()
|
|
91
|
+
await knex(table).delete()
|
|
92
92
|
} catch (error) {
|
|
93
93
|
// Ignore missing tables in anyapi mode
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
} else {
|
|
97
97
|
for (const table of tableNames) {
|
|
98
|
-
await knex(table).delete()
|
|
98
|
+
await knex(table).delete()
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
}
|
|
@@ -103,23 +103,23 @@ export async function cleanTables(knex, tableNames) {
|
|
|
103
103
|
/**
|
|
104
104
|
* Counts records in a table
|
|
105
105
|
*/
|
|
106
|
-
export async function countRecords(knex, tableName) {
|
|
106
|
+
export async function countRecords (knex, tableName) {
|
|
107
107
|
if (storageMode.isAnyApi()) {
|
|
108
|
-
const resource = storageMode.getResourceForTable(tableName)
|
|
109
|
-
const linkInfo = storageMode.getLinkInfo(tableName)
|
|
110
|
-
const tenantId = storageMode.getTenantForTable(tableName)
|
|
108
|
+
const resource = storageMode.getResourceForTable(tableName)
|
|
109
|
+
const linkInfo = storageMode.getLinkInfo(tableName)
|
|
110
|
+
const tenantId = storageMode.getTenantForTable(tableName)
|
|
111
111
|
if (linkInfo) {
|
|
112
|
-
const relationshipKey = linkInfo.relationshipKey
|
|
113
|
-
|
|
112
|
+
const relationshipKey = linkInfo.relationshipKey ||
|
|
113
|
+
`${tenantId}:${linkInfo.ownerResource}:${linkInfo.relationshipName}`
|
|
114
114
|
const result = await knex('any_links')
|
|
115
115
|
.where({ tenant_id: tenantId })
|
|
116
116
|
.andWhere((builder) => {
|
|
117
117
|
builder.where('relationship', relationshipKey)
|
|
118
|
-
.orWhere('inverse_relationship', relationshipKey)
|
|
118
|
+
.orWhere('inverse_relationship', relationshipKey)
|
|
119
119
|
})
|
|
120
120
|
.count('* as count')
|
|
121
|
-
.first()
|
|
122
|
-
return parseInt(result?.count ?? 0, 10)
|
|
121
|
+
.first()
|
|
122
|
+
return parseInt(result?.count ?? 0, 10)
|
|
123
123
|
}
|
|
124
124
|
if (resource) {
|
|
125
125
|
const result = await knex('any_records')
|
|
@@ -128,76 +128,76 @@ export async function countRecords(knex, tableName) {
|
|
|
128
128
|
resource,
|
|
129
129
|
})
|
|
130
130
|
.count('* as count')
|
|
131
|
-
.first()
|
|
132
|
-
return parseInt(result.count)
|
|
131
|
+
.first()
|
|
132
|
+
return parseInt(result.count)
|
|
133
133
|
}
|
|
134
134
|
try {
|
|
135
|
-
const result = await knex(tableName).count('* as count').first()
|
|
136
|
-
return parseInt(result?.count || 0)
|
|
135
|
+
const result = await knex(tableName).count('* as count').first()
|
|
136
|
+
return parseInt(result?.count || 0)
|
|
137
137
|
} catch (error) {
|
|
138
|
-
return 0
|
|
138
|
+
return 0
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
const result = await knex(tableName).count('* as count').first()
|
|
143
|
-
return parseInt(result.count)
|
|
142
|
+
const result = await knex(tableName).count('* as count').first()
|
|
143
|
+
return parseInt(result.count)
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
147
|
* Helper to create a JSON:API document for POST/PUT/PATCH
|
|
148
148
|
*/
|
|
149
|
-
export function createJsonApiDocument(type, attributes, relationships = {}) {
|
|
149
|
+
export function createJsonApiDocument (type, attributes, relationships = {}) {
|
|
150
150
|
const doc = {
|
|
151
151
|
data: {
|
|
152
152
|
type,
|
|
153
153
|
attributes
|
|
154
154
|
}
|
|
155
|
-
}
|
|
156
|
-
|
|
155
|
+
}
|
|
156
|
+
|
|
157
157
|
if (Object.keys(relationships).length > 0) {
|
|
158
|
-
doc.data.relationships = relationships
|
|
158
|
+
doc.data.relationships = relationships
|
|
159
159
|
}
|
|
160
|
-
|
|
161
|
-
return doc
|
|
160
|
+
|
|
161
|
+
return doc
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/**
|
|
165
165
|
* Helper to create a relationship object
|
|
166
166
|
*/
|
|
167
|
-
export function createRelationship(resourceIdentifier) {
|
|
167
|
+
export function createRelationship (resourceIdentifier) {
|
|
168
168
|
return {
|
|
169
169
|
data: resourceIdentifier
|
|
170
|
-
}
|
|
170
|
+
}
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
/**
|
|
174
174
|
* Helper to create a to-many relationship object
|
|
175
175
|
*/
|
|
176
|
-
export function createToManyRelationship(resourceIdentifiers) {
|
|
176
|
+
export function createToManyRelationship (resourceIdentifiers) {
|
|
177
177
|
return {
|
|
178
178
|
data: resourceIdentifiers
|
|
179
|
-
}
|
|
179
|
+
}
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
/**
|
|
183
183
|
* Asserts that a resource has specific attributes
|
|
184
184
|
*/
|
|
185
|
-
export function assertResourceAttributes(resource, expectedAttributes) {
|
|
185
|
+
export function assertResourceAttributes (resource, expectedAttributes) {
|
|
186
186
|
Object.entries(expectedAttributes).forEach(([key, value]) => {
|
|
187
|
-
assert.deepEqual(resource.attributes[key], value,
|
|
188
|
-
`Attribute ${key} should equal ${JSON.stringify(value)}`)
|
|
189
|
-
})
|
|
187
|
+
assert.deepEqual(resource.attributes[key], value,
|
|
188
|
+
`Attribute ${key} should equal ${JSON.stringify(value)}`)
|
|
189
|
+
})
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
/**
|
|
193
193
|
* Asserts that a resource has a specific relationship
|
|
194
194
|
*/
|
|
195
|
-
export function assertResourceRelationship(resource, relationshipName, expectedIdentifier) {
|
|
196
|
-
assert(resource.relationships, 'Resource should have relationships')
|
|
197
|
-
assert(resource.relationships[relationshipName], `Resource should have ${relationshipName} relationship`)
|
|
195
|
+
export function assertResourceRelationship (resource, relationshipName, expectedIdentifier) {
|
|
196
|
+
assert(resource.relationships, 'Resource should have relationships')
|
|
197
|
+
assert(resource.relationships[relationshipName], `Resource should have ${relationshipName} relationship`)
|
|
198
198
|
assert.deepEqual(
|
|
199
199
|
resource.relationships[relationshipName].data,
|
|
200
200
|
expectedIdentifier,
|
|
201
201
|
`Relationship ${relationshipName} should match expected identifier`
|
|
202
|
-
)
|
|
202
|
+
)
|
|
203
203
|
}
|