Skip to content

Commit

Permalink
store the type name of all records
Browse files Browse the repository at this point in the history
Summary: Part 2/2 (see D2473427) - now that the compiler always adds `__typename` whenever the type is abstract, there is always sufficient information in a query & payload to determine the concrete type when creating a new record in the store:
- if `__typename` is present in the payload, that's the type name
- else `__typename` isn't present because the type here is guaranteed to be static - use the `parentType` metadata on the `id` field as the concrete type. Note that if `id` doesn't exist then this is a client-only record and the type is not required (because data about the record cannot be communicated with any other system).

Reviewed By: @wincent

Differential Revision: D2479202
  • Loading branch information
josephsavona authored and facebook-github-bot-0 committed Oct 1, 2015
1 parent 1a6f0c9 commit 1844dc9
Show file tree
Hide file tree
Showing 13 changed files with 404 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe('RelayContainer.hasOptimisticUpdate', () => {
it('is only true for queued records', () => {
var storeData = RelayStoreData.getDefaultInstance();
var recordStore = storeData.getRecordStoreForOptimisticMutation('mutation');
recordStore.putRecord('123');
recordStore.putRecord('123', 'Type');
var instance = RelayTestRenderer.render(genMockPointer => {
return <MockContainer foo={genMockPointer('123')} />;
});
Expand All @@ -70,7 +70,8 @@ describe('RelayContainer.hasOptimisticUpdate', () => {
});

it('is false for non-queued records', () => {
RelayStoreData.getDefaultInstance().getRecordStore().putRecord('123');
RelayStoreData.getDefaultInstance().getRecordStore()
.putRecord('123', 'Type');

var instance = RelayTestRenderer.render(genMockPointer => {
return <MockContainer foo={genMockPointer('123')} />;
Expand Down
51 changes: 43 additions & 8 deletions src/store/RelayQueryWriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@

'use strict';

var GraphQLStoreDataHandler = require('GraphQLStoreDataHandler');
var RelayQuery = require('RelayQuery');
import type RelayChangeTracker from 'RelayChangeTracker';
var RelayConnectionInterface = require('RelayConnectionInterface');
var RelayNodeInterface = require('RelayNodeInterface');
import type RelayQueryPath from 'RelayQueryPath';
import type RelayQueryTracker from 'RelayQueryTracker';
var RelayQueryVisitor = require('RelayQueryVisitor');
Expand All @@ -40,8 +42,8 @@ type WriterState = {
path: RelayQueryPath;
};

var {ID, TYPENAME} = RelayNodeInterface;
var {EDGES, NODE, PAGE_INFO} = RelayConnectionInterface;
var ID = 'id';

/**
* @internal
Expand Down Expand Up @@ -74,6 +76,29 @@ class RelayQueryWriter extends RelayQueryVisitor<WriterState> {
return this._store;
}

getRecordTypeName(
field: RelayQuery.Node,
recordID: DataID,
payload: Object
): ?string {
if (GraphQLStoreDataHandler.isClientID(recordID)) {
return null;
}
var typeName = payload[TYPENAME];
if (typeName == null) {
var idField = field.getFieldByStorageKey(ID);
if (idField) {
typeName = idField.getParentType();
}
}
warning(
typeName,
'RelayQueryWriter: Could not find a type name for record `%s`.',
recordID
);
return typeName || null;
}

/**
* Traverses a query and payload in parallel, writing the results into the
* store.
Expand Down Expand Up @@ -142,11 +167,12 @@ class RelayQueryWriter extends RelayQueryVisitor<WriterState> {
createRecordIfMissing(
node: RelayQuery.Node,
recordID: DataID,
typeName: ?string,
path: RelayQueryPath
): void {
var recordState = this._store.getRecordState(recordID);
if (recordState !== RelayRecordState.EXISTENT) {
this._store.putRecord(recordID, path);
this._store.putRecord(recordID, typeName, path);
this.recordCreate(recordID);
}
if (this.isNewRecord(recordID) || this._updateTrackedQueries) {
Expand Down Expand Up @@ -175,8 +201,15 @@ class RelayQueryWriter extends RelayQueryVisitor<WriterState> {
}
return;
}
invariant(
typeof responseData === 'object' && responseData !== null,
'RelayQueryWriter: Cannot update record `%s`, expected response to be ' +
'an array or object.',
recordID
);
if (recordState !== RelayRecordState.EXISTENT) {
this._store.putRecord(recordID, path);
var typeName = this.getRecordTypeName(root, recordID, responseData);
this._store.putRecord(recordID, typeName, path);
this.recordCreate(recordID);
}
if (this.isNewRecord(recordID) || this._updateTrackedQueries) {
Expand Down Expand Up @@ -290,7 +323,7 @@ class RelayQueryWriter extends RelayQueryVisitor<WriterState> {
// always update the store to ensure the value is present in the appropriate
// data sink (records/queuedRecords), but only record an update if the value
// changed.
this._store.putRecord(connectionID, path);
this._store.putRecord(connectionID, null, path);
this._store.putLinkedRecordID(recordID, storageKey, connectionID);
// record the create/update only if something changed
if (connectionRecordState !== RelayRecordState.EXISTENT) {
Expand Down Expand Up @@ -453,7 +486,7 @@ class RelayQueryWriter extends RelayQueryVisitor<WriterState> {
// TODO: Flow: `nodeID` is `string`
var edgeID = generateClientEdgeID(connectionID, (nodeID: any));
var path = state.path.getPath(edges, edgeID);
this.createRecordIfMissing(edges, edgeID, path);
this.createRecordIfMissing(edges, edgeID, null, path);
fetchedEdgeIDs.push(edgeID);

// Write data for the edge, using `nodeID` as the id for direct descendant
Expand Down Expand Up @@ -532,7 +565,8 @@ class RelayQueryWriter extends RelayQueryVisitor<WriterState> {
nextLinkedIDs.push(nextLinkedID);

var path = state.path.getPath(field, nextLinkedID);
this.createRecordIfMissing(field, nextLinkedID, path);
var typeName = this.getRecordTypeName(field, nextLinkedID, nextRecord);
this.createRecordIfMissing(field, nextLinkedID, typeName, path);
isUpdate = (
isUpdate ||
nextLinkedID !== prevLinkedID ||
Expand Down Expand Up @@ -571,7 +605,7 @@ class RelayQueryWriter extends RelayQueryVisitor<WriterState> {
var {nodeID} = state;
var storageKey = field.getStorageKey();
invariant(
typeof fieldData === 'object',
typeof fieldData === 'object' && fieldData !== null,
'RelayQueryWriter: Expected data for non-scalar field `%s` on record ' +
'`%s` to be an object.',
storageKey,
Expand All @@ -592,7 +626,8 @@ class RelayQueryWriter extends RelayQueryVisitor<WriterState> {
);

var path = state.path.getPath(field, nextLinkedID);
this.createRecordIfMissing(field, nextLinkedID, path);
var typeName = this.getRecordTypeName(field, nextLinkedID, fieldData);
this.createRecordIfMissing(field, nextLinkedID, typeName, path);
// always update the store to ensure the value is present in the appropriate
// data sink (record/queuedRecords), but only record an update if the value
// changed.
Expand Down
15 changes: 12 additions & 3 deletions src/store/RelayRecordStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export type Record = {
__mutationIDs__?: Array<ClientMutationID>;
__range__?: GraphQLRange;
__path__?: RelayQueryPath;
__typename?: ?string;

/**
* $FlowIssue: the catch-all "mixed" type above should allow us to set
Expand Down Expand Up @@ -231,6 +232,7 @@ class RelayRecordStore {
*/
putRecord(
dataID: DataID,
typeName: ?string,
path?: RelayQueryPath
): void {
var target = this._queuedRecords || this._records;
Expand All @@ -243,6 +245,7 @@ class RelayRecordStore {
}
var nextRecord: Record = ({
__dataID__: dataID,
__typename: typeName,
}: $FixMe);
if (target === this._queuedRecords) {
this._setClientMutationID(nextRecord);
Expand All @@ -259,10 +262,10 @@ class RelayRecordStore {
target[dataID] = nextRecord;
var cacheManager = this._cacheManager;
if (!this._queuedRecords && cacheManager) {
cacheManager.cacheField(dataID, '__dataID__', dataID);
cacheManager.cacheField(dataID, '__dataID__', dataID, typeName);
var cachedPath = nextRecord[PATH];
if (cachedPath) {
cacheManager.cacheField(dataID, '__path__', cachedPath);
cacheManager.cacheField(dataID, '__path__', cachedPath, typeName);
}
}
}
Expand Down Expand Up @@ -355,6 +358,11 @@ class RelayRecordStore {
}
}

getType(dataID: DataID): ?string {
// `__typename` property is typed as `string`
return (this._getField(dataID, '__typename'): any);
}

/**
* Returns the value of the field for the given dataID.
*/
Expand Down Expand Up @@ -383,7 +391,8 @@ class RelayRecordStore {
);
record[storageKey] = value;
if (!this._queuedRecords && this._cacheManager) {
this._cacheManager.cacheField(dataID, storageKey, value);
var typeName = record.__typename;
this._cacheManager.cacheField(dataID, storageKey, value, typeName);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/store/__tests__/RelayRecordStore-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ describe('RelayRecordStore', () => {
query.getFieldByStorageKey('actor').getFieldByStorageKey('address'),
addressID
);
store.putRecord(addressID, path);
store.putRecord(addressID, 'Type', path);
expect(store.getPathToRecord(addressID)).toMatchPath(path);
});
});
Expand Down Expand Up @@ -710,7 +710,7 @@ describe('RelayRecordStore', () => {
it('returns undefined if the connection is unfetched', () => {
var records = {};
var store = new RelayRecordStore({records});
store.putRecord('1');
store.putRecord('1', 'Type');
expect(store.getConnectionIDsForField('1', 'news_feed')).toBe(undefined);
});

Expand Down
Loading

0 comments on commit 1844dc9

Please sign in to comment.