mirror: add typename support to `UpdateResult`s (#1664)

Summary:
The internal `UpdateResult` structure now lists IDs of objects whose
typename has been queried. This list is expected to be empty for now.

Test Plan:
Unit tests added.

wchargin-branch: mirror-typename-updateresult
This commit is contained in:
William Chargin 2020-02-29 16:59:39 -08:00 committed by GitHub
parent 477243fc2c
commit 5c937d8604
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 12 deletions

View File

@ -735,7 +735,9 @@ export class Mirror {
queryResult: UpdateResult queryResult: UpdateResult
): void { ): void {
for (const topLevelKey of Object.keys(queryResult)) { for (const topLevelKey of Object.keys(queryResult)) {
if (topLevelKey.startsWith(_FIELD_PREFIXES.OWN_DATA)) { if (topLevelKey.startsWith(_FIELD_PREFIXES.TYPENAMES)) {
throw new Error("Typename update results not yet supported");
} else if (topLevelKey.startsWith(_FIELD_PREFIXES.OWN_DATA)) {
const rawValue: OwnDataUpdateResult | NodeConnectionsUpdateResult = const rawValue: OwnDataUpdateResult | NodeConnectionsUpdateResult =
queryResult[topLevelKey]; queryResult[topLevelKey];
const updateRecord: OwnDataUpdateResult = (rawValue: any); const updateRecord: OwnDataUpdateResult = (rawValue: any);
@ -2092,6 +2094,16 @@ type NestedFieldResult = {
+[Schema.Fieldname]: PrimitiveResult | NodeFieldResult, +[Schema.Fieldname]: PrimitiveResult | NodeFieldResult,
} | null; } | null;
/**
* Result describing only the typename of a set of nodes. Used when we
* only have references to nodes via unfaithful fields.
*/
type TypenamesUpdateResult = $ReadOnlyArray<{|
+__typename: Schema.Typename,
+id: Schema.ObjectId,
|}>;
export type _TypenamesUpdateResult = TypenamesUpdateResult; // for tests
/** /**
* Result describing own-data for many nodes of a given type. Whether a * Result describing own-data for many nodes of a given type. Whether a
* value is a `PrimitiveResult` or a `NodeFieldResult` is determined by * value is a `PrimitiveResult` or a `NodeFieldResult` is determined by
@ -2107,6 +2119,7 @@ type OwnDataUpdateResult = $ReadOnlyArray<{
| NodeFieldResult | NodeFieldResult
| NestedFieldResult, | NestedFieldResult,
}>; }>;
export type _OwnDataUpdateResult = OwnDataUpdateResult; // for tests
/** /**
* Result describing new elements for connections on a single node. * Result describing new elements for connections on a single node.
@ -2117,12 +2130,13 @@ type NodeConnectionsUpdateResult = {
+id: Schema.ObjectId, +id: Schema.ObjectId,
+[connectionFieldname: Schema.Fieldname]: ConnectionFieldResult, +[connectionFieldname: Schema.Fieldname]: ConnectionFieldResult,
}; };
export type _NodeConnectionsUpdateResult = NodeConnectionsUpdateResult; // for tests
/** /**
* Result describing both own-data updates and connection updates. Each * Result describing all kinds of updates. Each key's prefix determines
* key's prefix determines what type of results the corresponding value * what type of results the corresponding value represents (see
* represents (see constants below). No field prefix is a prefix of * constants below). No field prefix is a prefix of another, so this
* another, so this characterization is complete. * characterization is complete.
* *
* This type would be exact but for facebook/flow#2977, et al. * This type would be exact but for facebook/flow#2977, et al.
* *
@ -2131,10 +2145,19 @@ type NodeConnectionsUpdateResult = {
type UpdateResult = { type UpdateResult = {
// The prefix of each key determines what type of results the value // The prefix of each key determines what type of results the value
// represents. See constants below. // represents. See constants below.
+[string]: OwnDataUpdateResult | NodeConnectionsUpdateResult, +[string]:
| TypenamesUpdateResult
| OwnDataUpdateResult
| NodeConnectionsUpdateResult,
}; };
export const _FIELD_PREFIXES = deepFreeze({ export const _FIELD_PREFIXES = deepFreeze({
/**
* A key of an `UpdateResult` has this prefix if and only if the
* corresponding value represents `TypenamesUpdateResult`s.
*/
TYPENAMES: "typenames_",
/** /**
* A key of an `UpdateResult` has this prefix if and only if the * A key of an `UpdateResult` has this prefix if and only if the
* corresponding value represents `OwnDataUpdateResult`s. * corresponding value represents `OwnDataUpdateResult`s.

View File

@ -13,6 +13,8 @@ import {
_inTransaction, _inTransaction,
_makeSingleUpdateFunction, _makeSingleUpdateFunction,
Mirror, Mirror,
type _OwnDataUpdateResult,
type _TypenamesUpdateResult,
} from "./mirror"; } from "./mirror";
import stringify from "json-stable-stringify"; import stringify from "json-stable-stringify";
@ -799,6 +801,20 @@ describe("graphql/mirror", () => {
}).toThrow('Bad key in query result: "wat_0"'); }).toThrow('Bad key in query result: "wat_0"');
}); });
it("throws if given a key with typename results", () => {
const db = new Database(":memory:");
const mirror = new Mirror(db, buildGithubSchema());
const updateId = mirror._createUpdate(new Date(123));
const result = {
typenames_0: ([
{id: "issue:#1", __typename: "Issue"},
]: _TypenamesUpdateResult),
};
expect(() => {
mirror._nontransactionallyUpdateData(updateId, result);
}).toThrow("Typename update results not yet supported");
});
// We test the happy path lightly, because it just delegates to // We test the happy path lightly, because it just delegates to
// other methods, which are themselves tested. This test is // other methods, which are themselves tested. This test is
// sufficient to effect full coverage. // sufficient to effect full coverage.
@ -810,14 +826,14 @@ describe("graphql/mirror", () => {
mirror.registerObject({typename: "Issue", id: "issue:#1"}); mirror.registerObject({typename: "Issue", id: "issue:#1"});
mirror.registerObject({typename: "Issue", id: "issue:#2"}); mirror.registerObject({typename: "Issue", id: "issue:#2"});
const result = { const result = {
owndata_0: [ owndata_0: ([
{ {
__typename: "Repository", __typename: "Repository",
id: "repo:foo/bar", id: "repo:foo/bar",
url: "url://foo/bar", url: "url://foo/bar",
}, },
], ]: _OwnDataUpdateResult),
owndata_1: [ owndata_1: ([
{ {
__typename: "Issue", __typename: "Issue",
id: "issue:#1", id: "issue:#1",
@ -846,7 +862,7 @@ describe("graphql/mirror", () => {
id: "user:alice", id: "user:alice",
}, },
}, },
], ]: _OwnDataUpdateResult),
node_0: { node_0: {
id: "repo:foo/bar", id: "repo:foo/bar",
issues: { issues: {
@ -3797,13 +3813,13 @@ describe("graphql/mirror", () => {
const rowid = mirror._logRequest(query, queryParameters, new Date(123)); const rowid = mirror._logRequest(query, queryParameters, new Date(123));
const response = { const response = {
owndata_0: [ owndata_0: ([
{ {
__typename: "Repository", __typename: "Repository",
id: "repo:foo/bar", id: "repo:foo/bar",
url: "url://foo/bar", url: "url://foo/bar",
}, },
], ]: _OwnDataUpdateResult),
}; };
mirror._logResponse(rowid, response, new Date(456)); mirror._logResponse(rowid, response, new Date(456));