From 9a4c91887b91db0fefbb511ef89136191b668df1 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Wed, 26 Sep 2018 21:24:30 -0700 Subject: [PATCH] mirror: include typenames in extracted data (#897) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/graphql/mirror.js | 11 ++++++++-- src/graphql/mirror.test.js | 45 +++++++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/graphql/mirror.js b/src/graphql/mirror.js index 18b6f22..ffeb735 100644 --- a/src/graphql/mirror.js +++ b/src/graphql/mirror.js @@ -964,24 +964,30 @@ export class Mirror { * For instance, the result of `extract("issue:1")` might be: * * { + * __typename: "Issue", * id: "issue:1172", * title: "bug: holding causes CPU to overheat", * body: "We should fix this immediately.", * author: { + * __typename: "User", * id: "user:admin", * login: "admin", * }, * comments: [ * { + * __typename: "IssueComment", * body: "I depend on this behavior; please do not change it.", * author: { + * __typename: "User", * id: "user:longtimeuser4", * login: "longtimeuser4", * }, * }, * { + * __typename: "IssueComment", * body: "That's horrifying.", * author: { + * __typename: "User", * id: "user:admin", * login: "admin", * }, @@ -1090,8 +1096,8 @@ export class Mirror { // Constructing the result set inherently requires mutation, // because the object graph can have cycles. We start by // creating a record for each object, with just that object's - // primitive data. Then, we link in node references and - // connection entries. + // primitive data and typename. Then, we link in node references + // and connection entries. const allObjects: Map = new Map(); for (const typename of typenames) { const objectType = this._schemaInfo.objectTypes[typename]; @@ -1125,6 +1131,7 @@ export class Mirror { for (const row of rows) { const object = {}; object.id = row.id; + object.__typename = typename; for (const key of Object.keys(row)) { if (key === "id") continue; object[key] = JSON.parse(row[key]); diff --git a/src/graphql/mirror.test.js b/src/graphql/mirror.test.js index 735baaf..fe2eecf 100644 --- a/src/graphql/mirror.test.js +++ b/src/graphql/mirror.test.js @@ -1405,16 +1405,19 @@ describe("graphql/mirror", () => { }); } type Caveman = {| + +__typename: "Caveman", +id: string, +only: mixed, +primitives: mixed, |}; type Feline = {| + +__typename: "Feline", +id: string, +only: null | Feline, +lynx: null | Feline, |}; type Socket = {| + +__typename: "Socket", +id: string, +only: $ReadOnlyArray, +connections: $ReadOnlyArray, @@ -1551,6 +1554,7 @@ describe("graphql/mirror", () => { ]); const result: Caveman = (mirror.extract("brog"): any); expect(result).toEqual({ + __typename: "Caveman", id: "brog", only: "ugg", primitives: "ook", @@ -1586,12 +1590,15 @@ describe("graphql/mirror", () => { ]); const result = mirror.extract("alpha"); expect(result).toEqual({ + __typename: "Feline", id: "alpha", only: null, lynx: { + __typename: "Feline", id: "beta", only: null, lynx: { + __typename: "Feline", id: "gamma", only: null, lynx: null, @@ -1634,15 +1641,27 @@ describe("graphql/mirror", () => { updateConnection("localhost:6060", "connections", []); const result = mirror.extract("localhost:8080"); expect(result).toEqual({ + __typename: "Socket", id: "localhost:8080", only: [], connections: [ { + __typename: "Socket", id: "localhost:7070", only: [], 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)); mirror._updateOwnData(updateId, [{__typename: "Empty", id: "mt"}]); const result = mirror.extract("mt"); - expect(result).toEqual({id: "mt"}); + expect(result).toEqual({__typename: "Empty", id: "mt"}); }); it("handles boolean primitives", () => { @@ -1668,6 +1687,7 @@ describe("graphql/mirror", () => { {__typename: "Caveman", id: "brog", only: false, primitives: true}, ]); expect(mirror.extract("brog")).toEqual({ + __typename: "Caveman", id: "brog", only: false, primitives: true, @@ -1683,6 +1703,7 @@ describe("graphql/mirror", () => { {__typename: "Caveman", id: "brog", only: null, primitives: null}, ]); expect(mirror.extract("brog")).toEqual({ + __typename: "Caveman", id: "brog", only: null, primitives: null, @@ -1698,6 +1719,7 @@ describe("graphql/mirror", () => { {__typename: "Caveman", id: "brog", only: 123, primitives: 987}, ]); expect(mirror.extract("brog")).toEqual({ + __typename: "Caveman", id: "brog", only: 123, primitives: 987, @@ -1733,12 +1755,15 @@ describe("graphql/mirror", () => { ]); const result: Feline = (mirror.extract("alpha"): any); expect(result).toEqual({ + __typename: "Feline", id: "alpha", only: null, lynx: { + __typename: "Feline", id: "beta", only: result.lynx, lynx: { + __typename: "Feline", id: "gamma", only: result.lynx, lynx: null, @@ -1784,16 +1809,19 @@ describe("graphql/mirror", () => { updateConnection("localhost:6060", "connections", ["localhost:7070"]); const result: Socket = (mirror.extract("localhost:8080"): any); expect(result).toEqual({ + __typename: "Socket", id: "localhost:8080", only: [], connections: [ { + __typename: "Socket", id: "localhost:7070", only: [], connections: [ result, result.connections[0], { + __typename: "Socket", id: "localhost:6060", only: [], connections: [result.connections[0]], @@ -1838,6 +1866,7 @@ describe("graphql/mirror", () => { }); const result: Socket = (mirror.extract("localhost"): any); expect(result).toEqual({ + __typename: "Socket", id: "localhost", only: [], connections: [null, result, null, result, result, null], @@ -2071,13 +2100,16 @@ describe("graphql/mirror", () => { const result = mirror.extract("repo:foo/bar"); expect(result).toEqual({ + __typename: "Repository", id: "repo:foo/bar", url: "url://foo/bar", issues: [ { + __typename: "Issue", id: "issue:#1", url: "url://issue/1", author: { + __typename: "User", id: "user:alice", url: "url://alice", login: "alice", @@ -2088,6 +2120,7 @@ describe("graphql/mirror", () => { timeline: [], }, { + __typename: "Issue", id: "issue:#2", url: "url://issue/2", author: null, @@ -2096,18 +2129,22 @@ describe("graphql/mirror", () => { "by the time you read this, I will have deleted my account", comments: [ { + __typename: "IssueComment", id: "comment:#2.1", body: "cya", author: { + __typename: "User", id: "user:alice", url: "url://alice", login: "alice", }, }, { + __typename: "IssueComment", id: "comment:#2.2", body: "alas, I did not know them well", author: { + __typename: "User", id: "user:bob", url: "url://bob", login: "bob", @@ -2116,8 +2153,10 @@ describe("graphql/mirror", () => { ], timeline: [ { + __typename: "ClosedEvent", id: "issue:#2!closed#0", actor: { + __typename: "User", id: "user:alice", url: "url://alice", login: "alice",