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

Fix logic around @omit emulation and smart-tag inheritance #2187

Merged
merged 10 commits into from
Sep 27, 2024
12 changes: 12 additions & 0 deletions .changeset/chilly-cups-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"graphile-build-pg": patch
"postgraphile": patch
---

Fix behavior inheritance especially around functions incorrectly inheriting from
their underlying codecs, bugs in unlogged/temp table behavior, and incorrect
skipping of generating table types. You may find after this change you have
fields appearing in your schema that were not present before, typically these
will represent database functions where you `@omit`'d the underlying table -
omitting the table should not prevent a function from accessing it. Further, fix
behavior of `@omit read` emulation to add `-connection -list -array -single`.
5 changes: 5 additions & 0 deletions .changeset/eleven-dolphins-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"graphile": patch
---

Fixes bug in 'graphile behavior debug pgResourceUnique' command.
20 changes: 18 additions & 2 deletions graphile-build/graphile-build-pg/src/plugins/PgBasicsPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ declare global {
orderBy: true;
"resource:connection": true;
"resource:list": true;
"resource:array": true;
"resource:single": true;
}
type HasGraphQLTypeForPgCodec = (
codec: PgCodec<any, any, any, any, any, any, any>,
Expand Down Expand Up @@ -237,6 +239,15 @@ export const PgBasicsPlugin: GraphileConfig.Plugin = {
description: "should we use a list field for this?",
entities: ["pgCodec", "pgResource", "pgResourceUnique"],
},
"resource:array": {
description:
"should we use a list field for this non-connection-capable thing?",
entities: ["pgCodec", "pgResource", "pgResourceUnique"],
},
"resource:single": {
description: "can we get one of this thing?",
entities: ["pgCodec", "pgResource", "pgResourceUnique"],
},
},
},
entityBehavior: {
Expand All @@ -255,15 +266,20 @@ export const PgBasicsPlugin: GraphileConfig.Plugin = {
const attribute = codec.attributes[attributeName];
return [
behavior,
getBehavior([codec.extensions, attribute.extensions]),
getBehavior([attribute.codec.extensions, attribute.extensions]),
];
},
},
pgResource: {
override(behavior, resource) {
return [
behavior,
getBehavior([resource.codec.extensions, resource.extensions]),
getBehavior(
resource.parameters
? // Functions should not inherit from their codec
[resource.extensions]
: [resource.codec.extensions, resource.extensions],
),
];
},
},
Expand Down
92 changes: 52 additions & 40 deletions graphile-build/graphile-build-pg/src/plugins/PgTablesPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type {
PgCodec,
PgCodecAttribute,
PgResource,
PgResourceExtensions,
PgResourceOptions,
PgResourceUnique,
} from "@dataplan/pg";
Expand Down Expand Up @@ -468,7 +467,7 @@ export const PgTablesPlugin: GraphileConfig.Plugin = {
const isVirtual = !["r", "v", "m", "f", "p"].includes(
pgClass.relkind,
);
const extensions: PgResourceExtensions = {
const extensions: DataplanPg.PgResourceExtensions = {
description,
pg: {
serviceName,
Expand Down Expand Up @@ -611,24 +610,13 @@ export const PgTablesPlugin: GraphileConfig.Plugin = {
before: ["inferred", "override"],
callback(behavior, codec) {
if (codec.attributes) {
const isUnloggedOrTemp =
codec.extensions?.pg?.persistence === "u" ||
codec.extensions?.pg?.persistence === "t";
return [
"resource:select",
"table",
...((!codec.isAnonymous
? ["resource:insert", "resource:update", "resource:delete"]
: []) as GraphileBuild.BehaviorString[]),
behavior,
...((isUnloggedOrTemp
? [
"-resource:select",
"-resource:insert",
"-resource:update",
"-resource:delete",
]
: []) as GraphileBuild.BehaviorString[]),
...unloggedOrTempBehaviors(codec.extensions, behavior, null),
];
} else {
return [behavior];
Expand All @@ -641,28 +629,29 @@ export const PgTablesPlugin: GraphileConfig.Plugin = {
provides: ["default"],
before: ["inferred", "override"],
callback(behavior, resource) {
const isFunction = !!resource.parameters;
const ext = resource.extensions;
const isUnloggedOrTemp =
ext?.pg?.persistence === "u" || ext?.pg?.persistence === "t";
return [
...(ext?.isInsertable === false ? ["-resource:insert"] : []),
...(ext?.isUpdatable === false ? ["-resource:update"] : []),
...(ext?.isDeletable === false ? ["-resource:delete"] : []),
...(!isFunction && !isUnloggedOrTemp ? ["resource:select"] : []),
behavior,
...(isUnloggedOrTemp
? [
"-resource:select",
"-resource:insert",
"-resource:update",
"-resource:delete",
]
: []),
...unloggedOrTempBehaviors(ext, behavior, resource),
] as GraphileBuild.BehaviorString[];
},
},
},
pgResourceUnique: {
inferred: {
provides: ["default"],
before: ["inferred", "override"],
callback(behavior, [resource, _unique]) {
return unloggedOrTempBehaviors(
resource.extensions,
behavior,
resource,
);
},
},
},
},
hooks: {
init(_, build, _context) {
Expand All @@ -689,12 +678,7 @@ export const PgTablesPlugin: GraphileConfig.Plugin = {
return;
}

const selectable = build.behavior.pgCodecMatches(
codec,
"resource:select",
);

if (selectable) {
if (isTable) {
build.registerObjectType(
tableTypeName,
{
Expand All @@ -721,8 +705,8 @@ export const PgTablesPlugin: GraphileConfig.Plugin = {
{
pgCodec: codec,
isInputType: true,
isPgRowType: selectable,
isPgCompoundType: !selectable,
isPgRowType: isTable,
isPgCompoundType: !isTable,
},
() => ({
description: `An input for mutations affecting \`${tableTypeName}\``,
Expand Down Expand Up @@ -754,8 +738,8 @@ export const PgTablesPlugin: GraphileConfig.Plugin = {
{
pgCodec: codec,
isPgPatch: true,
isPgRowType: selectable,
isPgCompoundType: !selectable,
isPgRowType: isTable,
isPgCompoundType: !isTable,
},
() => ({
description: `Represents an update to a \`${tableTypeName}\`. Fields that are set will be updated.`,
Expand Down Expand Up @@ -783,8 +767,8 @@ export const PgTablesPlugin: GraphileConfig.Plugin = {
{
pgCodec: codec,
isPgBaseInput: true,
isPgRowType: selectable,
isPgCompoundType: !selectable,
isPgRowType: isTable,
isPgCompoundType: !isTable,
},
() => ({
description: `An input representation of \`${tableTypeName}\` with nullable fields.`,
Expand All @@ -806,7 +790,7 @@ export const PgTablesPlugin: GraphileConfig.Plugin = {
}

if (
selectable &&
isTable &&
!codec.isAnonymous
// Even without the 'connection' behavior we may still need the connection type in specific circumstances
// && build.behavior.pgCodecMatches(codec, "*:connection")
Expand All @@ -830,3 +814,31 @@ export const PgTablesPlugin: GraphileConfig.Plugin = {
},
},
};

function unloggedOrTempBehaviors(
extensions:
| Partial<DataplanPg.PgCodecExtensions>
| Partial<DataplanPg.PgResourceExtensions>
| undefined,
behavior: GraphileBuild.BehaviorString,
resource: PgResource | null,
): GraphileBuild.BehaviorString[] {
const isUnloggedOrTemp =
extensions?.pg?.persistence === "u" || extensions?.pg?.persistence === "t";
return [
...(resource && !resource.parameters ? ["resource:select" as const] : []),
behavior,
...(isUnloggedOrTemp
? ([
"-resource:select",
"-resource:connection",
"-resource:list",
"-resource:array",
"-resource:single",
"-resource:insert",
"-resource:update",
"-resource:delete",
] as const)
: []),
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ const spec_lotsOfEnums = {
},
tags: Object.assign(Object.create(null), {
omit: true,
behavior: ["-*"]
behavior: ["-insert -select -node -connection -list -array -single -update -delete -queryField -mutationField -typeField -filter -filterBy -order -orderBy -query:resource:list -query:resource:connection -singularRelation:resource:list -singularRelation:resource:connection -manyRelation:resource:list -manyRelation:resource:connection -manyToMany"]
})
},
executor: executor
Expand Down
Loading
Loading