mirror of
https://github.com/status-im/sourcecred.git
synced 2025-01-13 22:25:47 +00:00
mirror: precompute some useful schema info (#857)
Summary: This is mostly useful not for computational efficiency, but for ease of implementation: there end up being multiple places where we want to find (say) the primitive fields on an object, and having to go through the whole iterate-and-switch-and-push process repeatedly is annoying. Test Plan: Unit tests included, with full coverage; run `yarn unit`. wchargin-branch: mirror-schema-info
This commit is contained in:
parent
1b1a1e4d46
commit
e69ff57c58
@ -12,6 +12,7 @@ import * as Schema from "./schema";
|
|||||||
export class Mirror {
|
export class Mirror {
|
||||||
+_db: Database;
|
+_db: Database;
|
||||||
+_schema: Schema.Schema;
|
+_schema: Schema.Schema;
|
||||||
|
+_schemaInfo: SchemaInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a GraphQL mirror using the given database connection and
|
* Create a GraphQL mirror using the given database connection and
|
||||||
@ -33,6 +34,7 @@ export class Mirror {
|
|||||||
if (schema == null) throw new Error("schema: " + String(schema));
|
if (schema == null) throw new Error("schema: " + String(schema));
|
||||||
this._db = db;
|
this._db = db;
|
||||||
this._schema = schema;
|
this._schema = schema;
|
||||||
|
this._schemaInfo = _buildSchemaInfo(this._schema);
|
||||||
this._initialize();
|
this._initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,6 +278,98 @@ export class Mirror {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decomposition of a schema, grouping types by their kind (object vs.
|
||||||
|
* union) and object fields by their kind (primitive vs. link vs.
|
||||||
|
* connection).
|
||||||
|
*
|
||||||
|
* All arrays contain elements in arbitrary order.
|
||||||
|
*/
|
||||||
|
type SchemaInfo = {|
|
||||||
|
+objectTypes: {|
|
||||||
|
+[Schema.Typename]: {|
|
||||||
|
+fields: {|+[Schema.Fieldname]: Schema.FieldType|},
|
||||||
|
+primitiveFieldNames: $ReadOnlyArray<Schema.Fieldname>,
|
||||||
|
+linkFieldNames: $ReadOnlyArray<Schema.Fieldname>,
|
||||||
|
+connectionFieldNames: $ReadOnlyArray<Schema.Fieldname>,
|
||||||
|
// There is always exactly one ID field, so it needs no
|
||||||
|
// special representation. (It's still included in the `fields`
|
||||||
|
// dictionary, though.)
|
||||||
|
|},
|
||||||
|
|},
|
||||||
|
+unionTypes: {|
|
||||||
|
+[Schema.Fieldname]: {|
|
||||||
|
+clauses: $ReadOnlyArray<Schema.Typename>,
|
||||||
|
|},
|
||||||
|
|},
|
||||||
|
|};
|
||||||
|
|
||||||
|
export function _buildSchemaInfo(schema: Schema.Schema): SchemaInfo {
|
||||||
|
const result = {
|
||||||
|
objectTypes: (({}: any): {|
|
||||||
|
[Schema.Typename]: {|
|
||||||
|
+fields: {|+[Schema.Fieldname]: Schema.FieldType|},
|
||||||
|
+primitiveFieldNames: Array<Schema.Fieldname>,
|
||||||
|
+linkFieldNames: Array<Schema.Fieldname>,
|
||||||
|
+connectionFieldNames: Array<Schema.Fieldname>,
|
||||||
|
|},
|
||||||
|
|}),
|
||||||
|
unionTypes: (({}: any): {|
|
||||||
|
[Schema.Fieldname]: {|
|
||||||
|
+clauses: $ReadOnlyArray<Schema.Typename>,
|
||||||
|
|},
|
||||||
|
|}),
|
||||||
|
};
|
||||||
|
for (const typename of Object.keys(schema)) {
|
||||||
|
const type = schema[typename];
|
||||||
|
switch (type.type) {
|
||||||
|
case "OBJECT": {
|
||||||
|
const entry: {|
|
||||||
|
+fields: {|+[Schema.Fieldname]: Schema.FieldType|},
|
||||||
|
+primitiveFieldNames: Array<Schema.Fieldname>,
|
||||||
|
+linkFieldNames: Array<Schema.Fieldname>,
|
||||||
|
+connectionFieldNames: Array<Schema.Fieldname>,
|
||||||
|
|} = {
|
||||||
|
fields: type.fields,
|
||||||
|
primitiveFieldNames: [],
|
||||||
|
linkFieldNames: [],
|
||||||
|
connectionFieldNames: [],
|
||||||
|
};
|
||||||
|
result.objectTypes[typename] = entry;
|
||||||
|
for (const fieldname of Object.keys(type.fields)) {
|
||||||
|
const field = type.fields[fieldname];
|
||||||
|
switch (field.type) {
|
||||||
|
case "ID":
|
||||||
|
break;
|
||||||
|
case "PRIMITIVE":
|
||||||
|
entry.primitiveFieldNames.push(fieldname);
|
||||||
|
break;
|
||||||
|
case "NODE":
|
||||||
|
entry.linkFieldNames.push(fieldname);
|
||||||
|
break;
|
||||||
|
case "CONNECTION":
|
||||||
|
entry.connectionFieldNames.push(fieldname);
|
||||||
|
break;
|
||||||
|
// istanbul ignore next
|
||||||
|
default:
|
||||||
|
throw new Error((field.type: empty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "UNION": {
|
||||||
|
const entry = {clauses: Object.keys(type.clauses)};
|
||||||
|
result.unionTypes[typename] = entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// istanbul ignore next
|
||||||
|
default:
|
||||||
|
throw new Error((type.type: empty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a function inside a database transaction.
|
* Execute a function inside a database transaction.
|
||||||
*
|
*
|
||||||
|
@ -5,7 +5,7 @@ import fs from "fs";
|
|||||||
import tmp from "tmp";
|
import tmp from "tmp";
|
||||||
|
|
||||||
import * as Schema from "./schema";
|
import * as Schema from "./schema";
|
||||||
import {_inTransaction, Mirror} from "./mirror";
|
import {_buildSchemaInfo, _inTransaction, Mirror} from "./mirror";
|
||||||
|
|
||||||
describe("graphql/mirror", () => {
|
describe("graphql/mirror", () => {
|
||||||
function buildGithubSchema(): Schema.Schema {
|
function buildGithubSchema(): Schema.Schema {
|
||||||
@ -175,6 +175,41 @@ describe("graphql/mirror", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("_buildSchemaInfo", () => {
|
||||||
|
it("processes object types properly", () => {
|
||||||
|
const result = _buildSchemaInfo(buildGithubSchema());
|
||||||
|
expect(Object.keys(result.objectTypes).sort()).toEqual(
|
||||||
|
[
|
||||||
|
"Repository",
|
||||||
|
"Issue",
|
||||||
|
"IssueComment",
|
||||||
|
"User",
|
||||||
|
"Bot",
|
||||||
|
"Organization",
|
||||||
|
].sort()
|
||||||
|
);
|
||||||
|
expect(result.objectTypes["Issue"].fields).toEqual(
|
||||||
|
(buildGithubSchema().Issue: any).fields
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
result.objectTypes["Issue"].primitiveFieldNames.slice().sort()
|
||||||
|
).toEqual(["url", "title"].sort());
|
||||||
|
expect(result.objectTypes["Issue"].linkFieldNames.slice().sort()).toEqual(
|
||||||
|
["author", "parent"].sort()
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
result.objectTypes["Issue"].connectionFieldNames.slice().sort()
|
||||||
|
).toEqual(["comments"].sort());
|
||||||
|
});
|
||||||
|
it("processes union types correctly", () => {
|
||||||
|
const result = _buildSchemaInfo(buildGithubSchema());
|
||||||
|
expect(Object.keys(result.unionTypes).sort()).toEqual(["Actor"].sort());
|
||||||
|
expect(result.unionTypes["Actor"].clauses.slice().sort()).toEqual(
|
||||||
|
["User", "Bot", "Organization"].sort()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("_inTransaction", () => {
|
describe("_inTransaction", () => {
|
||||||
it("runs its callback inside a transaction", () => {
|
it("runs its callback inside a transaction", () => {
|
||||||
// We use an on-disk database file here because we need to open
|
// We use an on-disk database file here because we need to open
|
||||||
|
Loading…
x
Reference in New Issue
Block a user