1 |
|
2 |
|
3 |
|
4 | import { assert, inspect, warn } from '@ember/debug';
|
5 | import { assign } from '@ember/polyfills';
|
6 | import { run } from '@ember/runloop';
|
7 | import { isEqual } from '@ember/utils';
|
8 | import { DEBUG } from '@glimmer/env';
|
9 |
|
10 | import { RECORD_DATA_ERRORS, RECORD_DATA_STATE } from '@ember-data/canary-features';
|
11 |
|
12 | import coerceId from './coerce-id';
|
13 | import Relationships from './relationships/state/create';
|
14 |
|
15 | type RecordIdentifier = import('@ember-data/store/-private/ts-interfaces/identifier').RecordIdentifier;
|
16 | type RecordDataStoreWrapper = import('@ember-data/store/-private/ts-interfaces/record-data-store-wrapper').RecordDataStoreWrapper;
|
17 | type RelationshipRecordData = import('./ts-interfaces/relationship-record-data').RelationshipRecordData;
|
18 | type DefaultSingleResourceRelationship = import('./ts-interfaces/relationship-record-data').DefaultSingleResourceRelationship;
|
19 | type DefaultCollectionResourceRelationship = import('./ts-interfaces/relationship-record-data').DefaultCollectionResourceRelationship;
|
20 | type JsonApiResource = import('@ember-data/store/-private/ts-interfaces/record-data-json-api').JsonApiResource;
|
21 | type JsonApiValidationError = import('@ember-data/store/-private/ts-interfaces/record-data-json-api').JsonApiValidationError;
|
22 | type AttributesHash = import('@ember-data/store/-private/ts-interfaces/record-data-json-api').AttributesHash;
|
23 | type RecordData = import('@ember-data/store/-private/ts-interfaces/record-data').RecordData;
|
24 | type ChangedAttributesHash = import('@ember-data/store/-private/ts-interfaces/record-data').ChangedAttributesHash;
|
25 | type Relationship = import('./relationships/state/relationship').default;
|
26 | type ManyRelationship = import('./relationships/state/has-many').default;
|
27 | type BelongsToRelationship = import('./relationships/state/belongs-to').default;
|
28 |
|
29 | let nextBfsId = 1;
|
30 |
|
31 | export default class RecordDataDefault implements RelationshipRecordData {
|
32 | _errors?: JsonApiValidationError[];
|
33 | __relationships: Relationships | null;
|
34 | __implicitRelationships: { [key: string]: Relationship } | null;
|
35 | modelName: string;
|
36 | clientId: string;
|
37 | id: string | null;
|
38 | isDestroyed: boolean;
|
39 | _isNew: boolean;
|
40 | _bfsId: number;
|
41 | __attributes: any;
|
42 | __inFlightAttributes: any;
|
43 | __data: any;
|
44 | _scheduledDestroy: any;
|
45 | _isDeleted: boolean;
|
46 | _isDeletionCommited: boolean;
|
47 |
|
48 | constructor(private identifier: RecordIdentifier, public storeWrapper: RecordDataStoreWrapper) {
|
49 | this.modelName = identifier.type;
|
50 | this.clientId = identifier.lid;
|
51 | this.id = identifier.id;
|
52 |
|
53 | this.__relationships = null;
|
54 | this.__implicitRelationships = null;
|
55 | this.isDestroyed = false;
|
56 | this._isNew = false;
|
57 | this._isDeleted = false;
|
58 |
|
59 |
|
60 | this._bfsId = 0;
|
61 | this.reset();
|
62 | }
|
63 |
|
64 |
|
65 | getResourceIdentifier(): RecordIdentifier {
|
66 | return this.identifier;
|
67 | }
|
68 |
|
69 | pushData(data: JsonApiResource, calculateChange: boolean) {
|
70 | let changedKeys;
|
71 |
|
72 | if (this._isNew) {
|
73 | this._isNew = false;
|
74 | this.notifyStateChange();
|
75 | }
|
76 |
|
77 | if (calculateChange) {
|
78 | changedKeys = this._changedKeys(data.attributes);
|
79 | }
|
80 |
|
81 | assign(this._data, data.attributes);
|
82 | if (this.__attributes) {
|
83 |
|
84 | this._updateChangedAttributes();
|
85 | }
|
86 |
|
87 | if (data.relationships) {
|
88 | this._setupRelationships(data);
|
89 | }
|
90 |
|
91 | if (data.id) {
|
92 | this.id = coerceId(data.id);
|
93 | }
|
94 |
|
95 | return changedKeys;
|
96 | }
|
97 |
|
98 | willCommit() {
|
99 | this._inFlightAttributes = this._attributes;
|
100 | this._attributes = null;
|
101 | }
|
102 |
|
103 | hasChangedAttributes() {
|
104 | return this.__attributes !== null && Object.keys(this.__attributes).length > 0;
|
105 | }
|
106 |
|
107 | _clearErrors() {
|
108 | if (RECORD_DATA_ERRORS) {
|
109 | if (this._errors) {
|
110 | this._errors = undefined;
|
111 | this.storeWrapper.notifyErrorsChange(this.modelName, this.id, this.clientId);
|
112 | }
|
113 | }
|
114 | }
|
115 |
|
116 | getErrors(): JsonApiValidationError[] {
|
117 | assert('Can not call getErrors unless the RECORD_DATA_ERRORS feature flag is on', !!RECORD_DATA_ERRORS);
|
118 | if (RECORD_DATA_ERRORS) {
|
119 | let errors: JsonApiValidationError[] = this._errors || [];
|
120 | return errors;
|
121 | } else {
|
122 | return [];
|
123 | }
|
124 | }
|
125 |
|
126 |
|
127 |
|
128 |
|
129 | isEmpty() {
|
130 | return this.__attributes === null && this.__inFlightAttributes === null && this.__data === null;
|
131 | }
|
132 |
|
133 | deleteRecord() {
|
134 | this._isDeleted = true;
|
135 | this.notifyStateChange();
|
136 | }
|
137 |
|
138 | isDeleted() {
|
139 | return this._isDeleted;
|
140 | }
|
141 |
|
142 | setIsDeleted(isDeleted: boolean): void {
|
143 | this._isDeleted = isDeleted;
|
144 | if (this._isNew) {
|
145 | this._deletionConfirmed();
|
146 | }
|
147 | this.notifyStateChange();
|
148 | }
|
149 |
|
150 | isDeletionCommitted(): boolean {
|
151 | return this._isDeletionCommited;
|
152 | }
|
153 |
|
154 | reset() {
|
155 | this.__attributes = null;
|
156 | this.__inFlightAttributes = null;
|
157 | this.__data = null;
|
158 | this._errors = undefined;
|
159 | }
|
160 |
|
161 | _setupRelationships(data) {
|
162 | let relationships = this.storeWrapper.relationshipsDefinitionFor(this.modelName);
|
163 | let keys = Object.keys(relationships);
|
164 | for (let i = 0; i < keys.length; i++) {
|
165 | let relationshipName = keys[i];
|
166 |
|
167 | if (!data.relationships[relationshipName]) {
|
168 | continue;
|
169 | }
|
170 |
|
171 |
|
172 | let relationshipData = data.relationships[relationshipName];
|
173 |
|
174 | if (DEBUG) {
|
175 | let storeWrapper = this.storeWrapper;
|
176 | let recordData = this;
|
177 | let relationshipMeta = relationships[relationshipName];
|
178 | if (!relationshipData || !relationshipMeta) {
|
179 | continue;
|
180 | }
|
181 |
|
182 | if (relationshipData.links) {
|
183 | let isAsync = relationshipMeta.options && relationshipMeta.options.async !== false;
|
184 | let relationship = this._relationships.get(relationshipName);
|
185 | warn(
|
186 | `You pushed a record of type '${this.modelName}' with a relationship '${relationshipName}' configured as 'async: false'. You've included a link but no primary data, this may be an error in your payload. EmberData will treat this relationship as known-to-be-empty.`,
|
187 | isAsync || relationshipData.data || relationship.hasAnyRelationshipData,
|
188 | {
|
189 | id: 'ds.store.push-link-for-sync-relationship',
|
190 | }
|
191 | );
|
192 | } else if (relationshipData.data) {
|
193 | if (relationshipMeta.kind === 'belongsTo') {
|
194 | assert(
|
195 | `A ${
|
196 | this.modelName
|
197 | } record was pushed into the store with the value of ${relationshipName} being ${inspect(
|
198 | relationshipData.data
|
199 | )}, but ${relationshipName} is a belongsTo relationship so the value must not be an array. You should probably check your data payload or serializer.`,
|
200 | !Array.isArray(relationshipData.data)
|
201 | );
|
202 | assertRelationshipData(storeWrapper, recordData, relationshipData.data, relationshipMeta);
|
203 | } else if (relationshipMeta.kind === 'hasMany') {
|
204 | assert(
|
205 | `A ${
|
206 | this.modelName
|
207 | } record was pushed into the store with the value of ${relationshipName} being '${inspect(
|
208 | relationshipData.data
|
209 | )}', but ${relationshipName} is a hasMany relationship so the value must be an array. You should probably check your data payload or serializer.`,
|
210 | Array.isArray(relationshipData.data)
|
211 | );
|
212 | if (Array.isArray(relationshipData.data)) {
|
213 | for (let i = 0; i < relationshipData.data.length; i++) {
|
214 | assertRelationshipData(storeWrapper, recordData, relationshipData.data[i], relationshipMeta);
|
215 | }
|
216 | }
|
217 | }
|
218 | }
|
219 | }
|
220 |
|
221 | let relationship = this._relationships.get(relationshipName);
|
222 |
|
223 | relationship.push(relationshipData);
|
224 | }
|
225 | }
|
226 |
|
227 | |
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 | _updateChangedAttributes() {
|
238 | let changedAttributes = this.changedAttributes();
|
239 | let changedAttributeNames = Object.keys(changedAttributes);
|
240 | let attrs = this._attributes;
|
241 |
|
242 | for (let i = 0, length = changedAttributeNames.length; i < length; i++) {
|
243 | let attribute = changedAttributeNames[i];
|
244 | let data = changedAttributes[attribute];
|
245 | let oldData = data[0];
|
246 | let newData = data[1];
|
247 |
|
248 | if (oldData === newData) {
|
249 | delete attrs[attribute];
|
250 | }
|
251 | }
|
252 | }
|
253 |
|
254 | |
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 | changedAttributes(): ChangedAttributesHash {
|
262 | let oldData = this._data;
|
263 | let currentData = this._attributes;
|
264 | let inFlightData = this._inFlightAttributes;
|
265 | let newData = assign({}, inFlightData, currentData);
|
266 | let diffData = Object.create(null);
|
267 | let newDataKeys = Object.keys(newData);
|
268 |
|
269 | for (let i = 0, length = newDataKeys.length; i < length; i++) {
|
270 | let key = newDataKeys[i];
|
271 | diffData[key] = [oldData[key], newData[key]];
|
272 | }
|
273 |
|
274 | return diffData;
|
275 | }
|
276 |
|
277 | isNew() {
|
278 | return this._isNew;
|
279 | }
|
280 |
|
281 | rollbackAttributes() {
|
282 | let dirtyKeys;
|
283 | this._isDeleted = false;
|
284 |
|
285 | if (this.hasChangedAttributes()) {
|
286 | dirtyKeys = Object.keys(this._attributes);
|
287 | this._attributes = null;
|
288 | }
|
289 |
|
290 | if (this.isNew()) {
|
291 | this.removeFromInverseRelationships(true);
|
292 | this._isDeleted = true;
|
293 | this._isNew = false;
|
294 | }
|
295 |
|
296 | this._inFlightAttributes = null;
|
297 |
|
298 | this._clearErrors();
|
299 | this.notifyStateChange();
|
300 |
|
301 | return dirtyKeys;
|
302 | }
|
303 |
|
304 | _deletionConfirmed() {
|
305 | this.removeFromInverseRelationships();
|
306 | }
|
307 |
|
308 | didCommit(data: JsonApiResource | null) {
|
309 | if (this._isDeleted) {
|
310 | this._deletionConfirmed();
|
311 | this._isDeletionCommited = true;
|
312 | }
|
313 |
|
314 | this._isNew = false;
|
315 | let newCanonicalAttributes: AttributesHash | null = null;
|
316 | if (data) {
|
317 |
|
318 | if (data.relationships) {
|
319 | this._setupRelationships(data);
|
320 | }
|
321 | if (data.id) {
|
322 |
|
323 | this.storeWrapper.setRecordId(this.modelName, data.id, this.clientId);
|
324 | this.id = coerceId(data.id);
|
325 | }
|
326 | newCanonicalAttributes = data.attributes || null;
|
327 | }
|
328 | let changedKeys = this._changedKeys(newCanonicalAttributes);
|
329 |
|
330 | assign(this._data, this.__inFlightAttributes, newCanonicalAttributes);
|
331 |
|
332 | this._inFlightAttributes = null;
|
333 |
|
334 | this._updateChangedAttributes();
|
335 | this._clearErrors();
|
336 |
|
337 | this.notifyStateChange();
|
338 | return changedKeys;
|
339 | }
|
340 |
|
341 | notifyStateChange() {
|
342 | if (RECORD_DATA_STATE) {
|
343 | this.storeWrapper.notifyStateChange(this.modelName, this.id, this.clientId);
|
344 | }
|
345 | }
|
346 |
|
347 |
|
348 | getHasMany(key): DefaultCollectionResourceRelationship {
|
349 | return (this._relationships.get(key) as ManyRelationship).getData();
|
350 | }
|
351 |
|
352 |
|
353 | setDirtyHasMany(key, recordDatas) {
|
354 | let relationship = this._relationships.get(key);
|
355 | relationship.clear();
|
356 | relationship.addRecordDatas(recordDatas);
|
357 | }
|
358 |
|
359 |
|
360 | addToHasMany(key, recordDatas, idx) {
|
361 | this._relationships.get(key).addRecordDatas(recordDatas, idx);
|
362 | }
|
363 |
|
364 |
|
365 | removeFromHasMany(key, recordDatas) {
|
366 | this._relationships.get(key).removeRecordDatas(recordDatas);
|
367 | }
|
368 |
|
369 | commitWasRejected(identifier?, errors?: JsonApiValidationError[]) {
|
370 | let keys = Object.keys(this._inFlightAttributes);
|
371 | if (keys.length > 0) {
|
372 | let attrs = this._attributes;
|
373 | for (let i = 0; i < keys.length; i++) {
|
374 | if (attrs[keys[i]] === undefined) {
|
375 | attrs[keys[i]] = this._inFlightAttributes[keys[i]];
|
376 | }
|
377 | }
|
378 | }
|
379 | this._inFlightAttributes = null;
|
380 | if (RECORD_DATA_ERRORS) {
|
381 | if (errors) {
|
382 | this._errors = errors;
|
383 | }
|
384 | this.storeWrapper.notifyErrorsChange(this.modelName, this.id, this.clientId);
|
385 | }
|
386 | }
|
387 |
|
388 | getBelongsTo(key: string): DefaultSingleResourceRelationship {
|
389 | return (this._relationships.get(key) as BelongsToRelationship).getData();
|
390 | }
|
391 |
|
392 | setDirtyBelongsTo(key: string, recordData: RelationshipRecordData) {
|
393 | (this._relationships.get(key) as BelongsToRelationship).setRecordData(recordData);
|
394 | }
|
395 |
|
396 | setDirtyAttribute(key: string, value: any) {
|
397 | let originalValue;
|
398 |
|
399 | this._attributes[key] = value;
|
400 |
|
401 | if (key in this._inFlightAttributes) {
|
402 | originalValue = this._inFlightAttributes[key];
|
403 | } else {
|
404 | originalValue = this._data[key];
|
405 | }
|
406 |
|
407 | if (value === originalValue) {
|
408 | delete this._attributes[key];
|
409 | }
|
410 | }
|
411 |
|
412 |
|
413 | __setId(id: string) {
|
414 | if (this.id !== id) {
|
415 | this.id = id;
|
416 | }
|
417 | }
|
418 |
|
419 | getAttr(key: string): string {
|
420 | if (key in this._attributes) {
|
421 | return this._attributes[key];
|
422 | } else if (key in this._inFlightAttributes) {
|
423 | return this._inFlightAttributes[key];
|
424 | } else {
|
425 | return this._data[key];
|
426 | }
|
427 | }
|
428 |
|
429 | hasAttr(key: string): boolean {
|
430 | return key in this._attributes || key in this._inFlightAttributes || key in this._data;
|
431 | }
|
432 |
|
433 | unloadRecord() {
|
434 | if (this.isDestroyed) {
|
435 | return;
|
436 | }
|
437 | this._destroyRelationships();
|
438 | this.reset();
|
439 | if (!this._scheduledDestroy) {
|
440 | this._scheduledDestroy = run.backburner.schedule('destroy', this, '_cleanupOrphanedRecordDatas');
|
441 | }
|
442 | }
|
443 |
|
444 | _cleanupOrphanedRecordDatas() {
|
445 | let relatedRecordDatas = this._allRelatedRecordDatas();
|
446 | if (areAllModelsUnloaded(relatedRecordDatas)) {
|
447 | for (let i = 0; i < relatedRecordDatas.length; ++i) {
|
448 | let recordData = relatedRecordDatas[i];
|
449 | if (!recordData.isDestroyed) {
|
450 | recordData.destroy();
|
451 | }
|
452 | }
|
453 | }
|
454 | this._scheduledDestroy = null;
|
455 | }
|
456 |
|
457 | destroy() {
|
458 | this._relationships.forEach((name, rel) => rel.destroy());
|
459 | this.isDestroyed = true;
|
460 | this.storeWrapper.disconnectRecord(this.modelName, this.id, this.clientId);
|
461 | }
|
462 |
|
463 | isRecordInUse() {
|
464 | return this.storeWrapper.isRecordInUse(this.modelName, this.id, this.clientId);
|
465 | }
|
466 |
|
467 | |
468 |
|
469 |
|
470 |
|
471 | _directlyRelatedRecordDatasIterable = () => {
|
472 | const initializedRelationships = this._relationships.initializedRelationships;
|
473 | const relationships = Object.keys(initializedRelationships).map(key => initializedRelationships[key]);
|
474 |
|
475 | let i = 0;
|
476 | let j = 0;
|
477 | let k = 0;
|
478 |
|
479 | const findNext = () => {
|
480 | while (i < relationships.length) {
|
481 | while (j < 2) {
|
482 | let members = j === 0 ? relationships[i].members.list : relationships[i].canonicalMembers.list;
|
483 | while (k < members.length) {
|
484 | return members[k++];
|
485 | }
|
486 | k = 0;
|
487 | j++;
|
488 | }
|
489 | j = 0;
|
490 | i++;
|
491 | }
|
492 | return undefined;
|
493 | };
|
494 |
|
495 | return {
|
496 | iterator() {
|
497 | return {
|
498 | next: () => {
|
499 | const value = findNext();
|
500 | return { value, done: value === undefined };
|
501 | },
|
502 | };
|
503 | },
|
504 | };
|
505 | };
|
506 |
|
507 | |
508 |
|
509 |
|
510 |
|
511 |
|
512 |
|
513 |
|
514 |
|
515 |
|
516 |
|
517 | _allRelatedRecordDatas(): RecordDataDefault[] {
|
518 | let array: RecordDataDefault[] = [];
|
519 | let queue: RecordDataDefault[] = [];
|
520 | let bfsId = nextBfsId++;
|
521 | queue.push(this);
|
522 | this._bfsId = bfsId;
|
523 | while (queue.length > 0) {
|
524 | let node = queue.shift() as RecordDataDefault;
|
525 | array.push(node);
|
526 |
|
527 | const iterator = this._directlyRelatedRecordDatasIterable().iterator();
|
528 | for (let obj = iterator.next(); !obj.done; obj = iterator.next()) {
|
529 | const recordData = obj.value;
|
530 | if (recordData instanceof RecordDataDefault) {
|
531 | assert('Internal Error: seen a future bfs iteration', recordData._bfsId <= bfsId);
|
532 | if (recordData._bfsId < bfsId) {
|
533 | queue.push(recordData);
|
534 | recordData._bfsId = bfsId;
|
535 | }
|
536 | }
|
537 | }
|
538 | }
|
539 |
|
540 | return array;
|
541 | }
|
542 |
|
543 | isAttrDirty(key: string): boolean {
|
544 | if (this._attributes[key] === undefined) {
|
545 | return false;
|
546 | }
|
547 | let originalValue;
|
548 | if (this._inFlightAttributes[key] !== undefined) {
|
549 | originalValue = this._inFlightAttributes[key];
|
550 | } else {
|
551 | originalValue = this._data[key];
|
552 | }
|
553 |
|
554 | return originalValue !== this._attributes[key];
|
555 | }
|
556 |
|
557 | get _attributes() {
|
558 | if (this.__attributes === null) {
|
559 | this.__attributes = Object.create(null);
|
560 | }
|
561 | return this.__attributes;
|
562 | }
|
563 |
|
564 | set _attributes(v) {
|
565 | this.__attributes = v;
|
566 | }
|
567 |
|
568 | get _relationships() {
|
569 | if (this.__relationships === null) {
|
570 | this.__relationships = new Relationships(this);
|
571 | }
|
572 |
|
573 | return this.__relationships;
|
574 | }
|
575 |
|
576 | get _data() {
|
577 | if (this.__data === null) {
|
578 | this.__data = Object.create(null);
|
579 | }
|
580 | return this.__data;
|
581 | }
|
582 |
|
583 | set _data(v) {
|
584 | this.__data = v;
|
585 | }
|
586 |
|
587 | |
588 |
|
589 |
|
590 |
|
591 |
|
592 |
|
593 |
|
594 |
|
595 |
|
596 |
|
597 |
|
598 |
|
599 |
|
600 |
|
601 |
|
602 |
|
603 |
|
604 |
|
605 |
|
606 |
|
607 |
|
608 |
|
609 |
|
610 |
|
611 |
|
612 |
|
613 |
|
614 | get _implicitRelationships() {
|
615 | if (this.__implicitRelationships === null) {
|
616 | let relationships = Object.create(null);
|
617 | this.__implicitRelationships = relationships;
|
618 | return relationships;
|
619 | }
|
620 | return this.__implicitRelationships;
|
621 | }
|
622 |
|
623 | get _inFlightAttributes() {
|
624 | if (this.__inFlightAttributes === null) {
|
625 | this.__inFlightAttributes = Object.create(null);
|
626 | }
|
627 | return this.__inFlightAttributes;
|
628 | }
|
629 |
|
630 | set _inFlightAttributes(v) {
|
631 | this.__inFlightAttributes = v;
|
632 | }
|
633 |
|
634 | |
635 |
|
636 |
|
637 |
|
638 |
|
639 |
|
640 |
|
641 |
|
642 |
|
643 | _initRecordCreateOptions(options) {
|
644 | let createOptions = {};
|
645 |
|
646 | if (options !== undefined) {
|
647 | let { modelName, storeWrapper } = this;
|
648 | let attributeDefs = storeWrapper.attributesDefinitionFor(modelName);
|
649 | let relationshipDefs = storeWrapper.relationshipsDefinitionFor(modelName);
|
650 | let relationships = this._relationships;
|
651 | let propertyNames = Object.keys(options);
|
652 |
|
653 | for (let i = 0; i < propertyNames.length; i++) {
|
654 | let name = propertyNames[i];
|
655 | let propertyValue = options[name];
|
656 |
|
657 | if (name === 'id') {
|
658 | this.id = propertyValue;
|
659 | continue;
|
660 | }
|
661 |
|
662 | let fieldType = relationshipDefs[name] || attributeDefs[name];
|
663 | let kind = fieldType !== undefined ? fieldType.kind : null;
|
664 | let relationship;
|
665 |
|
666 | switch (kind) {
|
667 | case 'attribute':
|
668 | this.setDirtyAttribute(name, propertyValue);
|
669 | break;
|
670 | case 'belongsTo':
|
671 | this.setDirtyBelongsTo(name, propertyValue);
|
672 | relationship = relationships.get(name);
|
673 | relationship.setHasAnyRelationshipData(true);
|
674 | relationship.setRelationshipIsEmpty(false);
|
675 | break;
|
676 | case 'hasMany':
|
677 | this.setDirtyHasMany(name, propertyValue);
|
678 | relationship = relationships.get(name);
|
679 | relationship.setHasAnyRelationshipData(true);
|
680 | relationship.setRelationshipIsEmpty(false);
|
681 | break;
|
682 | default:
|
683 |
|
684 | createOptions[name] = propertyValue;
|
685 | }
|
686 | }
|
687 | }
|
688 |
|
689 | return createOptions;
|
690 | }
|
691 |
|
692 | |
693 |
|
694 |
|
695 |
|
696 |
|
697 |
|
698 |
|
699 |
|
700 |
|
701 |
|
702 |
|
703 |
|
704 |
|
705 |
|
706 |
|
707 |
|
708 | removeFromInverseRelationships(isNew = false) {
|
709 | this._relationships.forEach((name, rel) => {
|
710 | rel.removeCompletelyFromInverse();
|
711 | if (isNew === true) {
|
712 | rel.clear();
|
713 | }
|
714 | });
|
715 | this.__relationships = null;
|
716 |
|
717 | let implicitRelationships = this._implicitRelationships;
|
718 | this.__implicitRelationships = null;
|
719 |
|
720 | Object.keys(implicitRelationships).forEach(key => {
|
721 | let rel = implicitRelationships[key];
|
722 |
|
723 | rel.removeCompletelyFromInverse();
|
724 | if (isNew === true) {
|
725 | rel.clear();
|
726 | }
|
727 | });
|
728 | }
|
729 |
|
730 | _destroyRelationships() {
|
731 | let relationships = this._relationships;
|
732 | relationships.forEach((name, rel) => destroyRelationship(rel));
|
733 |
|
734 | let implicitRelationships = this._implicitRelationships;
|
735 | this.__implicitRelationships = null;
|
736 | Object.keys(implicitRelationships).forEach(key => {
|
737 | let rel = implicitRelationships[key];
|
738 | destroyRelationship(rel);
|
739 | });
|
740 | }
|
741 |
|
742 | clientDidCreate() {
|
743 | this._isNew = true;
|
744 | }
|
745 |
|
746 | |
747 |
|
748 |
|
749 |
|
750 |
|
751 |
|
752 |
|
753 |
|
754 |
|
755 |
|
756 |
|
757 |
|
758 |
|
759 |
|
760 |
|
761 |
|
762 |
|
763 |
|
764 |
|
765 |
|
766 |
|
767 |
|
768 |
|
769 |
|
770 |
|
771 |
|
772 |
|
773 |
|
774 |
|
775 |
|
776 |
|
777 |
|
778 |
|
779 |
|
780 |
|
781 |
|
782 |
|
783 |
|
784 |
|
785 |
|
786 |
|
787 | |
788 |
|
789 |
|
790 |
|
791 |
|
792 | _changedKeys(updates) {
|
793 | let changedKeys: string[] = [];
|
794 |
|
795 | if (updates) {
|
796 | let original, i, value, key;
|
797 | let keys = Object.keys(updates);
|
798 | let length = keys.length;
|
799 | let hasAttrs = this.hasChangedAttributes();
|
800 | let attrs;
|
801 | if (hasAttrs) {
|
802 | attrs = this._attributes;
|
803 | }
|
804 |
|
805 | original = assign(Object.create(null), this._data, this.__inFlightAttributes);
|
806 |
|
807 | for (i = 0; i < length; i++) {
|
808 | key = keys[i];
|
809 | value = updates[key];
|
810 |
|
811 |
|
812 |
|
813 |
|
814 |
|
815 | if (hasAttrs === true && attrs[key] !== undefined) {
|
816 | continue;
|
817 | }
|
818 |
|
819 | if (!isEqual(original[key], value)) {
|
820 | changedKeys.push(key);
|
821 | }
|
822 | }
|
823 | }
|
824 |
|
825 | return changedKeys;
|
826 | }
|
827 |
|
828 | toString() {
|
829 | return `<${this.modelName}:${this.id}>`;
|
830 | }
|
831 | }
|
832 |
|
833 | function assertRelationshipData(store, recordData, data, meta) {
|
834 | assert(
|
835 | `A ${recordData.modelName} record was pushed into the store with the value of ${meta.key} being '${JSON.stringify(
|
836 | data
|
837 | )}', but ${
|
838 | meta.key
|
839 | } is a belongsTo relationship so the value must not be an array. You should probably check your data payload or serializer.`,
|
840 | !Array.isArray(data)
|
841 | );
|
842 | assert(
|
843 | `Encountered a relationship identifier without a type for the ${meta.kind} relationship '${
|
844 | meta.key
|
845 | }' on ${recordData}, expected a json-api identifier with type '${meta.type}' but found '${JSON.stringify(
|
846 | data
|
847 | )}'. Please check your serializer and make sure it is serializing the relationship payload into a JSON API format.`,
|
848 | data === null || (typeof data.type === 'string' && data.type.length)
|
849 | );
|
850 | assert(
|
851 | `Encountered a relationship identifier without an id for the ${meta.kind} relationship '${
|
852 | meta.key
|
853 | }' on ${recordData}, expected a json-api identifier but found '${JSON.stringify(
|
854 | data
|
855 | )}'. Please check your serializer and make sure it is serializing the relationship payload into a JSON API format.`,
|
856 | data === null || !!coerceId(data.id)
|
857 | );
|
858 | assert(
|
859 | `Encountered a relationship identifier with type '${data.type}' for the ${meta.kind} relationship '${meta.key}' on ${recordData}, Expected a json-api identifier with type '${meta.type}'. No model was found for '${data.type}'.`,
|
860 | data === null || !data.type || store._hasModelFor(data.type)
|
861 | );
|
862 | }
|
863 |
|
864 |
|
865 |
|
866 |
|
867 |
|
868 |
|
869 |
|
870 |
|
871 |
|
872 |
|
873 |
|
874 | function destroyRelationship(rel) {
|
875 | rel.recordDataDidDematerialize();
|
876 |
|
877 | if (rel._inverseIsSync()) {
|
878 | rel.removeAllRecordDatasFromOwn();
|
879 | rel.removeAllCanonicalRecordDatasFromOwn();
|
880 | }
|
881 | }
|
882 |
|
883 | function areAllModelsUnloaded(recordDatas) {
|
884 | for (let i = 0; i < recordDatas.length; ++i) {
|
885 | if (recordDatas[i].isRecordInUse()) {
|
886 | return false;
|
887 | }
|
888 | }
|
889 | return true;
|
890 | }
|