add graphql-relay typings

This commit is contained in:
Alexandro
2016-11-27 16:22:19 +07:00
parent 3e9ed9d35f
commit d6442eb9f2
4 changed files with 562 additions and 0 deletions
+230
View File
@@ -0,0 +1,230 @@
import {
GraphQLString,
GraphQLObjectType,
GraphQLFieldResolver,
GraphQLFieldConfigMap,
GraphQLResolveInfo,
GraphQLTypeResolver,
GraphQLFieldConfig,
GraphQLInterfaceType,
GraphQLInputType,
GraphQLInputFieldConfigMap,
GraphQLNonNull,
GraphQLID,
} from "graphql";
import {
// Connections
connectionArgs,
forwardConnectionArgs,
backwardConnectionArgs,
connectionDefinitions,
connectionFromArray,
connectionFromPromisedArray,
cursorForObjectInConnection,
// Object Identification
nodeDefinitions,
toGlobalId,
fromGlobalId,
globalIdField,
pluralIdentifyingRootField,
// Mutations
mutationWithClientMutationId,
} from ".";
// Connections
// connectionArgs returns the arguments that fields should provide when they return a connection type that supports bidirectional pagination.
connectionArgs.first = 10;
connectionArgs.after = "a";
connectionArgs.before = "b";
connectionArgs.last = 10;
// forwardConnectionArgs returns the arguments that fields should provide when they return a connection type that only supports forward pagination.
forwardConnectionArgs.after = "a";
forwardConnectionArgs.first = 10;
// backwardConnectionArgs returns the arguments that fields should provide when they return a connection type that only supports backward pagination.
backwardConnectionArgs.before = "b";
backwardConnectionArgs.last = 10;
// connectionDefinitions returns a connectionType and its associated edgeType, given a node type.
let resolve: GraphQLFieldResolver<any, any> = (source, args, context, info) => {
info.fieldName = "f";
};
const fields: GraphQLFieldConfigMap<any, any> = {};
let t: GraphQLObjectType;
let e: GraphQLObjectType;
let def = connectionDefinitions({
connectionFields: fields,
edgeFields: fields,
name: "N",
nodeType: new GraphQLObjectType({
name: "N",
fields: {},
}),
resolveCursor: resolve,
resolveNode: resolve,
});
t = def.connectionType;
e = def.edgeType;
// connectionFromArray is a helper method that takes an array and the arguments from connectionArgs, does pagination and filtering, and returns an object in the shape expected by a connectionType's resolve function.
const conn = connectionFromArray([1, 2, 3], {
after: "a",
before: "b",
first: 1,
last: 5,
});
conn.edges.map((e) => { e.cursor.toLowerCase(); e.node.toExponential(); });
conn.pageInfo.endCursor = "e";
conn.pageInfo.hasNextPage = true;
conn.pageInfo.hasPreviousPage = true;
conn.pageInfo.startCursor = "s";
// connectionFromPromisedArray is similar to connectionFromArray, but it takes a promise that resolves to an array, and returns a promise that resolves to the expected shape by connectionType.
const conn2 = connectionFromPromisedArray(new Promise<number[]>((resolve) => {
resolve();
}), {
after: "a",
before: "b",
first: 1,
last: 5,
});
conn2.then((res) => {
res = conn;
});
// cursorForObjectInConnection is a helper method that takes an array and a member object, and returns a cursor for use in the mutation payload.
cursorForObjectInConnection(["a"], "b").toLowerCase();
// An example usage of these methods from the test schema:
let shipType: GraphQLObjectType = new GraphQLObjectType({
name: "ShipType",
fields: {},
});
const {connectionType: ShipConnection} =
connectionDefinitions({ nodeType: shipType });
const factionType = new GraphQLObjectType({
name: "Faction",
fields: () => ({
ships: {
type: ShipConnection,
args: connectionArgs,
resolve: (faction, args) => connectionFromArray(
faction.ships.map((id: any) => id),
args
),
}
}),
});
// Object Identification
// nodeDefinitions returns the Node interface that objects can implement, and returns the node root field to include on the query type. To implement this, it takes a function to resolve an ID to an object, and to determine the type of a given object.
const resolver: GraphQLTypeResolver<any, any> = () => {
return new GraphQLObjectType({
name: "T",
fields: {},
});
};
const idFetcher = (id: string, context: number, info: GraphQLResolveInfo) => {
info.fieldName = "f";
};
const nodeDef = nodeDefinitions<number>(idFetcher, resolver);
const fieldConfig: GraphQLFieldConfig<any, any> = nodeDef.nodeField;
const interfaceType: GraphQLInterfaceType = nodeDef.nodeInterface;
// toGlobalId takes a type name and an ID specific to that type name, and returns a "global ID" that is unique among all types.
toGlobalId("t", "i").toLowerCase();
// fromGlobalId takes the "global ID" created by toGlobalID, and returns the type name and ID used to create it.
const fgi = fromGlobalId("gid");
fgi.id.toLowerCase();
fgi.type.toUpperCase();
// globalIdField creates the configuration for an id field on a node.
const idFetcher2 = (object: any, context: any, info: GraphQLResolveInfo) => {
return "";
};
const gif: GraphQLFieldConfig<any, any> = globalIdField("t", idFetcher2);
// pluralIdentifyingRootField creates a field that accepts a list of non-ID identifiers (like a username) and maps them to their corresponding objects.
const input: GraphQLInputType = GraphQLString;
const prf: GraphQLFieldConfig<any, any> = pluralIdentifyingRootField({
argName: "a",
inputType: input,
outputType: input,
resolveSingleInput: (input: any, context: any, info: GraphQLResolveInfo) => {
return "";
},
description: "d",
});
// An example usage of these methods from the test schema:
const {nodeInterface, nodeField} = nodeDefinitions(
(globalId) => {
var {type, id} = fromGlobalId(globalId);
return "data[type][id]";
},
(obj) => {
return obj.ships ? factionType : shipType;
}
);
const factionType2 = new GraphQLObjectType({
name: 'Faction',
fields: () => ({
id: globalIdField(),
}),
interfaces: [nodeInterface]
});
const queryType = new GraphQLObjectType({
name: 'Query',
fields: () => ({
node: nodeField
})
});
// Mutations
// mutationWithClientMutationId takes a name, input fields, output fields, and a mutation method to map from the input fields to the output fields, performing the mutation along the way. It then creates and returns a field configuration that can be used as a top-level field on the mutation type.
const gifcm: GraphQLInputFieldConfigMap = {};
const gfcm: GraphQLFieldConfigMap<any, any> = {};
mutationWithClientMutationId({
name: "M",
description: "D",
inputFields: gifcm,
mutateAndGetPayload: (object: any,
ctx: any,
info: GraphQLResolveInfo) => {
return new Promise<string>((resolve) => {
resolve(info.fieldName);
});
},
outputFields: gfcm,
});
// An example usage of these methods from the test schema:
const data: any = {};
var shipMutation = mutationWithClientMutationId({
name: 'IntroduceShip',
inputFields: {
shipName: {
type: new GraphQLNonNull(GraphQLString)
},
factionId: {
type: new GraphQLNonNull(GraphQLID)
}
},
outputFields: {
ship: {
type: shipType,
resolve: (payload) => data['Ship'][payload.shipId]
},
faction: {
type: factionType,
resolve: (payload) => data['Faction'][payload.factionId]
}
},
mutateAndGetPayload: ({shipName, factionId}) => {
var newShip = {
id: "11",
name: shipName
};
// data.Ship[newShip.id] = newShip;
// data.Faction[factionId].ships.push(newShip.id);
return {
shipId: newShip.id,
factionId,
};
}
});
var mutationType = new GraphQLObjectType({
name: 'Mutation',
fields: () => ({
introduceShip: shipMutation
})
});
+310
View File
@@ -0,0 +1,310 @@
// Type definitions for graphql-relay 0.4
// Project: https://github.com/graphql/graphql-relay-js
// Definitions by: Arvitaly <https://github.com/arvitaly>, nitintutlani <https://github.com/nitintutlani>, Grelinfo <https://github.com/Grelinfo>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
import {
GraphQLBoolean,
GraphQLInt,
GraphQLNonNull,
GraphQLList,
GraphQLObjectType,
GraphQLString,
GraphQLFieldConfig,
GraphQLInputFieldConfigMap,
GraphQLFieldConfigMap,
GraphQLFieldConfigArgumentMap,
GraphQLResolveInfo,
GraphQLInterfaceType,
GraphQLInputType,
GraphQLOutputType,
GraphQLFieldResolver,
GraphQLTypeResolver,
Thunk
} from "graphql";
// connection/connection.js
/**
* Returns a GraphQLFieldConfigArgumentMap appropriate to include on a field
* whose return type is a connection type with forward pagination.
*/
type ForwardConnectionArgs = {
after: ConnectionCursor;
first: number;
}
export const forwardConnectionArgs: GraphQLFieldConfigArgumentMap & {
after: ConnectionCursor;
first: number;
};
/**
* Returns a GraphQLFieldConfigArgumentMap appropriate to include on a field
* whose return type is a connection type with backward pagination.
*/
type BackwardConnectionArgs = {
before: ConnectionCursor;
last: number;
}
export const backwardConnectionArgs: GraphQLFieldConfigArgumentMap & {
before: ConnectionCursor;
last: number;
};
/**
* Returns a GraphQLFieldConfigArgumentMap appropriate to include on a field
* whose return type is a connection type with bidirectional pagination.
*/
export const connectionArgs: GraphQLFieldConfigArgumentMap & ForwardConnectionArgs & BackwardConnectionArgs;
type ConnectionConfig = {
name?: string | null,
nodeType: GraphQLObjectType,
resolveNode?: GraphQLFieldResolver<any, any> | null,
resolveCursor?: GraphQLFieldResolver<any, any> | null,
edgeFields?: Thunk<GraphQLFieldConfigMap<any, any>> | null,
connectionFields?: Thunk<GraphQLFieldConfigMap<any, any>> | null
};
type GraphQLConnectionDefinitions = {
edgeType: GraphQLObjectType,
connectionType: GraphQLObjectType
};
/**
* Returns a GraphQLObjectType for a connection with the given name,
* and whose nodes are of the specified type.
*/
export function connectionDefinitions(
config: ConnectionConfig
): GraphQLConnectionDefinitions;
// connection/connectiontypes.js
/**
* An flow type alias for cursors in this implementation.
*/
export type ConnectionCursor = string;
/**
* A flow type designed to be exposed as `PageInfo` over GraphQL.
*/
export type PageInfo = {
startCursor: ConnectionCursor,
endCursor: ConnectionCursor,
hasPreviousPage: boolean,
hasNextPage: boolean
};
/**
* A flow type designed to be exposed as a `Connection` over GraphQL.
*/
export type Connection<T> = {
edges: Array<Edge<T>>;
pageInfo: PageInfo;
};
/**
* A flow type designed to be exposed as a `Edge` over GraphQL.
*/
type Edge<T> = {
node: T;
cursor: ConnectionCursor;
};
/**
* A flow type describing the arguments a connection field receives in GraphQL.
*/
type ConnectionArguments = {
before?: ConnectionCursor;
after?: ConnectionCursor;
first?: number;
last?: number;
};
// connection/arrayconnection.js
type ArraySliceMetaInfo = {
sliceStart: number;
arrayLength: number;
};
/**
* A simple function that accepts an array and connection arguments, and returns
* a connection object for use in GraphQL. It uses array offsets as pagination,
* so pagination will only work if the array is static.
*/
export function connectionFromArray<T>(
data: Array<T>,
args: ConnectionArguments
): Connection<T>;
/**
* A version of `connectionFromArray` that takes a promised array, and returns a
* promised connection.
*/
export function connectionFromPromisedArray<T>(
dataPromise: Promise<Array<T>>,
args: ConnectionArguments
): Promise<Connection<T>>;
/**
* Given a slice (subset) of an array, returns a connection object for use in
* GraphQL.
*
* This function is similar to `connectionFromArray`, but is intended for use
* cases where you know the cardinality of the connection, consider it too large
* to materialize the entire array, and instead wish pass in a slice of the
* total result large enough to cover the range specified in `args`.
*/
export function connectionFromArraySlice<T>(
arraySlice: Array<T>,
args: ConnectionArguments,
meta: ArraySliceMetaInfo
): Connection<T>;
/**
* A version of `connectionFromArraySlice` that takes a promised array slice,
* and returns a promised connection.
*/
export function connectionFromPromisedArraySlice<T>(
dataPromise: Promise<Array<T>>,
args: ConnectionArguments,
arrayInfo: ArraySliceMetaInfo
): Promise<Connection<T>>;
/**
* Creates the cursor string from an offset.
*/
export function offsetToCursor(offset: number): ConnectionCursor;
/**
* Rederives the offset from the cursor string.
*/
export function cursorToOffset(cursor: ConnectionCursor): number;
/**
* Return the cursor associated with an object in an array.
*/
export function cursorForObjectInConnection<T>(
data: Array<T>,
object: T
): ConnectionCursor;
/**
* Given an optional cursor and a default offset, returns the offset
* to use; if the cursor contains a valid offset, that will be used,
* otherwise it will be the default.
*/
export function getOffsetWithDefault(
cursor?: ConnectionCursor,
defaultOffset?: number
): number;
// mutation/mutation.js
type mutationFn = (
object: any,
ctx: any,
info: GraphQLResolveInfo
) => Promise<any> | any;
/**
* A description of a mutation consumable by mutationWithClientMutationId
* to create a GraphQLFieldConfig for that mutation.
*
* The inputFields and outputFields should not include `clientMutationId`,
* as this will be provided automatically.
*
* An input object will be created containing the input fields, and an
* object will be created containing the output fields.
*
* mutateAndGetPayload will receieve an Object with a key for each
* input field, and it should return an Object with a key for each
* output field. It may return synchronously, or return a Promise.
*/
type MutationConfig = {
name: string,
description?: string
inputFields: Thunk<GraphQLInputFieldConfigMap>,
outputFields: Thunk<GraphQLFieldConfigMap<any, any>>,
mutateAndGetPayload: mutationFn,
};
/**
* Returns a GraphQLFieldConfig for the mutation described by the
* provided MutationConfig.
*/
export function mutationWithClientMutationId(
config: MutationConfig
): GraphQLFieldConfig<any, any>;
// node/node.js
type GraphQLNodeDefinitions = {
nodeInterface: GraphQLInterfaceType,
nodeField: GraphQLFieldConfig<any, any>
};
type typeResolverFn = ((any: any) => GraphQLObjectType) |
((any: any) => Promise<GraphQLObjectType>);
/**
* Given a function to map from an ID to an underlying object, and a function
* to map from an underlying object to the concrete GraphQLObjectType it
* corresponds to, constructs a `Node` interface that objects can implement,
* and a field config for a `node` root field.
*
* If the typeResolver is omitted, object resolution on the interface will be
* handled with the `isTypeOf` method on object types, as with any GraphQL
* interface without a provided `resolveType` method.
*/
export function nodeDefinitions<TContext>(
idFetcher: ((id: string, context: TContext, info: GraphQLResolveInfo) => any),
typeResolver?: GraphQLTypeResolver<any, TContext>
): GraphQLNodeDefinitions;
type ResolvedGlobalId = {
type: string,
id: string
};
/**
* Takes a type name and an ID specific to that type name, and returns a
* "global ID" that is unique among all types.
*/
export function toGlobalId(type: string, id: string): string;
/**
* Takes the "global ID" created by toGlobalID, and returns the type name and ID
* used to create it.
*/
export function fromGlobalId(globalId: string): ResolvedGlobalId;
/**
* Creates the configuration for an id field on a node, using `toGlobalId` to
* construct the ID from the provided typename. The type-specific ID is fetched
* by calling idFetcher on the object, or if not provided, by accessing the `id`
* property on the object.
*/
export function globalIdField(
typeName?: string,
idFetcher?: (object: any, context: any, info: GraphQLResolveInfo) => string
): GraphQLFieldConfig<any, any>;
// node/plural.js
type PluralIdentifyingRootFieldConfig = {
argName: string,
inputType: GraphQLInputType,
outputType: GraphQLOutputType,
resolveSingleInput: (input: any, context: any, info: GraphQLResolveInfo) => any,
description?: string,
};
export function pluralIdentifyingRootField(
config: PluralIdentifyingRootFieldConfig
): GraphQLFieldConfig<any, any>;
+21
View File
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"noImplicitAny": true,
"strictNullChecks": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [
"graphql"
],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"graphql-relay-tests.ts"
]
}
+1
View File
@@ -0,0 +1 @@
{ "extends": "../tslint.json" }