Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto smart comments and multi response #1

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,19 @@
},
"homepage": "https://github.com/tjmoses/postgraphile-plugin-many-create-update-delete#readme",
"dependencies": {
"postgraphile": "^4.7.0",
"graphile-utils": "^4.5.6"
"graphile-utils": "^4.5.6",
"lodash": "^4.17.20",
"postgraphile": "^4.7.0"
},
"devDependencies": {
"@types/lodash": "^4.14.168",
"@types/nanographql": "^2.0.1",
"@types/pg": "^7.14.4",
"@typescript-eslint/parser": "^3.6.0",
"graphile-build": "^4.7.0",
"prettier-standard": "^16.4.1",
"rimraf": "^3.0.0",
"typescript": "^3.9.6",
"prettier-standard": "^16.4.1"
"typescript": "^3.9.6"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
Expand Down
122 changes: 62 additions & 60 deletions src/PostGraphileManyCreatePlugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as T from './pluginTypes';
import debugFactory from 'debug';
const debug = debugFactory('graphile-build-pg');
import * as T from "./pluginTypes";
import debugFactory from "debug";
const debug = debugFactory("graphile-build-pg");

const PostGraphileManyCreatePlugin: T.Plugin = (
builder: T.SchemaBuilder,
Expand All @@ -13,17 +13,17 @@ const PostGraphileManyCreatePlugin: T.Plugin = (
*/
builder.hook(
// @ts-ignore
'GraphQLObjectType:fields',
"GraphQLObjectType:fields",
GQLObjectFieldsHookHandlerFcn,
['PgMutationManyCreate'], // Hook provides
["PgMutationManyCreate"], // Hook provides
[], // Hook before
['PgMutationCreate'] // Hook after
["PgMutationCreate"] // Hook after
);

/**
* Handles adding the new "many create" root level fields
*/
function GQLObjectFieldsHookHandlerFcn (
function GQLObjectFieldsHookHandlerFcn(
fields: any,
build: T.Build,
context: T.Context
Expand All @@ -42,7 +42,7 @@ const PostGraphileManyCreatePlugin: T.Plugin = (
GraphQLInputObjectType,
GraphQLNonNull,
GraphQLString,
GraphQLList
GraphQLList,
},
pgColumnFilter,
inflection,
Expand All @@ -51,12 +51,12 @@ const PostGraphileManyCreatePlugin: T.Plugin = (
pgViaTemporaryTable: viaTemporaryTable,
describePgEntity,
sqlCommentByAddingTags,
pgField
pgField,
} = build;

const {
scope: { isRootMutation },
fieldWithHooks
fieldWithHooks,
} = context;

if (!isRootMutation) return fields;
Expand All @@ -68,12 +68,12 @@ const PostGraphileManyCreatePlugin: T.Plugin = (
handleAdditionsFromTableInfo(pgIntrospectionResultsByKind.class[i]);
}

function handleAdditionsFromTableInfo (table: T.PgClass) {
function handleAdditionsFromTableInfo(table: T.PgClass) {
if (
!table.namespace ||
!table.isSelectable ||
!table.isInsertable ||
omit(table, 'create') ||
omit(table, "create") ||
!table.tags.mncud
)
return;
Expand All @@ -98,37 +98,38 @@ const PostGraphileManyCreatePlugin: T.Plugin = (
debug(
`There was no input type for table '${table.namespace.name}.${table.name}', so we're going to omit it from the create mutation.`
);
return;
}
const tableTypeName = inflection.tableType(table);

// Setup args for the input type
const newInputHookType = GraphQLInputObjectType;
const newInputHookSpec = {
name: `mn${inflection.createInputType(table)}`,
name: inflection.camelCase(inflection.createInputType(table)),
description: `All input for the create mn\`${tableTypeName}\` mutation.`,
fields: () => ({
clientMutationId: {
description:
'An arbitrary string value with no semantic meaning. Will be included in the payload verbatim. May be used to track mutations by the client.',
type: GraphQLString
"An arbitrary string value with no semantic meaning. Will be included in the payload verbatim. May be used to track mutations by the client.",
type: GraphQLString,
},
[`mn${tableTypeName}`]: {
[inflection.pluralize(inflection.camelCase(tableTypeName))]: {
description: `The one or many \`${tableTypeName}\` to be created by this mutation.`,
type: new GraphQLList(new GraphQLNonNull(TableInput))
}
})
type: new GraphQLList(new GraphQLNonNull(TableInput)),
},
}),
};
const newInputHookScope = {
__origin: `Adding many table create input type for ${describePgEntity(
table
)}.
You can rename the table's GraphQL type via a 'Smart Comment':
\n\n ${sqlCommentByAddingTags(table, {
name: 'newNameHere'
name: "newNameHere",
})}`,
isPgCreateInputType: true,
pgInflection: table,
pgIntrospection: table
pgIntrospection: table,
};
const InputType = newWithHooks(
newInputHookType,
Expand All @@ -139,88 +140,89 @@ const PostGraphileManyCreatePlugin: T.Plugin = (
// Setup args for payload type
const newPayloadHookType = GraphQLObjectType;
const newPayloadHookSpec = {
name: `mn${inflection.createPayloadType(table)}`,
name: inflection.pluralize(inflection.createPayloadType(table)),
description: `The output of our many create \`${tableTypeName}\` mutation.`,
fields: ({ fieldWithHooks }) => {
const tableName = inflection.tableFieldName(table);
return {
clientMutationId: {
description:
'The exact same `clientMutationId` that was provided in the mutation input, unchanged and unused. May be used by a client to track mutations.',
type: GraphQLString
"The exact same `clientMutationId` that was provided in the mutation input, unchanged and unused. May be used by a client to track mutations.",
type: GraphQLString,
},
[tableName]: pgField(
build,
fieldWithHooks,
tableName,
{
description: `The \`${tableTypeName}\` that was created by this mutation.`,
type: tableType
type: new GraphQLList(new GraphQLNonNull(tableType)),
},
{
isPgCreatePayloadResultField: true,
pgFieldIntrospection: table
pgFieldIntrospection: table,
}
)
),
};
}
},
};
const newPayloadHookScope = {
__origin: `Adding many table many create payload type for ${describePgEntity(
table
)}.
You can rename the table's GraphQL type via a 'Smart Comment':
\n\n ${sqlCommentByAddingTags(table, {
name: 'newNameHere'
name: "newNameHere",
})}\n\nor disable the built-in create mutation via:\n\n
${sqlCommentByAddingTags(table, { omit: 'create' })}`,
${sqlCommentByAddingTags(table, { omit: "create" })}`,
isMutationPayload: true,
isPgCreatePayloadType: true,
pgIntrospection: table
pgIntrospection: table,
};
const PayloadType = newWithHooks(
newPayloadHookType,
newPayloadHookSpec,
newPayloadHookScope
);

const fieldName = `mn${inflection.upperCamelCase(
inflection.createField(table)
)}`;
const fieldName = inflection.pluralize(
inflection.camelCase(inflection.createField(table))
);

function newFieldWithHooks (): T.FieldWithHooksFunction {
function newFieldWithHooks(): T.FieldWithHooksFunction {
return fieldWithHooks(
fieldName,
context => {
(context) => {
context.table = table;
context.relevantAttributes = table.attributes.filter(
attr =>
pgColumnFilter(attr, build, context) && !omit(attr, 'create')
(attr) =>
pgColumnFilter(attr, build, context) && !omit(attr, "create")
);
return {
description: `Creates one or many \`${tableTypeName}\`.`,
type: PayloadType,
args: {
input: {
type: new GraphQLNonNull(InputType)
}
type: new GraphQLNonNull(InputType),
},
},
resolve: resolver.bind(context)
resolve: resolver.bind(context),
};
},
{
pgFieldIntrospection: table,
isPgCreateMutationField: true
isPgCreateMutationField: true,
isMultipleMutation: true,
}
);
}

async function resolver (_data, args, resolveContext, resolveInfo) {
async function resolver(_data, args, resolveContext, resolveInfo) {
const { input } = args;
const {
table,
getDataFromParsedResolveInfoFragment,
relevantAttributes
relevantAttributes,
}: {
table: T.PgClass;
getDataFromParsedResolveInfoFragment: any;
Expand Down Expand Up @@ -251,7 +253,9 @@ const PostGraphileManyCreatePlugin: T.Plugin = (
const sqlColumns: T.SQL[] = [];
const inputData: Object[] =
input[
`mn${inflection.upperCamelCase(inflection.tableFieldName(table))}`
inflection.pluralize(
inflection.camelCase(inflection.tableFieldName(table))
)
];
if (!inputData || inputData.length === 0) return null;
const sqlValues: T.SQL[][] = Array(inputData.length).fill([]);
Expand All @@ -268,10 +272,10 @@ const PostGraphileManyCreatePlugin: T.Plugin = (
if (Object.prototype.hasOwnProperty.call(dataObj, fieldName)) {
sqlValues[i] = [
...sqlValues[i],
gql2pg(dataValue, attr.type, attr.typeModifier)
gql2pg(dataValue, attr.type, attr.typeModifier),
];
} else {
sqlValues[i] = [...sqlValues[i], sql.raw('default')];
sqlValues[i] = [...sqlValues[i], sql.raw("default")];
}
});
});
Expand All @@ -280,48 +284,46 @@ const PostGraphileManyCreatePlugin: T.Plugin = (
INSERT INTO ${sql.identifier(table.namespace.name, table.name)}
${
sqlColumns.length
? sql.fragment`(${sql.join(sqlColumns, ', ')})
? sql.fragment`(${sql.join(sqlColumns, ", ")})
VALUES (${sql.join(
sqlValues.map(
dataGroup => sql.fragment`${sql.join(dataGroup, ', ')}`
(dataGroup) => sql.fragment`${sql.join(dataGroup, ", ")}`
),
'),('
"),("
)})`
: sql.fragment`default values`
} returning *`;
let row;
let rows;
try {
await pgClient.query('SAVEPOINT graphql_mutation');
const rows = await viaTemporaryTable(
await pgClient.query("SAVEPOINT graphql_mutation");
rows = await viaTemporaryTable(
pgClient,
sql.identifier(table.namespace.name, table.name),
mutationQuery,
insertedRowAlias,
query
);

row = rows[0];
await pgClient.query('RELEASE SAVEPOINT graphql_mutation');
await pgClient.query("RELEASE SAVEPOINT graphql_mutation");
} catch (e) {
await pgClient.query('ROLLBACK TO SAVEPOINT graphql_mutation');
await pgClient.query("ROLLBACK TO SAVEPOINT graphql_mutation");
throw e;
}

return {
clientMutationId: input.clientMutationId,
data: row
data: rows,
};
}

newFields = extend(
newFields,
{
[fieldName]: newFieldWithHooks
[fieldName]: newFieldWithHooks,
},
`Adding create mutation for ${describePgEntity(table)}. You can omit
this default mutation with a 'Smart Comment':\n\n
${sqlCommentByAddingTags(table, {
omit: 'create'
omit: "create",
})}`
);
}
Expand Down
Loading