diff --git a/src/graphql/mirror.js b/src/graphql/mirror.js index 813d563..9fc4d62 100644 --- a/src/graphql/mirror.js +++ b/src/graphql/mirror.js @@ -1056,6 +1056,8 @@ export class Mirror { case "CONNECTION": // Not handled by this function. return null; + case "NESTED": + throw new Error("Nested fields not supported."); // istanbul ignore next default: throw new Error((field.type: empty)); @@ -1563,6 +1565,8 @@ export function _buildSchemaInfo(schema: Schema.Schema): SchemaInfo { case "CONNECTION": entry.connectionFieldNames.push(fieldname); break; + case "NESTED": + throw new Error("Nested fields not supported."); // istanbul ignore next default: throw new Error((field.type: empty)); diff --git a/src/graphql/schema.js b/src/graphql/schema.js index 437a0bc..ed779fc 100644 --- a/src/graphql/schema.js +++ b/src/graphql/schema.js @@ -31,15 +31,41 @@ export type ObjectId = string; // represented as `PRIMITIVE`s (except for `ID`s). // - Connections are supported as object fields, but arbitrary lists // are not. +// +// To accommodate schemata where some object types do not have IDs, +// objects may have "nested" fields of primitive or node-reference type. +// These may be nested to depth exactly 1. Suppose that `Foo` is an +// object type that includes `bar: Bar!`, but `Bar` is an object type +// without an `id`. Then `Bar` may not be a first-class type, but `Foo` +// may pull properties off of it using +// +// bar: nested({x: primitive(), y: node("Baz")}); +// +// The property "bar" in the above example is called a _nested_ +// property, and its fields "x" and "y" are called _eggs_. (The nest +// contains the eggs.) export type Schema = {+[Typename]: NodeType}; export type NodeType = | {|+type: "OBJECT", +fields: {|+[Fieldname]: FieldType|}|} | {|+type: "UNION", +clauses: {|+[Typename]: true|}|}; + export type FieldType = - | {|+type: "ID"|} - | {|+type: "PRIMITIVE"|} - | {|+type: "NODE", +elementType: Typename|} - | {|+type: "CONNECTION", +elementType: Typename|}; + | IdFieldType + | PrimitiveFieldType + | NodeFieldType + | ConnectionFieldType + | NestedFieldType; +export type IdFieldType = {|+type: "ID"|}; +export type PrimitiveFieldType = {|+type: "PRIMITIVE"|}; +export type NodeFieldType = {|+type: "NODE", +elementType: Typename|}; +export type ConnectionFieldType = {| + +type: "CONNECTION", + +elementType: Typename, +|}; +export type NestedFieldType = {| + +type: "NESTED", + +eggs: {+[Fieldname]: PrimitiveFieldType | NodeFieldType}, +|}; // Every object must have exactly one `id` field, and it must have this // name. @@ -111,18 +137,24 @@ export function union(clauses: $ReadOnlyArray): NodeType { return {type: "UNION", clauses: clausesMap}; } -export function id(): FieldType { +export function id(): IdFieldType { return {type: "ID"}; } -export function primitive(): FieldType { +export function primitive(): PrimitiveFieldType { return {type: "PRIMITIVE"}; } -export function node(elementType: Typename): FieldType { +export function node(elementType: Typename): NodeFieldType { return {type: "NODE", elementType}; } -export function connection(elementType: Typename): FieldType { +export function connection(elementType: Typename): ConnectionFieldType { return {type: "CONNECTION", elementType}; } + +export function nested(eggs: { + +[Fieldname]: PrimitiveFieldType | NodeFieldType, +}): NestedFieldType { + return {type: "NESTED", eggs: {...eggs}}; +} diff --git a/src/graphql/schema.test.js b/src/graphql/schema.test.js index 8459a08..b1e44fe 100644 --- a/src/graphql/schema.test.js +++ b/src/graphql/schema.test.js @@ -19,6 +19,14 @@ describe("graphql/schema", () => { title: s.primitive(), comments: s.connection("IssueComment"), }), + Commit: s.object({ + id: s.id(), + oid: s.primitive(), + author: /* GitActor */ s.nested({ + date: s.primitive(), + user: s.node("User"), + }), + }), IssueComment: s.object({ id: s.id(), body: s.primitive(),