Skip to content

Commit 0e794a8

Browse files
committed
Convert plugins to V5
1 parent c053b55 commit 0e794a8

File tree

5 files changed

+188
-165
lines changed

5 files changed

+188
-165
lines changed

@app/server/src/plugins/Orders.ts

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,59 @@
11
import { makeAddPgTableOrderByPlugin, orderByAscDesc } from "graphile-utils";
2+
import type { SQL } from "pg-sql2";
3+
4+
/*
5+
// This is a rudimentary translation of the old V4 plugin using a subquery,
6+
// just to show changes can be minimal:
27
38
export default makeAddPgTableOrderByPlugin(
4-
"app_public",
5-
"organization_memberships",
6-
({ pgSql: sql }) => {
9+
{ schemaName: "app_public", tableName: "organization_memberships" },
10+
({ sql }) => {
711
const sqlIdentifier = sql.identifier(Symbol("member"));
8-
return orderByAscDesc(
9-
"MEMBER_NAME",
10-
// @ts-ignore
11-
({ queryBuilder }) => sql.fragment`(
12+
return orderByAscDesc("MEMBER_NAME", ($organizationMemberships) => ({
13+
fragment: sql.fragment`(
1214
select ${sqlIdentifier}.name
1315
from app_public.users as ${sqlIdentifier}
14-
where ${sqlIdentifier}.id = ${queryBuilder.getTableAlias()}.user_id
16+
where ${sqlIdentifier}.id = ${$organizationMemberships.alias}.user_id
1517
limit 1
16-
)`
18+
)`,
19+
codec: TYPES.text,
20+
}));
21+
}
22+
);
23+
24+
// But what follows is a more efficient implementation using a join:
25+
*/
26+
27+
export default makeAddPgTableOrderByPlugin(
28+
{ schemaName: "app_public", tableName: "organization_memberships" },
29+
(build) => {
30+
const {
31+
sql,
32+
input: { pgSources },
33+
} = build;
34+
const usersSource = pgSources.find(
35+
(s) =>
36+
!s.parameters &&
37+
s.extensions?.pg?.schemaName === "app_public" &&
38+
s.extensions.pg.name === "users"
1739
);
40+
if (!usersSource) {
41+
throw new Error(`Couldn't find the source for app_public.users`);
42+
}
43+
const sqlIdentifier = sql.identifier(Symbol("member"));
44+
return orderByAscDesc("MEMBER_NAME", ($organizationMemberships) => {
45+
$organizationMemberships.join({
46+
type: "inner",
47+
source: usersSource.source as SQL,
48+
alias: sqlIdentifier,
49+
conditions: [
50+
sql`${sqlIdentifier}.id = ${$organizationMemberships.alias}.user_id`,
51+
],
52+
});
53+
return {
54+
fragment: sql`${sqlIdentifier}.name`,
55+
codec: usersSource.codec.columns["name"].codec,
56+
};
57+
});
1858
}
1959
);

@app/server/src/plugins/PassportLoginPlugin.ts

Lines changed: 54 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { gql, makeExtendSchemaPlugin, Resolvers } from "graphile-utils";
1+
import { PgClassExpressionStep } from "@dataplan/pg";
2+
import { access, SafeError } from "grafast";
3+
import { gql, makeExtendSchemaPlugin,Plans,Resolvers } from "graphile-utils";
24

3-
import { OurGraphQLContext } from "../graphile.config";
5+
import type {} from "../middleware/installPostGraphile";
46
import { ERROR_MESSAGE_OVERRIDES } from "../utils/handleErrors";
57

