Skip to content

Commit

Permalink
Add emulation of simpleSubscriptions: true to V4 preset (#1878)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjie authored Dec 4, 2023
2 parents bfedf68 + f5a03db commit 8c16aeb
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/red-swans-sing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"postgraphile": patch
---

Add emulation for `--simple-subscriptions` to V4 preset.
5 changes: 5 additions & 0 deletions .changeset/thirty-ants-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"graphile-build": patch
---

Export getNodeIdHandlerByTypeName to make writing plugins easier
10 changes: 6 additions & 4 deletions grafast/grafast/src/steps/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class NodeStep
private possibleTypes: {
[typeName: string]: NodeIdHandler;
},
$id: ExecutableStep<string>,
$id: ExecutableStep<string | null | undefined>,
) {
super();
const decodeNodeId = makeDecodeNodeId(Object.values(possibleTypes));
Expand Down Expand Up @@ -94,7 +94,7 @@ export function node(
possibleTypes: {
[typeName: string]: NodeIdHandler;
},
$id: ExecutableStep<string>,
$id: ExecutableStep<string | null | undefined>,
): NodeStep {
return new NodeStep(possibleTypes, $id);
}
Expand Down Expand Up @@ -126,7 +126,8 @@ export function specFromNodeId(
export function makeDecodeNodeId(handlers: NodeIdHandler[]) {
const codecs = [...new Set(handlers.map((h) => h.codec))];

function decodeNodeIdWithCodecs(raw: string) {
function decodeNodeIdWithCodecs(raw: string | null | undefined) {
if (raw == null) return null;
return codecs.reduce(
(memo, codec) => {
try {
Expand All @@ -142,5 +143,6 @@ export function makeDecodeNodeId(handlers: NodeIdHandler[]) {
);
}
decodeNodeIdWithCodecs.isSyncAndSafe = true; // Optimization
return ($id: ExecutableStep<string>) => lambda($id, decodeNodeIdWithCodecs);
return ($id: ExecutableStep<string | null | undefined>) =>
lambda($id, decodeNodeIdWithCodecs);
}
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ export const PgPolymorphismPlugin: GraphileConfig.Plugin = {
);
return object({
match: lambda($specifier, (specifier) => {
const value = specifier[handler.codec.name];
const value = specifier?.[handler.codec.name];
return value != null
? handler.match(value)
: false;
Expand Down Expand Up @@ -916,7 +916,7 @@ export const PgPolymorphismPlugin: GraphileConfig.Plugin = {
);
return object({
match: lambda($specifier, (specifier) => {
const value = specifier[handler.codec.name];
const value = specifier?.[handler.codec.name];
return value != null
? handler.match(value)
: false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ export const AddNodeInterfaceToSuitableTypesPlugin: GraphileConfig.Plugin = {
const {
getTypeByName,
inflection,

// To get this in your own plugin, use
// `const nodeIdHandlerByTypeName = build.getNodeIdHandlerByTypeName();` instead
[NODE_ID_HANDLER_BY_TYPE_NAME]: nodeIdHandlerByTypeName,

[NODE_ID_CODECS]: nodeIdCodecs,
graphql: { GraphQLNonNull, GraphQLID },
} = build;
Expand Down
8 changes: 7 additions & 1 deletion graphile-build/graphile-build/src/plugins/NodePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ declare global {
registerNodeIdHandler?(matcher: NodeIdHandler): void;
getNodeIdHandler?(typeName: string): NodeIdHandler | undefined;
getNodeTypeNames?(): string[];
getNodeIdHandlerByTypeName?(): Readonly<Record<string, NodeIdHandler>>;
}

interface ScopeObjectFieldsField {
Expand Down Expand Up @@ -77,8 +78,13 @@ export const NodePlugin: GraphileConfig.Plugin = {
return build.extend(
build,
{
/** @internal */
[NODE_ID_CODECS]: nodeIdCodecs,
/** @internal */
[NODE_ID_HANDLER_BY_TYPE_NAME]: nodeIdHandlerByTypeName,
getNodeIdHandlerByTypeName() {
return nodeIdHandlerByTypeName;
},
registerNodeIdCodec(codec) {
const codecName = codec.name;
if (nodeIdCodecs[codecName]) {
Expand Down Expand Up @@ -169,8 +175,8 @@ export const NodePlugin: GraphileConfig.Plugin = {
extend,
graphql: { GraphQLNonNull, GraphQLID },
inflection,
[NODE_ID_HANDLER_BY_TYPE_NAME]: nodeIdHandlerByTypeName,
} = build;
const nodeIdHandlerByTypeName = build.getNodeIdHandlerByTypeName!();
const nodeIdFieldName = build.inflection.nodeIdFieldName();
const nodeType = getTypeByName(inflection.builtin("Node")) as
| GraphQLObjectType
Expand Down
3 changes: 1 addition & 2 deletions graphile-build/graphile-utils/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"include": ["src"],
"references": [
{ "path": "../../utils/graphile-config" },
{ "path": "../../grafast/grafast" },
{ "path": "../../postgraphile/postgraphile/tsconfig.build.json" }
{ "path": "../../grafast/grafast" }
]
}
1 change: 1 addition & 0 deletions postgraphile/postgraphile/graphile.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ const preset: GraphileConfig.Preset = {
graphiql: true,
graphiqlRoute: "/",
ignoreRBAC: false,
simpleSubscriptions: true,
}),
// PgManyToManyPreset,
// PostGraphileConnectionFilterPreset,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { JSONParseStep } from "@dataplan/json";
import { jsonParse } from "@dataplan/json";
import { context, lambda, listen, node } from "grafast";
import { EXPORTABLE, gql, makeExtendSchemaPlugin } from "graphile-utils";

export const PgV4SimpleSubscriptionsPlugin = makeExtendSchemaPlugin((build) => {
const nodeIdHandlerByTypeName = build.getNodeIdHandlerByTypeName?.();
return {
typeDefs: [
gql`
extend type Subscription {
listen(topic: String!): ListenPayload
}
type ListenPayload {
event: String
}
`,
...// Only add the relatedNode if supported
(nodeIdHandlerByTypeName
? [
gql`
extend type ListenPayload {
relatedNode: Node
relatedNodeId: ID
}
`,
]
: []),
],
plans: {
Subscription: {
listen: {
subscribePlan: EXPORTABLE(
(context, jsonParse, lambda, listen) =>
function subscribePlan(_$root, { $topic }) {
const $pgSubscriber = context().get("pgSubscriber");
const $derivedTopic = lambda(
$topic,
(topic) => `postgraphile:${topic}`,
);
return listen($pgSubscriber, $derivedTopic, jsonParse);
},
[context, jsonParse, lambda, listen],
),
plan: EXPORTABLE(
() =>
function plan($event) {
return $event;
},
[],
),
},
},
ListenPayload: {
event($event) {
return $event.get("event");
},
...(nodeIdHandlerByTypeName
? {
relatedNodeId($event) {
return nodeIdFromEvent($event);
},
relatedNode($event) {
const $nodeId = nodeIdFromEvent($event);
return node(nodeIdHandlerByTypeName, $nodeId);
},
}
: null),
},
},
};
}, "PgV4SimpleSubscriptionsPlugin");

// WARNING: this function assumes that you're using the `base64JSON` NodeID codec, which was the case for PostGraphile V4. If you are not doing so, YMMV.
function nodeIdFromEvent($event: JSONParseStep<{ __node__?: any[] }>) {
const $nodeObj = $event.get("__node__");
const $nodeId = lambda($nodeObj, nodeObjToNodeId);
return $nodeId;
}

// base64JSON codec copy
function nodeObjToNodeId(obj: any[] | undefined): string | null {
if (!obj) return null;
return Buffer.from(JSON.stringify(obj), "utf8").toString("base64");
}
11 changes: 10 additions & 1 deletion postgraphile/postgraphile/src/presets/v4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ import type { IncomingMessage, ServerResponse } from "http";

import { PgV4BehaviorPlugin } from "../plugins/PgV4BehaviorPlugin.js";
import { PgV4InflectionPlugin } from "../plugins/PgV4InflectionPlugin.js";
import { PgV4SimpleSubscriptionsPlugin } from "../plugins/PgV4SimpleSubscriptionsPlugin.js";
import { PgV4SmartTagsPlugin } from "../plugins/PgV4SmartTagsPlugin.js";

export { PgV4BehaviorPlugin, PgV4InflectionPlugin, PgV4SmartTagsPlugin };
export {
PgV4BehaviorPlugin,
PgV4InflectionPlugin,
PgV4SimpleSubscriptionsPlugin,
PgV4SmartTagsPlugin,
};

type PromiseOrDirect<T> = T | Promise<T>;
type DirectOrCallback<Request, T> = T | ((req: Request) => PromiseOrDirect<T>);
Expand Down Expand Up @@ -87,6 +93,8 @@ export interface V4Options<
watchPg?: boolean;
/** @deprecated Use 'preset.grafast.context' callback instead */
defaultRole?: never;

simpleSubscriptions?: boolean;
}

function isNotNullish<T>(arg: T | undefined | null): arg is T {
Expand Down Expand Up @@ -200,6 +208,7 @@ export const makeV4Preset = (
PgV4InflectionPlugin,
PgV4SmartTagsPlugin,
PgV4BehaviorPlugin,
options.simpleSubscriptions ? PgV4SimpleSubscriptionsPlugin : null,
makeV4Plugin(options),
...(options.appendPlugins ? options.appendPlugins : []),
].filter(isNotNullish),
Expand Down
3 changes: 2 additions & 1 deletion postgraphile/postgraphile/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
{ "path": "../../utils/graphile-export" },
{ "path": "../../grafast/ruru/tsconfig.build.json" },
{ "path": "../../grafast/grafserv-persisted" },
{ "path": "../../utils/graphile-config" }
{ "path": "../../utils/graphile-config" },
{ "path": "../../graphile-build/graphile-utils" }
]
}

0 comments on commit 8c16aeb

Please sign in to comment.