mirror: include typenames in extracted data (#897)

Summary:
These typenames are often superfluous, but sometimes they are useful.
For instance, we might want to fetch the same data for `User`s, `Bot`s,
and `Organization`s, but still differentiate which kind of node we
fetched from an `Actor` union reference. Similarly, many timeline events
may have similar signatures (like, “issue closed” vs. “issue reopened”).

Test Plan:
Existing unit tests have been updated; run `yarn unit`.

wchargin-branch: mirror-extract-typenames
This commit is contained in:
William Chargin 2018-09-26 21:24:30 -07:00 committed by GitHub
parent c7ba89b807
commit 9a4c91887b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 5 deletions

View File

@ -964,24 +964,30 @@ export class Mirror {
* For instance, the result of `extract("issue:1")` might be: * For instance, the result of `extract("issue:1")` might be:
* *
* { * {
* __typename: "Issue",
* id: "issue:1172", * id: "issue:1172",
* title: "bug: holding <Space> causes CPU to overheat", * title: "bug: holding <Space> causes CPU to overheat",
* body: "We should fix this immediately.", * body: "We should fix this immediately.",
* author: { * author: {
* __typename: "User",
* id: "user:admin", * id: "user:admin",
* login: "admin", * login: "admin",
* }, * },
* comments: [ * comments: [
* { * {
* __typename: "IssueComment",
* body: "I depend on this behavior; please do not change it.", * body: "I depend on this behavior; please do not change it.",
* author: { * author: {
* __typename: "User",
* id: "user:longtimeuser4", * id: "user:longtimeuser4",
* login: "longtimeuser4", * login: "longtimeuser4",
* }, * },
* }, * },
* { * {
* __typename: "IssueComment",
* body: "That's horrifying.", * body: "That's horrifying.",
* author: { * author: {
* __typename: "User",
* id: "user:admin", * id: "user:admin",
* login: "admin", * login: "admin",
* }, * },
@ -1090,8 +1096,8 @@ export class Mirror {
// Constructing the result set inherently requires mutation, // Constructing the result set inherently requires mutation,
// because the object graph can have cycles. We start by // because the object graph can have cycles. We start by
// creating a record for each object, with just that object's // creating a record for each object, with just that object's
// primitive data. Then, we link in node references and // primitive data and typename. Then, we link in node references
// connection entries. // and connection entries.
const allObjects: Map<Schema.ObjectId, Object> = new Map(); const allObjects: Map<Schema.ObjectId, Object> = new Map();
for (const typename of typenames) { for (const typename of typenames) {
const objectType = this._schemaInfo.objectTypes[typename]; const objectType = this._schemaInfo.objectTypes[typename];
@ -1125,6 +1131,7 @@ export class Mirror {
for (const row of rows) { for (const row of rows) {
const object = {}; const object = {};
object.id = row.id; object.id = row.id;
object.__typename = typename;
for (const key of Object.keys(row)) { for (const key of Object.keys(row)) {
if (key === "id") continue; if (key === "id") continue;
object[key] = JSON.parse(row[key]); object[key] = JSON.parse(row[key]);

View File

@ -1405,16 +1405,19 @@ describe("graphql/mirror", () => {
}); });
} }
type Caveman = {| type Caveman = {|
+__typename: "Caveman",
+id: string, +id: string,
+only: mixed, +only: mixed,
+primitives: mixed, +primitives: mixed,
|}; |};
type Feline = {| type Feline = {|
+__typename: "Feline",
+id: string, +id: string,
+only: null | Feline, +only: null | Feline,
+lynx: null | Feline, +lynx: null | Feline,
|}; |};
type Socket = {| type Socket = {|
+__typename: "Socket",
+id: string, +id: string,
+only: $ReadOnlyArray<null | Socket>, +only: $ReadOnlyArray<null | Socket>,
+connections: $ReadOnlyArray<null | Socket>, +connections: $ReadOnlyArray<null | Socket>,
@ -1551,6 +1554,7 @@ describe("graphql/mirror", () => {
]); ]);
const result: Caveman = (mirror.extract("brog"): any); const result: Caveman = (mirror.extract("brog"): any);
expect(result).toEqual({ expect(result).toEqual({
__typename: "Caveman",
id: "brog", id: "brog",
only: "ugg", only: "ugg",
primitives: "ook", primitives: "ook",
@ -1586,12 +1590,15 @@ describe("graphql/mirror", () => {
]); ]);
const result = mirror.extract("alpha"); const result = mirror.extract("alpha");
expect(result).toEqual({ expect(result).toEqual({
__typename: "Feline",
id: "alpha", id: "alpha",
only: null, only: null,
lynx: { lynx: {
__typename: "Feline",
id: "beta", id: "beta",
only: null, only: null,
lynx: { lynx: {
__typename: "Feline",
id: "gamma", id: "gamma",
only: null, only: null,
lynx: null, lynx: null,
@ -1634,15 +1641,27 @@ describe("graphql/mirror", () => {
updateConnection("localhost:6060", "connections", []); updateConnection("localhost:6060", "connections", []);
const result = mirror.extract("localhost:8080"); const result = mirror.extract("localhost:8080");
expect(result).toEqual({ expect(result).toEqual({
__typename: "Socket",
id: "localhost:8080", id: "localhost:8080",
only: [], only: [],
connections: [ connections: [
{ {
__typename: "Socket",
id: "localhost:7070", id: "localhost:7070",
only: [], only: [],
connections: [ connections: [
{id: "localhost:6060", only: [], connections: []}, {
{id: "localhost:6060", only: [], connections: []}, __typename: "Socket",
id: "localhost:6060",
only: [],
connections: [],
},
{
__typename: "Socket",
id: "localhost:6060",
only: [],
connections: [],
},
], ],
}, },
], ],
@ -1656,7 +1675,7 @@ describe("graphql/mirror", () => {
const updateId = mirror._createUpdate(new Date(123)); const updateId = mirror._createUpdate(new Date(123));
mirror._updateOwnData(updateId, [{__typename: "Empty", id: "mt"}]); mirror._updateOwnData(updateId, [{__typename: "Empty", id: "mt"}]);
const result = mirror.extract("mt"); const result = mirror.extract("mt");
expect(result).toEqual({id: "mt"}); expect(result).toEqual({__typename: "Empty", id: "mt"});
}); });
it("handles boolean primitives", () => { it("handles boolean primitives", () => {
@ -1668,6 +1687,7 @@ describe("graphql/mirror", () => {
{__typename: "Caveman", id: "brog", only: false, primitives: true}, {__typename: "Caveman", id: "brog", only: false, primitives: true},
]); ]);
expect(mirror.extract("brog")).toEqual({ expect(mirror.extract("brog")).toEqual({
__typename: "Caveman",
id: "brog", id: "brog",
only: false, only: false,
primitives: true, primitives: true,
@ -1683,6 +1703,7 @@ describe("graphql/mirror", () => {
{__typename: "Caveman", id: "brog", only: null, primitives: null}, {__typename: "Caveman", id: "brog", only: null, primitives: null},
]); ]);
expect(mirror.extract("brog")).toEqual({ expect(mirror.extract("brog")).toEqual({
__typename: "Caveman",
id: "brog", id: "brog",
only: null, only: null,
primitives: null, primitives: null,
@ -1698,6 +1719,7 @@ describe("graphql/mirror", () => {
{__typename: "Caveman", id: "brog", only: 123, primitives: 987}, {__typename: "Caveman", id: "brog", only: 123, primitives: 987},
]); ]);
expect(mirror.extract("brog")).toEqual({ expect(mirror.extract("brog")).toEqual({
__typename: "Caveman",
id: "brog", id: "brog",
only: 123, only: 123,
primitives: 987, primitives: 987,
@ -1733,12 +1755,15 @@ describe("graphql/mirror", () => {
]); ]);
const result: Feline = (mirror.extract("alpha"): any); const result: Feline = (mirror.extract("alpha"): any);
expect(result).toEqual({ expect(result).toEqual({
__typename: "Feline",
id: "alpha", id: "alpha",
only: null, only: null,
lynx: { lynx: {
__typename: "Feline",
id: "beta", id: "beta",
only: result.lynx, only: result.lynx,
lynx: { lynx: {
__typename: "Feline",
id: "gamma", id: "gamma",
only: result.lynx, only: result.lynx,
lynx: null, lynx: null,
@ -1784,16 +1809,19 @@ describe("graphql/mirror", () => {
updateConnection("localhost:6060", "connections", ["localhost:7070"]); updateConnection("localhost:6060", "connections", ["localhost:7070"]);
const result: Socket = (mirror.extract("localhost:8080"): any); const result: Socket = (mirror.extract("localhost:8080"): any);
expect(result).toEqual({ expect(result).toEqual({
__typename: "Socket",
id: "localhost:8080", id: "localhost:8080",
only: [], only: [],
connections: [ connections: [
{ {
__typename: "Socket",
id: "localhost:7070", id: "localhost:7070",
only: [], only: [],
connections: [ connections: [
result, result,
result.connections[0], result.connections[0],
{ {
__typename: "Socket",
id: "localhost:6060", id: "localhost:6060",
only: [], only: [],
connections: [result.connections[0]], connections: [result.connections[0]],
@ -1838,6 +1866,7 @@ describe("graphql/mirror", () => {
}); });
const result: Socket = (mirror.extract("localhost"): any); const result: Socket = (mirror.extract("localhost"): any);
expect(result).toEqual({ expect(result).toEqual({
__typename: "Socket",
id: "localhost", id: "localhost",
only: [], only: [],
connections: [null, result, null, result, result, null], connections: [null, result, null, result, result, null],
@ -2071,13 +2100,16 @@ describe("graphql/mirror", () => {
const result = mirror.extract("repo:foo/bar"); const result = mirror.extract("repo:foo/bar");
expect(result).toEqual({ expect(result).toEqual({
__typename: "Repository",
id: "repo:foo/bar", id: "repo:foo/bar",
url: "url://foo/bar", url: "url://foo/bar",
issues: [ issues: [
{ {
__typename: "Issue",
id: "issue:#1", id: "issue:#1",
url: "url://issue/1", url: "url://issue/1",
author: { author: {
__typename: "User",
id: "user:alice", id: "user:alice",
url: "url://alice", url: "url://alice",
login: "alice", login: "alice",
@ -2088,6 +2120,7 @@ describe("graphql/mirror", () => {
timeline: [], timeline: [],
}, },
{ {
__typename: "Issue",
id: "issue:#2", id: "issue:#2",
url: "url://issue/2", url: "url://issue/2",
author: null, author: null,
@ -2096,18 +2129,22 @@ describe("graphql/mirror", () => {
"by the time you read this, I will have deleted my account", "by the time you read this, I will have deleted my account",
comments: [ comments: [
{ {
__typename: "IssueComment",
id: "comment:#2.1", id: "comment:#2.1",
body: "cya", body: "cya",
author: { author: {
__typename: "User",
id: "user:alice", id: "user:alice",
url: "url://alice", url: "url://alice",
login: "alice", login: "alice",
}, },
}, },
{ {
__typename: "IssueComment",
id: "comment:#2.2", id: "comment:#2.2",
body: "alas, I did not know them well", body: "alas, I did not know them well",
author: { author: {
__typename: "User",
id: "user:bob", id: "user:bob",
url: "url://bob", url: "url://bob",
login: "bob", login: "bob",
@ -2116,8 +2153,10 @@ describe("graphql/mirror", () => {
], ],
timeline: [ timeline: [
{ {
__typename: "ClosedEvent",
id: "issue:#2!closed#0", id: "issue:#2!closed#0",
actor: { actor: {
__typename: "User",
id: "user:alice", id: "user:alice",
url: "url://alice", url: "url://alice",
login: "alice", login: "alice",