68
const PassportLoginPlugin = makeExtendSchemaPlugin((build) => {
@@ -14,7 +16,7 @@ const PassportLoginPlugin = makeExtendSchemaPlugin((build) => {
1416
}
1517
1618
type RegisterPayload {
17-
user: User! @pgField
19+
user: User!
1820
}
1921
2022
input LoginInput {
@@ -23,7 +25,7 @@ const PassportLoginPlugin = makeExtendSchemaPlugin((build) => {
2325
}
2426
2527
type LoginPayload {
26-
user: User! @pgField
28+
user: User!
2729
}
2830
2931
type LogoutPayload {
@@ -85,12 +87,40 @@ const PassportLoginPlugin = makeExtendSchemaPlugin((build) => {
8587
resetPassword(input: ResetPasswordInput!): ResetPasswordPayload
8688
}
8789
`;
90+
const userSource = build.input.pgSources.find((s) => s.name === "users");
91+
const currentUserIdSource = build.input.pgSources.find(
92+
(s) => s.name === "current_user_id"
93+
);
94+
if (!userSource || !currentUserIdSource) {
95+
throw new Error(
96+
"Couldn't find either the 'users' or 'current_user_id' source"
97+
);
98+
}
99+
const plans: Plans = {
100+
RegisterPayload: {
101+
user($obj) {
102+
const $userId = access($obj, "userId");
103+
return userSource.get({ id: $userId });
104+
},
105+
},
106+
LoginPayload: {
107+
user() {
108+
const $userId = currentUserIdSource.execute() as PgClassExpressionStep<
109+
any,
110+
any,
111+
any,
112+
any,
113+
any
114+
>;
115+
return userSource.get({ id: $userId });
116+
},
117+
},
118+
};
88119
const resolvers: Resolvers = {
89120
Mutation: {
90-
async register(_mutation, args, context: OurGraphQLContext, resolveInfo) {
91-
const { selectGraphQLResultFromTable } = resolveInfo.graphile;
121+
async register(_mutation, args, context: Grafast.Context) {
92122
const { username, password, email, name, avatarUrl } = args.input;
93-
const { rootPgPool, login, pgClient } = context;
123+
const { rootPgPool, login, pgSettings } = context;
94124
try {
95125
// Create a user and create a session for it in the proccess
96126
const {
@@ -123,28 +153,15 @@ const PassportLoginPlugin = makeExtendSchemaPlugin((build) => {
123153
}
124154

125155
if (details.session_id) {
126-
// Store into transaction
127-
await pgClient.query(
128-
`select set_config('jwt.claims.session_id', $1, true)`,
129-
[details.session_id]
130-
);
156+
// Update pgSettings so future queries will use the new session
157+
pgSettings!["jwt.claims.session_id"] = details.session_id;
131158

132159
// Tell Passport.js we're logged in
133160
await login({ session_id: details.session_id });
134161
}
135162

136-
// Fetch the data that was requested from GraphQL, and return it
137-
const sql = build.pgSql;
138-
const [row] = await selectGraphQLResultFromTable(
139-
sql.fragment`app_public.users`,
140-
(tableAlias, sqlBuilder) => {
141-
sqlBuilder.where(
142-
sql.fragment`${tableAlias}.id = ${sql.value(details.user_id)}`
143-
);
144-
}
145-
);
146163
return {
147-
data: row,
164+
userId: details.user_id,
148165
};
149166
} catch (e: any) {
150167
const { code } = e;
@@ -155,6 +172,7 @@ const PassportLoginPlugin = makeExtendSchemaPlugin((build) => {
155172
...Object.keys(ERROR_MESSAGE_OVERRIDES),
156173
];
157174
if (safeErrorCodes.includes(code)) {
175+
// TODO: make SafeError
158176
throw e;
159177
} else {
160178
console.error(
@@ -167,10 +185,9 @@ const PassportLoginPlugin = makeExtendSchemaPlugin((build) => {
167185
}
168186
}
169187
},
170-
async login(_mutation, args, context: OurGraphQLContext, resolveInfo) {
171-
const { selectGraphQLResultFromTable } = resolveInfo.graphile;
188+
async login(_mutation, args, context: Grafast.Context) {
172189
const { username, password } = args.input;
173-
const { rootPgPool, login, pgClient } = context;
190+
const { rootPgPool, login, pgSettings } = context;
174191
try {
175192
// Call our login function to find out if the username/password combination exists
176193
const {
@@ -191,29 +208,15 @@ const PassportLoginPlugin = makeExtendSchemaPlugin((build) => {
191208
await login({ session_id: session.uuid });
192209
}
193210

194-
// Get session_id from PG
195-
await pgClient.query(
196-
`select set_config('jwt.claims.session_id', $1, true)`,
197-
[session.uuid]
198-
);
211+
// Update pgSettings so future queries will use the new session
212+
pgSettings!["jwt.claims.session_id"] = session.uuid;
199213

200-
// Fetch the data that was requested from GraphQL, and return it
201-
const sql = build.pgSql;
202-
const [row] = await selectGraphQLResultFromTable(
203-
sql.fragment`app_public.users`,
204-
(tableAlias, sqlBuilder) => {
205-
sqlBuilder.where(
206-
sql.fragment`${tableAlias}.id = app_public.current_user_id()`
207-
);
208-
}
209-
);
210-
return {
211-
data: row,
212-
};
214+
return {};
213215
} catch (e: any) {
214216
const code = e.extensions?.code ?? e.code;
215217
const safeErrorCodes = ["LOCKD", "CREDS"];
216218
if (safeErrorCodes.includes(code)) {
219+
// TODO: throw SafeError
217220
throw e;
218221
} else {
219222
console.error(e);
@@ -224,21 +227,18 @@ const PassportLoginPlugin = makeExtendSchemaPlugin((build) => {
224227
}
225228
},
226229

227-
async logout(_mutation, _args, context: OurGraphQLContext, _resolveInfo) {
228-
const { pgClient, logout } = context;
229-
await pgClient.query("select app_public.logout();");
230+
async logout(_mutation, _args, context: Grafast.Context) {
231+
const { pgSettings, withPgClient, logout } = context;
232+
await withPgClient(pgSettings, (pgClient) =>
233+
pgClient.query({ text: "select app_public.logout();" })
234+
);
230235
await logout();
231236
return {
232237
success: true,
233238
};
234239
},
235240

236-
async resetPassword(
237-
_mutation,
238-
args,
239-
context: OurGraphQLContext,
240-
_resolveInfo
241-
) {
241+
async resetPassword(_mutation, args, context: Grafast.Context) {
242242
const { rootPgPool } = context;
243243
const { userId, resetToken, newPassword, clientMutationId } =
244244
args.input;
@@ -264,6 +264,7 @@ const PassportLoginPlugin = makeExtendSchemaPlugin((build) => {
264264
};
265265
return {
266266
typeDefs,
267+
plans,
267268
resolvers,
268269
};
269270
});
Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
1-
import { Plugin } from "graphile-build";
1+
import type {} from "postgraphile";
22

3-
type PgConstraint = any;
4-
5-
const PrimaryKeyMutationsOnlyPlugin: Plugin = (builder) => {
6-
builder.hook(
7-
"build",
8-
(build) => {
9-
build.pgIntrospectionResultsByKind.constraint.forEach(
10-
(constraint: PgConstraint) => {
11-
if (!constraint.tags.omit && constraint.type !== "p") {
12-
constraint.tags.omit = ["update", "delete"];
3+
const PrimaryKeyMutationsOnlyPlugin: GraphileConfig.Plugin = {
4+
name: "PrimaryKeyMutationsOnlyPlugin",
5+
version: "0.0.0",
6+
gather: {
7+
hooks: {
8+
pgIntrospection_introspection(info, event) {
9+
const { introspection } = event;
10+
for (const pgConstraint of introspection.constraints) {
11+
if (pgConstraint.contype === "u") {
12+
const tags = pgConstraint.getTags();
13+
const newBehavior = ["-update", "-delete"];
14+
if (typeof tags.behavior === "string") {
15+
newBehavior.push(tags.behavior);
16+
} else if (Array.isArray(tags.behavior)) {
17+
newBehavior.push(...(tags.behavior as string[]));
18+
}
19+
tags.behavior = newBehavior;
1320
}
1421
}
15-
);
16-
return build;
22+
},
1723
},
18-
[],
19-
[],
20-
["PgIntrospection"]
21-
);
24+
},
2225
};
2326

2427
export default PrimaryKeyMutationsOnlyPlugin;
Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
import { Plugin } from "postgraphile";
2-
3-
const RemoveQueryQueryPlugin: Plugin = (builder) => {
4-
builder.hook("GraphQLObjectType:fields", (fields, build, context) => {
5-
if (context.scope.isRootQuery) {
6-
delete fields.query;
7-
}
8-
return fields;
9-
});
1+
const RemoveQueryQueryPlugin: GraphileConfig.Plugin = {
2+
name: "RemoveQueryQueryPlugin",
3+
version: "0.0.0",
4+
schema: {
5+
hooks: {
6+
GraphQLObjectType_fields(fields, _build, context) {
7+
if (context.scope.isRootQuery) {
8+
delete fields.query;
9+
}
10+
return fields;
11+
},
12+
},
13+
},
1014
};
1115

1216
export default RemoveQueryQueryPlugin;

0 commit comments

Comments
 (0)