diff --git a/schema.sql b/schema.sql index 9fa576e..9d35945 100644 --- a/schema.sql +++ b/schema.sql @@ -1 +1,19 @@ --- Create your database schema here +create type item_type as enum ( + 'TOPIC' +); + +create table relational_items ( + id serial primary key, + type item_type not null default 'TOPIC'::item_type, + deleted_at timestamp with time zone +); + +create table relational_topics ( + id int primary key references relational_items, + title text not null +); + +comment on table relational_items is $$ + @interface mode:relational type:type + @type TOPIC references:relational_topics + $$; \ No newline at end of file diff --git a/src/graphile.config.ts b/src/graphile.config.ts index a052483..f6e4222 100644 --- a/src/graphile.config.ts +++ b/src/graphile.config.ts @@ -9,9 +9,57 @@ import { PgAggregatesPreset } from "@graphile/pg-aggregates"; import { PgManyToManyPreset } from "@graphile-contrib/pg-many-to-many"; // import { PgSimplifyInflectionPreset } from "@graphile/simplify-inflection"; import PersistedPlugin from "@grafserv/persisted"; -import { PgOmitArchivedPlugin } from "@graphile-contrib/pg-omit-archived"; +import { + PgOmitArchivedPlugin, + custom as customPgOmitArchived, +} from "@graphile-contrib/pg-omit-archived"; import { dirname } from "path"; import { fileURLToPath } from "url"; +import type { PgSQL, SQL } from "postgraphile/pg-sql2"; + +declare global { + namespace GraphileBuild { + interface SchemaOptions { + /** + * The name of the column to use to determine if the record is archived + * or not. Defaults to 'is_archived' + */ + pgDeletedColumnName?: string; + /** + * Set this true to invert the column logic - i.e. if your column is + * `is_visible` instead of `is_archived`. + */ + pgDeletedColumnImpliesVisible?: boolean; + /** + * If your determination of whether a record is archived or not is more complex + * than checking if a column is not null/not false then you can define an SQL + * expression instead. + */ + pgDeletedExpression?: (sql: PgSQL, tableAlias: SQL) => SQL; + /** + * The default option to use for the 'includeDeleted' argument. Defaults + * to 'NO', but will be replaced with 'INHERIT' where possible unless you set + * `pgDeletedDefaultInherit` to false. + */ + pgDeletedDefault?: "INHERIT" | "NO" | "YES" | "EXCLUSIVELY"; + /** + * Set false if you don't want the system to default to 'INHERIT' if it's + * able to do so. + */ + pgDeletedDefaultInherit?: boolean; + /** + * Set true if you want related record collections to have the + * pg-omit-archived behavior if they belong to a table that explicitly + * matches. + */ + pgDeletedRelations?: boolean; + /** + * If you want the system to apply the archived filter to a specific list of tables, list their names here: + */ + pgDeletedTables?: string[]; + } + } +} const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -33,7 +81,12 @@ const preset: GraphileConfig.Preset = { PgAggregatesPreset, // PgSimplifyInflectionPreset ], - plugins: [PersistedPlugin.default, PgOmitArchivedPlugin, TagsFilePlugin], + plugins: [ + PersistedPlugin.default, + PgOmitArchivedPlugin, + customPgOmitArchived("deleted"), + TagsFilePlugin, + ], pgServices: [ makePgService({ // Database connection string: @@ -56,6 +109,14 @@ const preset: GraphileConfig.Preset = { grafast: { explain: true, }, + schema: { + pgDeletedColumnName: "deleted_at", + pgDeletedColumnImpliesVisible: false, + pgDeletedRelations: false, + pgDeletedDefaultInherit: true, + pgDeletedDefault: "NO", + pgDeletedTables: [], + }, }; export default preset; diff --git a/src/server-express.ts b/src/server-express.ts index 0c99af0..ef4e98d 100644 --- a/src/server-express.ts +++ b/src/server-express.ts @@ -2,6 +2,7 @@ import express from "express"; import { grafserv } from "postgraphile/grafserv/express/v4"; import { postgraphile } from "postgraphile"; import preset from "./graphile.config.ts"; +import { test } from "./test.ts"; // Create an express app const app = express(); @@ -24,3 +25,5 @@ const server = app.listen(port, host, () => { // Mount our Grafserv instance into our Express app, passing the HTTP server so // we can attach websocket support. serv.addTo(app, server); + +test(await serv.getSchema(), serv.getPreset()); diff --git a/src/test.ts b/src/test.ts new file mode 100644 index 0000000..11c3ff1 --- /dev/null +++ b/src/test.ts @@ -0,0 +1,36 @@ +import { grafast } from "postgraphile/grafast"; +import type { GraphQLSchema } from "postgraphile/graphql"; + +/** + * Should print "column __relational_topics__.deleted_at does not exist" error + * @param schema + * @param resolvedPreset + */ +export async function test( + schema: GraphQLSchema, + resolvedPreset: GraphileConfig.ResolvedPreset +) { + const result = await grafast({ + schema, + source: `query MyQuery { + allRelationalTopics { + nodes { + title + } + } +}`, + rootValue: null, + contextValue: {}, + variableValues: {}, + operationName: null, + resolvedPreset, + requestContext: {}, + }); + if (Symbol.asyncIterator in result) { + console.log("Async iterator result"); + } else if (result.errors) { + console.error(result.errors); + } else { + console.log("Success"); + } +}