diff --git a/graphile.config.mjs b/graphile.config.mjs index 0166d12..b476652 100644 --- a/graphile.config.mjs +++ b/graphile.config.mjs @@ -11,6 +11,7 @@ import PersistedPlugin from "@grafserv/persisted"; import { PgOmitArchivedPlugin } from "@graphile-contrib/pg-omit-archived"; import { dirname } from "path"; import { fileURLToPath } from "url"; +import { myExtensions } from "./plugins.mjs"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -25,6 +26,7 @@ const preset = { AmberPreset.default ?? AmberPreset, makeV4Preset({ /* Enter your V4 options here */ + dynamicJson: true, graphiql: true, graphiqlRoute: "/", }), @@ -33,7 +35,7 @@ const preset = { PgAggregatesPreset, // PgSimplifyInflectionPreset ], - plugins: [PersistedPlugin.default, PgOmitArchivedPlugin, TagsFilePlugin], + plugins: [PersistedPlugin.default, PgOmitArchivedPlugin, TagsFilePlugin, myExtensions], pgServices: [ makePgService({ // Database connection string: diff --git a/plugins.mjs b/plugins.mjs new file mode 100644 index 0000000..72ec0a2 --- /dev/null +++ b/plugins.mjs @@ -0,0 +1,104 @@ +import { TYPES } from 'postgraphile/@dataplan/pg'; +import { gql, makeExtendSchemaPlugin } from "postgraphile/utils"; + +export const myExtensions = makeExtendSchemaPlugin((build) => { + const { + input: { + pgRegistry: { + pgResources: { shop, owner, v_metadata, image, get_shop_info }, + }, + }, + grafast: { each, constant, lambda, object }, + } = build; + + return { + typeDefs: gql` + extend type Query { + shopsByIds(ids: [String!]!): [Shop]! + } + + extend type Shop { + info: ShopInfo + owner: Owner + options: MetaOptions + image: Image + } + + type ShopInfo { + petInfo: JSON + customersInfo: JSON + } + + type MetaOptions { + pickup: Boolean! + worldwide: Boolean! + random: String + } + + extend type ShopFrontImage { + layouts: Image + } + `, + plans: { + Query: { + shopsByIds(_, { $ids }) { + return each($ids, ($id) => shop.find({ id: $id, visible: constant(true) }).single()) + } + }, + Shop: { + info($parent) { + return $parent; + }, + image($parent) { + const $layouts = image.find({ + parent_id: $parent.get('id'), + parent_type: 'shop', + type: 'front', + }); + return $layouts.single(); + }, + owner($parent) { + return owner + .find({ + id: $parent.get('owner_id'), + }) + .single(); + }, + options($parent) { + const special = v_metadata.find({ + id: $parent.get('dis_special'), + }); + const specialTitle = special.single().get('title'); + return object({ + pickup: lambda($parent.get('di_pickup'), (v) => v !== null), + worldwide: lambda($parent.get('di_worldwide'), (v) => v !== null), + random: specialTitle, + }); + } + }, + ShopInfo: { + petInfo($parent) { + const $fnData = get_shop_info.execute([{ step: $parent.get('id'), pgCodec: TYPES.int, name: 'id_param' }], 'normal') + + return $fnData; + }, + customersInfo($parent) { + const $fnData = get_shop_info.execute([{ step: $parent.get('id'), pgCodec: TYPES.int, name: 'id_param' }], 'normal') + + return $fnData; + } + }, + ShopFrontImage: { + layouts($parent) { + const $layouts = image.find({ + parent_id: $parent.get('id'), + parent_type: 'shop', + type: 'front', + }); + return $layouts.single(); + } + }, + }, + }; +}); + diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..7adc96c --- /dev/null +++ b/schema.sql @@ -0,0 +1,161 @@ +drop table if exists public.owner; +create table public.owner( + id text, + name text +); +insert into public.owner (id, name) +values ('1', 'Andrey'), ('2', 'Kaarel'); + +drop table if exists public.shop; +create table public.shop( + id text, + name text, + visible boolean, + owner_id text, + di_pickup text, + di_worldwide text, + dis_special text +); +INSERT INTO public.shop ( + id, + name, + visible, + owner_id, + di_pickup, + di_worldwide, + dis_special +) VALUES ( + '1', + 'One', + true, + '1', + '1', + '2', + '3' +), +( + '2', + 'Two', + true, + '2', + '1', + '2', + '3' +), +( + '3', + 'Three', + false, + '1', + '1', + '2', + '3' +), +( + '4', + 'Four', + true, + '2', + '1', + '2', + '3' +); + +drop table if exists public.pet; +create table public.pet( + id text, + name text, + type text, + shop_id text +); +insert into public.pet (id, name, type, shop_id) +values ('1', 'Nikki', 'cat', '1'), + ('2', 'Brown', 'dog', '2'); + +drop view if exists public.v_metadata; + +drop table if exists public.metadata; +create table public.metadata( + id text, + type text, + title text +); +INSERT INTO public.metadata (id, type, title) +VALUES ('1', 'pickup', '10% off on all pickups'), + ('2', 'donate', 'We accept your valuable donations via cards'), + ('3', 'worldwide', 'We delivery anywhere with care'); + +drop table if exists public.metadata_enriched; +create table public.metadata_enriched( + id text, + type text, + title text, + enriched_text text +); +INSERT INTO public.metadata_enriched (id, type, title, enriched_text) +VALUES ('1', 'pickup', '10% off on all pickups', 'pickup enriched'), + ('2', 'donate', 'We accept your valuable donations via cards', 'donate enriched'), + ('3', 'worldwide', 'We delivery anywhere with care', 'worldwide enriched'); + +drop table if exists public.app_config CASCADE; +create table public.app_config +( + id TEXT PRIMARY KEY, + feed_date TIMESTAMPTZ NOT NULL DEFAULT NOW(), + name TEXT NOT NULL default 'Fake name', + data jsonb +); +insert into public.app_config (id, data) +values ('seo', '{ "some": "data", "other": "more data"}'); + +drop view if exists public.v_metadata; +CREATE VIEW public.v_metadata AS +SELECT + m.id, + m.type, + m.title, + me.enriched_text +FROM public.metadata m + LEFT JOIN public.metadata_enriched me ON m.id = me.id + LEFT JOIN LATERAL ( + SELECT data as config + FROM public.app_config + WHERE id = 'seo' + LIMIT 1 + ) sc ON TRUE; + +drop table if exists public.image; +create table public.image( + parent_type text, + parent_id text, + type text, + original text, + dim64x64 text, + dim128x128 text, + + primary key (parent_type, parent_id, type) +); +insert into public.image (parent_type, parent_id, type, original, dim64x64, dim128x128) +values ('shop', '1', 'front', 'http://fake-original', 'http://fake-64', null), +('shop', '2', 'front', 'http://fake-original', 'http://fake-64', 'http://fake-128'); + +CREATE OR REPLACE FUNCTION public.get_shop_info(id_param text) +RETURNS json AS $$ +DECLARE + result_row json; +BEGIN + SELECT json_build_object( + 'assetId', s.id, + 'petId', p.id, + 'petType', p.type, + 'text', s.name || ' - ' || p.name + ) + INTO result_row + FROM public.shop s + LEFT JOIN public.pet p + ON s.id = p.shop_id + WHERE s.id = id_param; + + RETURN result_row; +END; +$$ LANGUAGE plpgsql STABLE;