packages/db/src/meta/process/resources.ts
import { logger } from "@truffle/db/logger";
const debug = logger("db:process");
import gql from "graphql-tag";
import * as graphql from "graphql";
import { pascalCase } from "change-case";
import { singular } from "pluralize";
import type {
CollectionName,
Collections,
Input,
Resource
} from "@truffle/db/meta/collections";
import { IdObject, toIdObject } from "@truffle/db/meta/id";
import type { Definitions, Process } from "./types";
export interface ResourceProcessorsOptions<C extends Collections> {
definitions: Definitions<C>;
}
export interface ResourceProcessors<C extends Collections> {
load: <N extends CollectionName<C>>(
collectionName: N,
inputs: (Input<C, N> | undefined)[]
) => Process<C, (IdObject<C, N> | undefined)[]>;
get: <N extends CollectionName<C>>(
collectionName: N,
id: string,
document: graphql.DocumentNode
) => Process<C, Resource<C, N> | undefined>;
find: <N extends CollectionName<C>>(
collectionName: N,
ids: (string | undefined)[],
document: graphql.DocumentNode
) => Process<C, (Resource<C, N> | undefined)[]>;
all: <N extends CollectionName<C>>(
collectionName: N,
document: graphql.DocumentNode
) => Process<C, Resource<C, N>[]>;
}
export const resourceProcessorsForDefinitions = <C extends Collections>(
definitions: Definitions<C>
): ResourceProcessors<C> => {
const names = <N extends CollectionName<C>>(collectionName: N) => {
const { mutable } = definitions[collectionName];
const resources = collectionName;
const Resources = pascalCase(resources);
const resource = singular(resources);
const Resource = pascalCase(resource);
const Mutate = mutable ? "Assign" : "Add";
const resourcesMutate = `${resources}${Mutate}`;
const ResourcesMutate = pascalCase(resourcesMutate);
return {
resource,
resources,
Resource,
Resources,
resourcesMutate,
ResourcesMutate
};
};
return {
*load<N extends CollectionName<C>>(
collectionName: N,
inputs: Input<C, N>[]
): Process<C, IdObject<C, N>[]> {
const { Resource, Resources, resources, resourcesMutate } = names(
collectionName
);
const response = yield {
type: "graphql",
request: gql`
mutation Load${Resources}(
$inputs: [${Resource}Input]!
) {
${resourcesMutate}(input: { ${resources}: $inputs }) {
${resources} {
id
}
}
}
`,
variables: { inputs }
};
debug("response %o", response);
return response.data[resourcesMutate][resources].map(toIdObject);
},
*get<N extends CollectionName<C>>(
collectionName: N,
id: string,
document: graphql.DocumentNode
): Process<C, Resource<C, N>> {
debug("get");
const { Resource, resource } = names(collectionName);
const fragments = document.definitions
.filter(({ kind }) => kind === "FragmentDefinition")
.filter(
({ typeCondition }: graphql.FragmentDefinitionNode) =>
typeCondition.name.value === Resource
)
.map(
({ name: { value } }: graphql.FragmentDefinitionNode) => `...${value}`
)
.join("\n");
const response = yield {
type: "graphql",
request: gql`
${document}
query Get${Resource}($id: ID!) {
${resource}(id: $id) {
${fragments}
}
}
`,
variables: {
id
}
};
debug("response %o", response);
return response.data[resource];
},
*find<N extends CollectionName<C>>(
collectionName: N,
ids: string[],
document: graphql.DocumentNode
): Process<C, Resource<C, N>[]> {
debug("find collectionName %o, ids: %o", collectionName, ids);
const { Resource, Resources, resources } = names(collectionName);
const fragments = document.definitions
.filter(({ kind }) => kind === "FragmentDefinition")
.filter(
({ typeCondition }: graphql.FragmentDefinitionNode) =>
typeCondition.name.value === Resource
)
.map(
({ name: { value } }: graphql.FragmentDefinitionNode) => `...${value}`
)
.join("\n");
const request = gql`
${document}
query Find${Resources}($ids: [ID!]!) {
${resources}(filter: { ids: $ids }) {
${fragments}
}
}
`;
debug("request: %s", graphql.print(request));
const response = yield {
type: "graphql",
request,
variables: {
ids
}
};
debug("response %o", response);
return response.data[resources];
},
*all<N extends CollectionName<C>>(
collectionName: N,
document: graphql.DocumentNode
): Process<C, Resource<C, N>[]> {
const { Resource, Resources, resources } = names(collectionName);
const fragments = document.definitions
.filter(({ kind }) => kind === "FragmentDefinition")
.filter(
({ typeCondition }: graphql.FragmentDefinitionNode) =>
typeCondition.name.value === Resource
)
.map(
({ name: { value } }: graphql.FragmentDefinitionNode) => `...${value}`
)
.join("\n");
const response = yield {
type: "graphql",
request: gql`
${document}
query All${Resources} {
${resources} {
${fragments}
}
}
`,
variables: {}
};
return response.data[resources];
}
};
};