1
1
import { assert } from '@ember/debug' ;
2
2
3
3
import { DEBUG } from '@ember-data/env' ;
4
+ import type Store from '@ember-data/store' ;
5
+ import type { BaseFinderOptions } from '@ember-data/store/-types/q/store' ;
6
+ import type { StableRecordIdentifier } from '@warp-drive/core-types' ;
7
+ import type { RelationshipSchema } from '@warp-drive/core-types/schema' ;
8
+ import type { ExistingResourceObject , JsonApiDocument } from '@warp-drive/core-types/spec/raw' ;
4
9
10
+ import { upgradeStore } from '../-private' ;
5
11
import { iterateData , payloadIsNotBlank } from './legacy-data-utils' ;
12
+ import type { MinimumAdapterInterface } from './minimum-adapter-interface' ;
6
13
import { normalizeResponseHelper } from './serializer-response' ;
7
14
8
- export function _findHasMany ( adapter , store , identifier , link , relationship , options ) {
9
- let promise = Promise . resolve ( ) . then ( ( ) => {
15
+ export function _findHasMany (
16
+ adapter : MinimumAdapterInterface ,
17
+ store : Store ,
18
+ identifier : StableRecordIdentifier ,
19
+ link : string | null | { href : string } ,
20
+ relationship : RelationshipSchema ,
21
+ options : BaseFinderOptions
22
+ ) {
23
+ upgradeStore ( store ) ;
24
+ const promise = Promise . resolve ( ) . then ( ( ) => {
10
25
const snapshot = store . _fetchManager . createSnapshot ( identifier , options ) ;
11
26
const useLink = ! link || typeof link === 'string' ;
12
27
const relatedLink = useLink ? link : link . href ;
28
+ assert (
29
+ `Attempted to load a hasMany relationship from a specified 'link' in the original payload, but the specified link is empty. You must provide a valid 'link' in the original payload to use 'findHasMany'` ,
30
+ relatedLink
31
+ ) ;
32
+ assert (
33
+ `Expected the adapter to implement 'findHasMany' but it does not` ,
34
+ typeof adapter . findHasMany === 'function'
35
+ ) ;
13
36
return adapter . findHasMany ( store , snapshot , relatedLink , relationship ) ;
14
37
} ) ;
15
38
16
- promise = promise . then (
17
- ( adapterPayload ) => {
18
- assert (
19
- `You made a 'findHasMany' request for a ${ identifier . type } 's '${ relationship . name } ' relationship, using link '${ link } ' , but the adapter's response did not have any data` ,
20
- payloadIsNotBlank ( adapterPayload )
21
- ) ;
22
- const modelClass = store . modelFor ( relationship . type ) ;
23
-
24
- const serializer = store . serializerFor ( relationship . type ) ;
25
- let payload = normalizeResponseHelper ( serializer , store , modelClass , adapterPayload , null , 'findHasMany' ) ;
39
+ return promise . then ( ( adapterPayload ) => {
40
+ assert (
41
+ `You made a 'findHasMany' request for a ${ identifier . type } 's '${
42
+ relationship . name
43
+ } ' relationship, using link '${ JSON . stringify ( link ) } ' , but the adapter's response did not have any data`,
44
+ payloadIsNotBlank ( adapterPayload )
45
+ ) ;
46
+ const modelClass = store . modelFor ( relationship . type ) ;
26
47
27
- assert (
28
- `fetched the hasMany relationship '${ relationship . name } ' for ${ identifier . type } :${ identifier . id } with link '${ link } ', but no data member is present in the response. If no data exists, the response should set { data: [] }` ,
29
- 'data' in payload && Array . isArray ( payload . data )
30
- ) ;
48
+ const serializer = store . serializerFor ( relationship . type ) ;
49
+ let payload = normalizeResponseHelper ( serializer , store , modelClass , adapterPayload , null , 'findHasMany' ) ;
31
50
32
- payload = syncRelationshipDataFromLink ( store , payload , identifier , relationship ) ;
33
- return store . _push ( payload , true ) ;
34
- } ,
35
- null ,
36
- `DS: Extract payload of '${ identifier . type } ' : hasMany '${ relationship . type } '`
37
- ) ;
51
+ assert (
52
+ `fetched the hasMany relationship '${ relationship . name } ' for ${ identifier . type } :${
53
+ identifier . id
54
+ } with link '${ JSON . stringify (
55
+ link
56
+ ) } ', but no data member is present in the response. If no data exists, the response should set { data: [] }`,
57
+ 'data' in payload && Array . isArray ( payload . data )
58
+ ) ;
38
59
39
- return promise ;
60
+ payload = syncRelationshipDataFromLink ( store , payload , identifier as ResourceIdentity , relationship ) ;
61
+ return store . _push ( payload , true ) ;
62
+ } , null ) ;
40
63
}
41
64
42
- export function _findBelongsTo ( store , identifier , link , relationship , options ) {
43
- let promise = Promise . resolve ( ) . then ( ( ) => {
65
+ export function _findBelongsTo (
66
+ store : Store ,
67
+ identifier : StableRecordIdentifier ,
68
+ link : string | null | { href : string } ,
69
+ relationship : RelationshipSchema ,
70
+ options : BaseFinderOptions
71
+ ) {
72
+ upgradeStore ( store ) ;
73
+ const promise = Promise . resolve ( ) . then ( ( ) => {
44
74
const adapter = store . adapterFor ( identifier . type ) ;
45
75
assert ( `You tried to load a belongsTo relationship but you have no adapter (for ${ identifier . type } )` , adapter ) ;
46
76
assert (
@@ -50,34 +80,35 @@ export function _findBelongsTo(store, identifier, link, relationship, options) {
50
80
const snapshot = store . _fetchManager . createSnapshot ( identifier , options ) ;
51
81
const useLink = ! link || typeof link === 'string' ;
52
82
const relatedLink = useLink ? link : link . href ;
83
+ assert (
84
+ `Attempted to load a belongsTo relationship from a specified 'link' in the original payload, but the specified link is empty. You must provide a valid 'link' in the original payload to use 'findBelongsTo'` ,
85
+ relatedLink
86
+ ) ;
53
87
return adapter . findBelongsTo ( store , snapshot , relatedLink , relationship ) ;
54
88
} ) ;
55
89
56
- promise = promise . then (
57
- ( adapterPayload ) => {
58
- const modelClass = store . modelFor ( relationship . type ) ;
59
- const serializer = store . serializerFor ( relationship . type ) ;
60
- let payload = normalizeResponseHelper ( serializer , store , modelClass , adapterPayload , null , 'findBelongsTo' ) ;
61
-
62
- assert (
63
- `fetched the belongsTo relationship '${ relationship . name } ' for ${ identifier . type } :${ identifier . id } with link '${ link } ', but no data member is present in the response. If no data exists, the response should set { data: null }` ,
64
- 'data' in payload &&
65
- ( payload . data === null || ( typeof payload . data === 'object' && ! Array . isArray ( payload . data ) ) )
66
- ) ;
90
+ return promise . then ( ( adapterPayload ) => {
91
+ const modelClass = store . modelFor ( relationship . type ) ;
92
+ const serializer = store . serializerFor ( relationship . type ) ;
93
+ let payload = normalizeResponseHelper ( serializer , store , modelClass , adapterPayload , null , 'findBelongsTo' ) ;
67
94
68
- if ( ! payload . data && ! payload . links && ! payload . meta ) {
69
- return null ;
70
- }
95
+ assert (
96
+ `fetched the belongsTo relationship '${ relationship . name } ' for ${ identifier . type } :${
97
+ identifier . id
98
+ } with link '${ JSON . stringify (
99
+ link
100
+ ) } ', but no data member is present in the response. If no data exists, the response should set { data: null }`,
101
+ 'data' in payload && ( payload . data === null || ( typeof payload . data === 'object' && ! Array . isArray ( payload . data ) ) )
102
+ ) ;
71
103
72
- payload = syncRelationshipDataFromLink ( store , payload , identifier , relationship ) ;
104
+ if ( ! payload . data && ! payload . links && ! payload . meta ) {
105
+ return null ;
106
+ }
73
107
74
- return store . _push ( payload , true ) ;
75
- } ,
76
- null ,
77
- `DS: Extract payload of ${ identifier . type } : ${ relationship . type } `
78
- ) ;
108
+ payload = syncRelationshipDataFromLink ( store , payload , identifier as ResourceIdentity , relationship ) ;
79
109
80
- return promise ;
110
+ return store . _push ( payload , true ) ;
111
+ } , null ) ;
81
112
}
82
113
83
114
// sync
@@ -86,7 +117,12 @@ export function _findBelongsTo(store, identifier, link, relationship, options) {
86
117
// assert that record.relationships[inverse] is either undefined (so we can fix it)
87
118
// or provide a data: {id, type} that matches the record that requested it
88
119
// return the relationship data for the parent
89
- function syncRelationshipDataFromLink ( store , payload , parentIdentifier , relationship ) {
120
+ function syncRelationshipDataFromLink (
121
+ store : Store ,
122
+ payload : JsonApiDocument ,
123
+ parentIdentifier : ResourceIdentity ,
124
+ relationship : RelationshipSchema
125
+ ) {
90
126
// ensure the right hand side (incoming payload) points to the parent record that
91
127
// requested this relationship
92
128
const relationshipData = payload . data
@@ -97,7 +133,7 @@ function syncRelationshipDataFromLink(store, payload, parentIdentifier, relation
97
133
} )
98
134
: null ;
99
135
100
- const relatedDataHash = { } ;
136
+ const relatedDataHash = { } as JsonApiDocument ;
101
137
102
138
if ( 'meta' in payload ) {
103
139
relatedDataHash . meta = payload . meta ;
@@ -127,7 +163,16 @@ function syncRelationshipDataFromLink(store, payload, parentIdentifier, relation
127
163
return payload ;
128
164
}
129
165
130
- function ensureRelationshipIsSetToParent ( payload , parentIdentifier , store , parentRelationship , index ) {
166
+ type ResourceIdentity = { id : string ; type : string } ;
167
+ type RelationshipData = ResourceIdentity | ResourceIdentity [ ] | null ;
168
+
169
+ function ensureRelationshipIsSetToParent (
170
+ payload : ExistingResourceObject ,
171
+ parentIdentifier : ResourceIdentity ,
172
+ store : Store ,
173
+ parentRelationship : RelationshipSchema ,
174
+ index : number
175
+ ) {
131
176
const { id, type } = payload ;
132
177
133
178
if ( ! payload . relationships ) {
@@ -139,14 +184,14 @@ function ensureRelationshipIsSetToParent(payload, parentIdentifier, store, paren
139
184
if ( inverse ) {
140
185
const { inverseKey, kind } = inverse ;
141
186
142
- const relationshipData = relationships [ inverseKey ] && relationships [ inverseKey ] . data ;
187
+ const relationshipData = relationships [ inverseKey ] ? .data as RelationshipData | undefined ;
143
188
144
189
if ( DEBUG ) {
145
190
if (
146
191
typeof relationshipData !== 'undefined' &&
147
192
! relationshipDataPointsToParent ( relationshipData , parentIdentifier )
148
193
) {
149
- const inspect = function inspect ( thing ) {
194
+ const inspect = function inspect ( thing : unknown ) {
150
195
return `'${ JSON . stringify ( thing ) } '` ;
151
196
} ;
152
197
const quotedType = inspect ( type ) ;
@@ -159,7 +204,8 @@ function ensureRelationshipIsSetToParent(payload, parentIdentifier, store, paren
159
204
const got = inspect ( relationshipData ) ;
160
205
const prefix = typeof index === 'number' ? `data[${ index } ]` : `data` ;
161
206
const path = `${ prefix } .relationships.${ inverseKey } .data` ;
162
- const other = relationshipData ? `<${ relationshipData . type } :${ relationshipData . id } >` : null ;
207
+ const data = Array . isArray ( relationshipData ) ? relationshipData [ 0 ] : relationshipData ;
208
+ const other = data ? `<${ data . type } :${ data . id } >` : null ;
163
209
const relationshipFetched = `${ expectedModel } .${ parentRelationship . kind } ("${ parentRelationship . name } ")` ;
164
210
const includedRecord = `<${ type } :${ id } >` ;
165
211
const message = [
@@ -176,12 +222,12 @@ function ensureRelationshipIsSetToParent(payload, parentIdentifier, store, paren
176
222
177
223
if ( kind !== 'hasMany' || typeof relationshipData !== 'undefined' ) {
178
224
relationships [ inverseKey ] = relationships [ inverseKey ] || { } ;
179
- relationships [ inverseKey ] . data = fixRelationshipData ( relationshipData , kind , parentIdentifier ) ;
225
+ relationships [ inverseKey ] . data = fixRelationshipData ( relationshipData ?? null , kind , parentIdentifier ) ;
180
226
}
181
227
}
182
228
}
183
229
184
- function inverseForRelationship ( store , identifier , key ) {
230
+ function inverseForRelationship ( store : Store , identifier : { type : string ; id ?: string } , key : string ) {
185
231
const definition = store . getSchemaDefinitionService ( ) . relationshipsDefinitionFor ( identifier ) [ key ] ;
186
232
if ( ! definition ) {
187
233
return null ;
@@ -195,7 +241,12 @@ function inverseForRelationship(store, identifier, key) {
195
241
return definition . options . inverse ;
196
242
}
197
243
198
- function getInverse ( store , parentIdentifier , parentRelationship , type ) {
244
+ function getInverse (
245
+ store : Store ,
246
+ parentIdentifier : ResourceIdentity ,
247
+ parentRelationship : RelationshipSchema ,
248
+ type : string
249
+ ) {
199
250
const { name : lhs_relationshipName } = parentRelationship ;
200
251
const { type : parentType } = parentIdentifier ;
201
252
const inverseKey = inverseForRelationship ( store , { type : parentType } , lhs_relationshipName ) ;
@@ -210,7 +261,7 @@ function getInverse(store, parentIdentifier, parentRelationship, type) {
210
261
}
211
262
}
212
263
213
- function relationshipDataPointsToParent ( relationshipData , identifier ) {
264
+ function relationshipDataPointsToParent ( relationshipData : RelationshipData , identifier : ResourceIdentity ) : boolean {
214
265
if ( relationshipData === null ) {
215
266
return false ;
216
267
}
@@ -232,37 +283,44 @@ function relationshipDataPointsToParent(relationshipData, identifier) {
232
283
return false ;
233
284
}
234
285
235
- function fixRelationshipData ( relationshipData , relationshipKind , { id, type } ) {
286
+ function fixRelationshipData (
287
+ relationshipData : RelationshipData ,
288
+ relationshipKind : 'hasMany' | 'belongsTo' ,
289
+ { id, type } : ResourceIdentity
290
+ ) {
236
291
const parentRelationshipData = {
237
292
id,
238
293
type,
239
294
} ;
240
295
241
- let payload ;
296
+ let payload : { type : string ; id : string } | { type : string ; id : string } [ ] | null = null ;
242
297
243
298
if ( relationshipKind === 'hasMany' ) {
244
- payload = relationshipData || [ ] ;
299
+ const relData = ( relationshipData as { type : string ; id : string } [ ] ) || [ ] ;
245
300
if ( relationshipData ) {
301
+ assert ( 'expected the relationship data to be an array' , Array . isArray ( relationshipData ) ) ;
246
302
// these arrays could be massive so this is better than filter
247
303
// Note: this is potentially problematic if type/id are not in the
248
304
// same state of normalization.
249
305
const found = relationshipData . find ( ( v ) => {
250
306
return v . type === parentRelationshipData . type && v . id === parentRelationshipData . id ;
251
307
} ) ;
252
308
if ( ! found ) {
253
- payload . push ( parentRelationshipData ) ;
309
+ relData . push ( parentRelationshipData ) ;
254
310
}
255
311
} else {
256
- payload . push ( parentRelationshipData ) ;
312
+ relData . push ( parentRelationshipData ) ;
257
313
}
314
+ payload = relData ;
258
315
} else {
259
- payload = relationshipData || { } ;
260
- Object . assign ( payload , parentRelationshipData ) ;
316
+ const relData = ( relationshipData as { type : string ; id : string } ) || { } ;
317
+ Object . assign ( relData , parentRelationshipData ) ;
318
+ payload = relData ;
261
319
}
262
320
263
321
return payload ;
264
322
}
265
323
266
- function validateRelationshipEntry ( { id } , { id : parentModelID } ) {
267
- return id && id . toString ( ) === parentModelID ;
324
+ function validateRelationshipEntry ( { id } : ResourceIdentity , { id : parentModelID } : ResourceIdentity ) : boolean {
325
+ return ! ! id && id . toString ( ) === parentModelID ;
268
326
}
0 commit comments