Skip to content

Commit

Permalink
Make it a Flow error to bypass the utility functions in RelayModernRe…
Browse files Browse the repository at this point in the history
…cord

Reviewed By: tyao1

Differential Revision: D47763183

fbshipit-source-id: e2d965ba654caab89a061306c3d25f42d9d8d52a
  • Loading branch information
Ryan Holdren authored and facebook-github-bot committed Aug 3, 2023
1 parent 4219468 commit 21a896c
Show file tree
Hide file tree
Showing 16 changed files with 148 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ if (__DEV__) {
}
const recordSource = environment.getStore().getSource();
const record = recordSource.get(id);
const typename = record && Record.getType(record);
const typename = record == null ? null : Record.getType(record);
if (typename == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ if (__DEV__) {
}
const recordSource = environment.getStore().getSource();
const record = recordSource.get(id);
const typename = record && Record.getType(record);
const typename = record == null ? null : Record.getType(record);
if (typename == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import type {MultiActorStoreUpdater} from '../MultiActorEnvironmentTypes';

const RelayModernRecord = require('../../store/RelayModernRecord');
const {getActorIdentifier} = require('../ActorIdentifier');
const MultiActorEnvironment = require('../MultiActorEnvironment');

Expand Down Expand Up @@ -47,7 +48,7 @@ describe('commitMultiActorUpdate', () => {
throw new Error('Test record is null.');
}
expect(testRecord).toHaveProperty('test');
expect(testRecord.test).toBe(42);
expect(RelayModernRecord.getValue(testRecord, 'test')).toBe(42);
});

expect(actorsCalled.includes('actor1')).toBe(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'use strict';

const defaultGetDataID = require('../../store/defaultGetDataID');
const RelayModernRecord = require('../../store/RelayModernRecord');
const RelayRecordSource = require('../../store/RelayRecordSource');
const RelayStoreUtils = require('../../store/RelayStoreUtils');
const RelayRecordProxy = require('../RelayRecordProxy');
Expand Down Expand Up @@ -132,8 +133,7 @@ describe('RelayRecordSourceProxy', () => {
const root = baseSource.get(ROOT_ID);
expect(root).not.toBeUndefined();
if (root != null) {
// Flow
root.__typename = 'User';
RelayModernRecord.setValue(root, TYPENAME_KEY, 'User');
}
expect(() => {
store.getRoot();
Expand Down
26 changes: 25 additions & 1 deletion packages/relay-runtime/store/RelayModernRecord.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
import type {DataID} from '../util/RelayRuntimeTypes';
import type {Record} from './RelayStoreTypes';

const deepFreeze = require('../util/deepFreeze');
const {generateClientObjectClientID, isClientID} = require('./ClientID');
Expand All @@ -34,6 +33,11 @@ const areEqual = require('areEqual');
const invariant = require('invariant');
const warning = require('warning');

/*
* An individual cached graph object.
*/
export opaque type Record = {[key: string]: mixed, ...};

/**
* @public
*
Expand Down Expand Up @@ -122,6 +126,15 @@ function create(dataID: DataID, typeName: string): Record {
return record;
}

/**
* @public
*
* Convert an object to a record.
*/
function fromObject(object: {[key: string]: mixed, ...}): Record {
return object;
}

/**
* @public
*
Expand Down Expand Up @@ -171,6 +184,15 @@ function getValue(record: Record, storageKey: string): mixed {
return value;
}

/**
* @public
*
* Check if a record has a value for the given field.
*/
function hasValue(record: Record, storageKey: string): boolean {
return storageKey in record;
}

/**
* @public
*
Expand Down Expand Up @@ -509,13 +531,15 @@ module.exports = {
copyFields,
create,
freeze,
fromObject,
getDataID,
getFields,
getInvalidationEpoch,
getLinkedRecordID,
getLinkedRecordIDs,
getType,
getValue,
hasValue,
merge,
setValue,
setLinkedRecordID,
Expand Down
9 changes: 6 additions & 3 deletions packages/relay-runtime/store/RelayOptimisticRecordSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ import type {
RecordSource,
} from './RelayStoreTypes';

const RelayModernRecord = require('./RelayModernRecord');
const RelayRecordSource = require('./RelayRecordSource');
const invariant = require('invariant');

const UNPUBLISH_RECORD_SENTINEL = Object.freeze({
__UNPUBLISH_RECORD_SENTINEL: true,
});
const UNPUBLISH_RECORD_SENTINEL = RelayModernRecord.fromObject(
Object.freeze({
__UNPUBLISH_RECORD_SENTINEL: true,
}),
);

/**
* An implementation of MutableRecordSource that represents a base RecordSource
Expand Down
6 changes: 3 additions & 3 deletions packages/relay-runtime/store/RelayReader.js
Original file line number Diff line number Diff line change
Expand Up @@ -845,9 +845,9 @@ class RelayReader {
const fragmentRef = {};
this._createFragmentPointer(
field.fragmentSpread,
{
RelayModernRecord.fromObject({
__id: dataID,
},
}),
fragmentRef,
);
data[applicationName] = {
Expand Down Expand Up @@ -991,7 +991,7 @@ class RelayReader {
record,
fieldData,
);
return fieldData;
return RelayModernRecord.fromObject(fieldData);
}

// Has three possible return values:
Expand Down
17 changes: 11 additions & 6 deletions packages/relay-runtime/store/RelayRecordSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@

import type {DataID} from '../util/RelayRuntimeTypes';
import type {RecordState} from './RelayRecordState';
import type {
MutableRecordSource,
Record,
RecordObjectMap,
} from './RelayStoreTypes';
import type {MutableRecordSource, Record} from './RelayStoreTypes';

const RelayModernRecord = require('./RelayModernRecord');
const RelayRecordState = require('./RelayRecordState');

const {EXISTENT, NONEXISTENT, UNKNOWN} = RelayRecordState;

/**
* A collection of records keyed by id.
*/
type RecordObjectMap = {[DataID]: ?{[key: string]: mixed}};

/**
* An implementation of the `MutableRecordSource` interface (defined in
* `RelayStoreTypes`) that holds all records in memory (JS Map).
Expand All @@ -34,7 +36,10 @@ class RelayRecordSource implements MutableRecordSource {
this._records = new Map();
if (records != null) {
Object.keys(records).forEach(key => {
this._records.set(key, records[key]);
const object = records[key];
const record =
object == null ? object : RelayModernRecord.fromObject(object);
this._records.set(key, record);
});
}
}
Expand Down
13 changes: 3 additions & 10 deletions packages/relay-runtime/store/RelayStoreTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,29 +49,22 @@ import type {
UpdatableQuery,
Variables,
} from '../util/RelayRuntimeTypes';
import type {Record as RelayModernRecord} from './RelayModernRecord';
import type {InvalidationState} from './RelayModernStore';
import type RelayOperationTracker from './RelayOperationTracker';
import type {RecordState} from './RelayRecordState';

export opaque type FragmentType = empty;
export type OperationTracker = RelayOperationTracker;

export type Record = RelayModernRecord;

export type MutationParameters = {
+response: {...},
+variables: {...},
+rawResponse?: {...},
};

/*
* An individual cached graph object.
*/
export type Record = {[key: string]: mixed, ...};

/**
* A collection of records keyed by id.
*/
export type RecordObjectMap = {[DataID]: ?Record};

export type FragmentMap = {[key: string]: ReaderFragment, ...};

/**
Expand Down
17 changes: 14 additions & 3 deletions packages/relay-runtime/store/ResolverCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,22 @@ class RecordResolverCache implements ResolverCache {
}

// $FlowFixMe[incompatible-type] - will always be empty
const answer: T = linkedRecord[RELAY_RESOLVER_VALUE_KEY];
const answer: T = RelayModernRecord.getValue(
linkedRecord,
RELAY_RESOLVER_VALUE_KEY,
);

// $FlowFixMe[incompatible-type] - casting mixed
const snapshot: ?Snapshot = linkedRecord[RELAY_RESOLVER_SNAPSHOT_KEY];
const snapshot: ?Snapshot = RelayModernRecord.getValue(
linkedRecord,
RELAY_RESOLVER_SNAPSHOT_KEY,
);

// $FlowFixMe[incompatible-type] - casting mixed
const error: ?Error = linkedRecord[RELAY_RESOLVER_ERROR_KEY];
const error: ?Error = RelayModernRecord.getValue(
linkedRecord,
RELAY_RESOLVER_ERROR_KEY,
);

return [answer, linkedID, error, snapshot, undefined, undefined];
}
Expand Down
1 change: 1 addition & 0 deletions packages/relay-runtime/store/StoreInspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ if (__DEV__) {
return record;
}
return new Proxy(
// $FlowFixMe: Do not assume that record is an object
{...record},
{
get(target, prop) {
Expand Down
52 changes: 52 additions & 0 deletions packages/relay-runtime/store/__tests__/RelayModernRecord-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ describe('RelayModernRecord', () => {
});
});

describe('fromObject()', () => {
it('returns the given object', () => {
const object = {};
const record = RelayModernRecord.fromObject(object);
expect(record).toBe(object);
});
});

describe('getLinkedRecordIDs()', () => {
let record;

Expand Down Expand Up @@ -244,6 +252,50 @@ describe('RelayModernRecord', () => {
});
});

describe('hasValue()', () => {
let record;

beforeEach(() => {
record = {
[ID_KEY]: '4',
[TYPENAME_KEY]: 'User',
fieldThatIsString: 'applesauce',
fieldThatIsNull: null,
fieldThatIsUndefined: undefined,
};
});

it('has no special treatment for the id field', () => {
expect(RelayModernRecord.hasValue(record, ID_KEY)).toBe(true);
});

it('has no special treatment for the typename field', () => {
expect(RelayModernRecord.hasValue(record, TYPENAME_KEY)).toBe(true);
});

it('returns true when the value is a string', () => {
expect(RelayModernRecord.hasValue(record, 'fieldThatIsString')).toBe(
true,
);
});

it('returns true when the value is null', () => {
expect(RelayModernRecord.hasValue(record, 'fieldThatIsNull')).toBe(true);
});

it('returns true when the value is explicitly undefined', () => {
expect(RelayModernRecord.hasValue(record, 'fieldThatIsUndefined')).toBe(
true,
);
});

it('returns false when the value is missing', () => {
expect(RelayModernRecord.hasValue(record, 'fieldThatIsMissing')).toBe(
false,
);
});
});

describe('update()', () => {
it('returns the first record if there are no changes', () => {
const prev = RelayModernRecord.create('4', 'User');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,9 @@ function cloneEventWithSets(event: LogEvent) {
if (!record) {
throw new Error('Expected to find record with id client:1');
}
expect(record[INVALIDATED_AT_KEY]).toEqual(1);
expect(
RelayModernRecord.getValue(record, INVALIDATED_AT_KEY),
).toEqual(1);
expect(store.check(owner)).toEqual({status: 'stale'});
});

Expand Down Expand Up @@ -657,7 +659,9 @@ function cloneEventWithSets(event: LogEvent) {
if (!record) {
throw new Error('Expected to find record with id "4"');
}
expect(record[INVALIDATED_AT_KEY]).toEqual(1);
expect(
RelayModernRecord.getValue(record, INVALIDATED_AT_KEY),
).toEqual(1);
expect(store.check(owner)).toEqual({status: 'stale'});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,9 @@ function cloneEventWithSets(event: LogEvent) {
if (!record) {
throw new Error('Expected to find record with id client:1');
}
expect(record[INVALIDATED_AT_KEY]).toEqual(1);
expect(
RelayModernRecord.getValue(record, INVALIDATED_AT_KEY),
).toEqual(1);
expect(store.check(owner)).toEqual({status: 'stale'});
});

Expand Down Expand Up @@ -970,7 +972,9 @@ function cloneEventWithSets(event: LogEvent) {
if (!record) {
throw new Error('Expected to find record with id "4"');
}
expect(record[INVALIDATED_AT_KEY]).toEqual(1);
expect(
RelayModernRecord.getValue(record, INVALIDATED_AT_KEY),
).toEqual(1);
expect(store.check(owner)).toEqual({status: 'stale'});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

'use strict';

import type {RecordObjectMap} from '../RelayStoreTypes';
import type {DataID} from 'relay-runtime/util/RelayRuntimeTypes';

import RelayNetwork from '../../network/RelayNetwork';
Expand Down Expand Up @@ -239,7 +238,7 @@ describe('RelayReferenceMarker', () => {
});

it('marks "handle" nodes with key and filters for queries', () => {
const data: RecordObjectMap = {
const data = {
'1': {
__id: '1',
__typename: 'User',
Expand Down
Loading

0 comments on commit 21a896c

Please sign in to comment.