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

Address a number of TODO items #406

Merged
merged 32 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fbebc29
Write up some docs for graphile-export
benjie Jul 8, 2023
1e2ea95
Fix deficiency in constraint finding for polymorphism
benjie Jul 10, 2023
e020672
Address some TODOs
benjie Jul 10, 2023
bd3e330
Add the Relay preset to the postgraphile config
benjie Jul 10, 2023
3b4821d
Outdated TODO?
benjie Jul 10, 2023
2bc6170
Determine whether or not the record is added to the mutation payload …
benjie Jul 10, 2023
54c5586
Change the behavior scopes used on mutation payloads
benjie Jul 10, 2023
3b1ee78
Ensure the Relay plugin runs after the V4 compatibility plugin
benjie Jul 10, 2023
0b98159
Add fieldBehaviorScope to deleted node ID field
benjie Jul 10, 2023
4fdbc76
Enable the deleted nodeId by default
benjie Jul 10, 2023
2f50825
Use gatherConfig TS CIF to improve types
benjie Jul 10, 2023
f8f40f7
Action a couple TODOs
benjie Jul 10, 2023
a5943ee
Demote
benjie Jul 10, 2023
160b503
Add Base64EncodedBinary/bytea support
benjie Jul 10, 2023
93a1e98
Base64 support via native hex encoding, plus tests
benjie Jul 10, 2023
01eb606
Lint
benjie Jul 10, 2023
ecd7598
docs(changeset): More docs for graphile-export
benjie Jul 10, 2023
a298fc8
docs(changeset): Fix constraint finding for polymorphism (was theoret…
benjie Jul 10, 2023
bfe5997
docs(changeset): Ability to control via behaviors whether the record …
benjie Jul 10, 2023
9f5a784
docs(changeset): Introduce TypeScript CIF gatherConfig() to help typi…
benjie Jul 10, 2023
51414d3
docs(changeset): Add support for `bytea` datatype using new `Base64En…
benjie Jul 10, 2023
35e6ef0
Update a few more TODOs
benjie Jul 10, 2023
85dfd38
Remove out of date TODO
benjie Jul 11, 2023
9a9ff57
Remove filterBy/orderBy from bytea columns
benjie Jul 11, 2023
2920c71
bytea is binary
benjie Jul 11, 2023
f952afd
Scope was on wrong side for orderBy
benjie Jul 11, 2023
c5eceba
docs(changeset): Incorrect ordering of behaviors `orderBy:array`/`ord…
benjie Jul 11, 2023
2f395e8
Don't orderBy or filter binary columns
benjie Jul 11, 2023
a29fe8b
More consistent approach
benjie Jul 11, 2023
3d1019b
orderBy checks each of the attribute types
benjie Jul 11, 2023
605658f
Implement per-type filterBy behaviors
benjie Jul 11, 2023
903c04b
docs(changeset): Add `filterBy` and `orderBy` behaviors for each code…
benjie Jul 11, 2023
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
7 changes: 7 additions & 0 deletions .changeset/calm-zebras-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"graphile-build-pg": patch
"postgraphile": patch
---

Ability to control via behaviors whether the record type is selectable on CRUD
mutation payloads.
7 changes: 7 additions & 0 deletions .changeset/dry-moons-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"graphile-build-pg": patch
"postgraphile": patch
---

Add `filterBy` and `orderBy` behaviors for each codec type (array, range,
composite, binary, scalar) to allow simpler global customization.
7 changes: 7 additions & 0 deletions .changeset/fluffy-taxis-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"graphile-build-pg": patch
"postgraphile": patch
---

Incorrect ordering of behaviors `orderBy:array`/`orderBy:range` fixed ->
`array:attribute:orderBy`/`range:attribute:orderBy`.
9 changes: 9 additions & 0 deletions .changeset/itchy-weeks-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"graphile-build-pg": patch
"graphile-build": patch
"graphile-utils": patch
"postgraphile": patch
---

Introduce TypeScript CIF gatherConfig() to help typing the `gather` phase for
plugins.
6 changes: 6 additions & 0 deletions .changeset/serious-weeks-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"eslint-plugin-graphile-export": patch
"graphile-export": patch
---

More docs for graphile-export
8 changes: 8 additions & 0 deletions .changeset/shaggy-crabs-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"graphile-build-pg": patch
"postgraphile": patch
"@dataplan/pg": patch
---

Add support for `bytea` datatype using new `Base64EncodedBinary` scalar in
GraphQL.
6 changes: 6 additions & 0 deletions .changeset/shaggy-frogs-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"graphile-build-pg": patch
---

Fix constraint finding for polymorphism (was theoretically possible to find the
wrong constraint).
31 changes: 27 additions & 4 deletions grafast/dataplan-pg/src/codecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ function t<TFromJavaScript = any, TFromPostgres = string>(): <
undefined
> {
return (oid, type, options = {}) => {
const { castFromPg, listCastFromPg, fromPg, toPg } = options;
const { castFromPg, listCastFromPg, fromPg, toPg, isBinary } = options;
return {
name: type,
sqlType: sql.identifier(...type.split(".")),
Expand All @@ -178,6 +178,7 @@ function t<TFromJavaScript = any, TFromPostgres = string>(): <
castFromPg,
listCastFromPg,
executor: null,
isBinary,
};
};
}
Expand Down Expand Up @@ -854,6 +855,7 @@ type Cast<TFromJavaScript = any, TFromPostgres = string> = {
listCastFromPg?(frag: SQL): SQL;
toPg?: PgEncode<TFromJavaScript>;
fromPg?: PgDecode<TFromJavaScript, TFromPostgres>;
isBinary?: boolean;
};

/**
Expand Down Expand Up @@ -1020,6 +1022,28 @@ export const TYPES = {
fromPg: parseHstore,
toPg: stringifyHstore,
}),
bytea: t<Buffer>()("17", "bytea", {
fromPg(str) {
// The bytea type supports two formats for input and output: “hex”
// format and PostgreSQL's historical “escape” format. Both of these
// are always accepted on input. The output format depends on the
// configuration parameter bytea_output; the default is hex.
// -- https://www.postgresql.org/docs/current/datatype-binary.html
if (str.startsWith("\\x")) {
// Hex format
return Buffer.from(str.substring(2), "hex");
} else {
// ENHANCE: consider supporting this
throw new Error(
`PostgreSQL bytea escape format is currently unsupported, please use \`bytea_output = 'hex'\` in your PostgreSQL configuration.`,
);
}
},
toPg(data: Buffer) {
return `\\x${data.toString("hex")}`;
},
isBinary: true,
}),
} as const;
exportAs("@dataplan/pg", TYPES, "TYPES");
for (const [name, codec] of Object.entries(TYPES)) {
Expand All @@ -1037,9 +1061,8 @@ export function getCodecByPgCatalogTypeName(pgCatalogTypeName: string) {
case "bool":
return TYPES.boolean;

// TODO!
//case "bytea":
// return TYPES.bytea; // oid: 17
case "bytea":
return TYPES.bytea; // oid: 17

case "char":
return TYPES.char;
Expand Down
5 changes: 5 additions & 0 deletions grafast/dataplan-pg/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ export interface PgCodec<
*/
isAnonymous?: boolean;

/**
* True if this type is a binary type (e.g. bytea)
*/
isBinary?: boolean;

/**
* If this is a composite type, the attributes it supports.
*/
Expand Down
16 changes: 8 additions & 8 deletions graphile-build/graphile-build-pg/src/plugins/PgCodecsPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
recordCodec,
TYPES,
} from "@dataplan/pg";
import { EXPORTABLE } from "graphile-build";
import { EXPORTABLE, gatherConfig } from "graphile-build";
import type { PgAttribute, PgClass, PgType } from "pg-introspection";
import sql from "pg-sql2";

