_normalizePluralLink( field: ConcreteLinkedField, record: Record, storageKey: string, fieldValue: mixed, ): void { invariant( Array.isArray(fieldValue), 'RelayResponseNormalizer: Expected data for field `%s` to be an array ' + 'of objects.', storageKey, ); const prevIDs = RelayModernRecord.getLinkedRecordIDs(record, storageKey); const nextIDs = []; fieldValue.forEach((item, nextIndex) => { // validate response data if (item == null) { nextIDs.push(item); return; } invariant( typeof item === 'object', 'RelayResponseNormalizer: Expected elements for field `%s` to be ' + 'objects.', storageKey, ); const nextID = item.id || (prevIDs && prevIDs[nextIndex]) || // Reuse previously generated client IDs generateRelayClientID( RelayModernRecord.getDataID(record), storageKey, nextIndex, ); invariant( typeof nextID === 'string', 'RelayResponseNormalizer: Expected id of elements of field `%s` to ' + 'be strings.', storageKey, ); nextIDs.push(nextID); let nextRecord = this._recordSource.get(nextID); if (!nextRecord) { const typeName = field.concreteType || this._getRecordType(item); nextRecord = RelayModernRecord.create(nextID, typeName); this._recordSource.set(nextID, nextRecord); } else if (__DEV__) { this._validateRecordType(nextRecord, field, item); } this._traverseSelections(field, nextRecord, item); }); RelayModernRecord.setLinkedRecordIDs(record, storageKey, nextIDs); }
/** * Returns an array of inspectors for the given plural "linked" field (a field * whose value is an array of Records instead of a scalar). May throw if the * field is present but not a plural linked record. */ getLinkedRecords(name: string, args?: ?Variables): ?Array<?RecordInspector> { const storageKey = args ? formatStorageKey(name, args) : name; const linkedIDs = RelayModernRecord.getLinkedRecordIDs( this._record, storageKey, ); if (linkedIDs == null) { return linkedIDs; } return linkedIDs.map(linkedID => { return linkedID != null ? this._sourceInspector.get(linkedID) : linkedID; }); }
it('sets an array of links', () => { const record = { [ID_KEY]: '4', }; const storageKey = 'friends{"first":10}'; RelayModernRecord.setLinkedRecordIDs(record, storageKey, [ 'beast', 'greg', null, ]); expect(RelayModernRecord.getLinkedRecordIDs(record, storageKey)).toEqual([ 'beast', 'greg', null, ]); });
getLinkedRecordIDs(dataID: DataID, storageKey: string): ?Array<?DataID> { for (let ii = 0; ii < this.__sources.length; ii++) { const record = this.__sources[ii].get(dataID); if (record) { const linkedIDs = RelayModernRecord.getLinkedRecordIDs( record, storageKey, ); if (linkedIDs !== undefined) { return linkedIDs; } } else if (record === null) { return null; } } }
_readPluralLink( field: ConcreteLinkedField, record: Record, data: SelectorData, ): void { const applicationName = field.alias || field.name; const storageKey = getStorageKey(field, this._variables); const linkedIDs = RelayModernRecord.getLinkedRecordIDs(record, storageKey); if (linkedIDs == null) { data[applicationName] = linkedIDs; return; } const prevData = data[applicationName]; invariant( prevData == null || Array.isArray(prevData), 'RelayReader(): Expected data for field `%s` on record `%s` ' + 'to be an array, got `%s`.', applicationName, RelayModernRecord.getDataID(record), prevData, ); const linkedArray = prevData || []; linkedIDs.forEach((linkedID, nextIndex) => { if (linkedID == null) { linkedArray[nextIndex] = linkedID; return; } const prevItem = linkedArray[nextIndex]; invariant( prevItem == null || typeof prevItem === 'object', 'RelayReader(): Expected data for field `%s` on record `%s` ' + 'to be an object, got `%s`.', applicationName, RelayModernRecord.getDataID(record), prevItem, ); const linkedItem = this._traverse(field, linkedID, prevItem); linkedArray[nextIndex] = linkedItem; }); data[applicationName] = linkedArray; }
it('returns the linked record IDs when they exist', () => { expect( RelayModernRecord.getLinkedRecordIDs(record, 'friends{"first":10}'), ).toEqual(['beast', 'greg', null]); });
it('returns null when the link is non-existent', () => { expect(RelayModernRecord.getLinkedRecordIDs(record, 'enemies')).toBe( null, ); });
it('returns undefined when the link is unknown', () => { expect(RelayModernRecord.getLinkedRecordIDs(record, 'colors')).toBe( undefined, ); });
expect(() => RelayModernRecord.getLinkedRecordIDs(record, 'hometown'),
expect(() => RelayModernRecord.getLinkedRecordIDs(record, 'name'),