Expand Down Expand Up @@ -274,8 +274,7 @@ export const PgCodecsPlugin: GraphileConfig.Plugin = {
},
},

// TODO: refactor TypeScript so this isn't necessary; maybe via `makePluginGatherConfig`?
gather: <GraphileConfig.PluginGatherConfig<"pgCodecs", State>>{
gather: gatherConfig({
namespace: "pgCodecs",
initialState: (): State => ({
codecByTypeIdByDatabaseName: new Map(),
Expand All @@ -289,7 +288,7 @@ export const PgCodecsPlugin: GraphileConfig.Plugin = {
info.state.codecByClassIdByDatabaseName.set(serviceName, map);
}
if (map.has(classId)) {
return map.get(classId);
return map.get(classId)!;
}

const promise = (async () => {
Expand Down Expand Up @@ -355,7 +354,7 @@ export const PgCodecsPlugin: GraphileConfig.Plugin = {
(attributeAttribute.attidentity != null &&
attributeAttribute.attidentity !== "") ||
attributeAttribute.getType()?.typdefault != null,
// TODO: identicalVia,
// PERF: identicalVia,
extensions: {
tags,
},
Expand Down Expand Up @@ -467,7 +466,7 @@ export const PgCodecsPlugin: GraphileConfig.Plugin = {
info.state.codecByTypeIdByDatabaseName.set(serviceName, map);
}
if (map.has(typeId)) {
return map.get(typeId);
return map.get(typeId)!;
}

const promise = (async (): Promise<PgCodec | null> => {
Expand Down Expand Up @@ -871,7 +870,7 @@ export const PgCodecsPlugin: GraphileConfig.Plugin = {
}
},
},
},
}),

schema: {
hooks: {
Expand Down Expand Up @@ -1080,6 +1079,7 @@ export const PgCodecsPlugin: GraphileConfig.Plugin = {
pgUseCustomNetworkScalars !== false
? inflection.builtin("MacAddress8")
: "String",
bytea: inflection.builtin("Base64EncodedBinary"),
};
for (const rawKey in typeNameByTYPESKey) {
const key = rawKey as keyof typeof typeNameByTYPESKey;
Expand Down Expand Up @@ -1464,7 +1464,7 @@ export const PgCodecsPlugin: GraphileConfig.Plugin = {
}
} else {
// We have no idea what this is or how to handle it.
// TODO: add some default handling, like "behavesLike = TYPES.string"?
// TODO: add some default handling, like "behavesLike = TYPES.text"?
console.warn(
`PgCodec '${codec.name}' not understood, please set 'domainOfCodec' to indicate the underlying behaviour the type should have when exposed to GraphQL`,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import "./PgTablesPlugin.js";
import "graphile-config";

import type {
PgCodec,
PgCodecWithAttributes,
PgConditionStep,
PgSelectParsedCursorStep,
Expand Down Expand Up @@ -44,7 +45,7 @@ export const PgConditionArgumentPlugin: GraphileConfig.Plugin = {
schema: {
entityBehavior: {
pgCodec: "select filter",
pgCodecAttribute: "filterBy",
pgCodecAttribute: "filterBy -binary:attribute:filterBy",
pgResource: {
provides: ["default"],
before: ["inferred", "override"],
Expand Down Expand Up @@ -119,16 +120,36 @@ export const PgConditionArgumentPlugin: GraphileConfig.Plugin = {
// PgNodeIdAttributesPlugin for similar approach for NodeIDs)
return Object.entries(attributes).reduce(
(memo, [attributeName, attribute]) => {
if (
!build.behavior.pgCodecAttributeMatches(
[codec, attributeName],
"attribute:filterBy",
)
) {
return memo;
const behaviors: string[] = [];
function walk(codec: PgCodec) {
if (codec.arrayOfCodec) {
behaviors.push("array:attribute:filterBy");
walk(codec.arrayOfCodec);
} else if (codec.rangeOfCodec) {
behaviors.push("range:attribute:filterBy");
walk(codec.rangeOfCodec);
} else if (codec.domainOfCodec) {
// No need to add a behavior for domain
walk(codec.domainOfCodec);
} else if (codec.attributes) {
behaviors.push("composite:attribute:filterBy");
} else if (codec.isBinary) {
behaviors.push("binary:attribute:filterBy");
} else {
behaviors.push("scalar:attribute:filterBy");
}
}
walk(attribute.codec);
for (const behavior of behaviors) {
if (
!build.behavior.pgCodecAttributeMatches(
[codec, attributeName],
behavior,
)
) {
return memo;
}
}

// TODO: add `attribute:filterBy:array`/`:range` ?

const fieldName = inflection.attribute({
attributeName,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { PgCodecExtensions, PgEnumCodec, PgEnumValue } from "@dataplan/pg";
import { enumCodec } from "@dataplan/pg";
import { gatherConfig } from "graphile-build";
import type {
Introspection,
PgAttribute,
Expand Down Expand Up @@ -64,7 +65,7 @@ interface State {
codecByPgConstraint: Map<PgConstraint, PgEnumCodec>;
codecByPgAttribute: Map<PgAttribute, PgEnumCodec>;
}
interface Cache {}
const EMPTY_OBJECT = Object.freeze({});

// TODO: Assert the attributes are text
/*
Expand Down Expand Up @@ -116,8 +117,11 @@ export const PgEnumTablesPlugin: GraphileConfig.Plugin = {
},
},

gather: <GraphileConfig.PluginGatherConfig<"pgEnumTables", State, Cache>>{
gather: gatherConfig({
namespace: "pgEnumTables",
initialCache() {
return EMPTY_OBJECT;
},
initialState: (): State => ({
codecByPgConstraint: new Map(),
codecByPgAttribute: new Map(),
Expand Down Expand Up @@ -337,7 +341,7 @@ Original error: ${e.message}
}
},
},
},
}),
};

function isEnumConstraint(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { PromiseOrDirect } from "grafast";
import type { GatherPluginContext } from "graphile-build";
import { gatherConfig } from "graphile-build";
import type {
Introspection,
PgAttribute,
Expand Down Expand Up @@ -35,7 +36,8 @@ declare global {
interface State {
fakeId: number;
}
interface Cache {}
const EMPTY_OBJECT = Object.freeze({});
type Cache = typeof EMPTY_OBJECT;

export const PgFakeConstraintsPlugin: GraphileConfig.Plugin = {
name: "PgFakeConstraintsPlugin",
Expand All @@ -44,10 +46,13 @@ export const PgFakeConstraintsPlugin: GraphileConfig.Plugin = {
version: version,
after: ["smart-tags"],

gather: {
gather: gatherConfig({
namespace: "pgFakeConstraints",
helpers: {},
initialState: () => ({
initialCache(): Cache {
return EMPTY_OBJECT;
},
initialState: (): State => ({
fakeId: 0,
}),
hooks: {
Expand Down Expand Up @@ -101,7 +106,7 @@ export const PgFakeConstraintsPlugin: GraphileConfig.Plugin = {
}
},
},
} as GraphileConfig.PluginGatherConfig<"pgFakeConstraints", State, Cache>,
}),
};

function parseConstraintSpec(rawSpec: string) {
Expand Down
Loading