From e2858538074596072b4b8fea6ff58317f33171dc Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Wed, 7 Oct 2020 17:43:02 +0200 Subject: [PATCH] feature: add postgraphile-tag-json-tools for json schema generation --- @app/server/package.json | 1 + .../server/pg-database-smart-tags.schema.json | 494 ++++++++++++++++++ @app/server/pg-smart-tags.schema.json | 88 ++++ @app/server/postgraphile.tags.jsonc | 2 +- .../src/middleware/installPostGraphile.ts | 15 + yarn.lock | 5 + 6 files changed, 604 insertions(+), 1 deletion(-) create mode 100644 @app/server/pg-database-smart-tags.schema.json create mode 100644 @app/server/pg-smart-tags.schema.json diff --git a/@app/server/package.json b/@app/server/package.json index a7cdffd6..6b48c80d 100644 --- a/@app/server/package.json +++ b/@app/server/package.json @@ -45,6 +45,7 @@ "passport-github2": "^0.1.12", "pg": "^8.0.3", "postgraphile": "^4.9.0", + "postgraphile-tag-json-tools": "^0.1.1", "redis": "^3.0.2", "source-map-support": "^0.5.13", "tslib": "^2.0.1" diff --git a/@app/server/pg-database-smart-tags.schema.json b/@app/server/pg-database-smart-tags.schema.json new file mode 100644 index 00000000..f93a7387 --- /dev/null +++ b/@app/server/pg-database-smart-tags.schema.json @@ -0,0 +1,494 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "JSONPgSmartTags", + "type": "object", + "properties": { + "version": { + "type": "number", + "minimum": 1 + }, + "config": { + "type": "object", + "properties": { + "class": { + "type": "object", + "properties": { + "app_public.organization_invitations": { + "$ref": "#/definitions/class:app_public.organization_invitations" + }, + "organization_invitations": { + "$ref": "#/definitions/class:app_public.organization_invitations" + }, + "app_public.organization_memberships": { + "$ref": "#/definitions/class:app_public.organization_memberships" + }, + "organization_memberships": { + "$ref": "#/definitions/class:app_public.organization_memberships" + }, + "app_public.organizations": { + "$ref": "#/definitions/class:app_public.organizations" + }, + "organizations": { + "$ref": "#/definitions/class:app_public.organizations" + }, + "app_public.user_authentications": { + "$ref": "#/definitions/class:app_public.user_authentications" + }, + "user_authentications": { + "$ref": "#/definitions/class:app_public.user_authentications" + }, + "app_public.user_emails": { + "$ref": "#/definitions/class:app_public.user_emails" + }, + "user_emails": { + "$ref": "#/definitions/class:app_public.user_emails" + }, + "app_public.users": { + "$ref": "#/definitions/class:app_public.users" + }, + "users": { + "$ref": "#/definitions/class:app_public.users" + } + }, + "additionalProperties": false + }, + "attribute": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "app_public.organization_invitations.code", + "app_public.organization_invitations.email", + "app_public.organization_invitations.id", + "app_public.organization_invitations.organization_id", + "app_public.organization_invitations.user_id", + "app_public.organization_memberships.created_at", + "app_public.organization_memberships.id", + "app_public.organization_memberships.is_billing_contact", + "app_public.organization_memberships.is_owner", + "app_public.organization_memberships.organization_id", + "app_public.organization_memberships.user_id", + "app_public.organizations.created_at", + "app_public.organizations.id", + "app_public.organizations.name", + "app_public.organizations.slug", + "app_public.user_authentications.created_at", + "app_public.user_authentications.details", + "app_public.user_authentications.id", + "app_public.user_authentications.identifier", + "app_public.user_authentications.service", + "app_public.user_authentications.updated_at", + "app_public.user_authentications.user_id", + "app_public.user_emails.created_at", + "app_public.user_emails.email", + "app_public.user_emails.id", + "app_public.user_emails.is_primary", + "app_public.user_emails.is_verified", + "app_public.user_emails.updated_at", + "app_public.user_emails.user_id", + "app_public.users.avatar_url", + "app_public.users.created_at", + "app_public.users.id", + "app_public.users.is_admin", + "app_public.users.is_verified", + "app_public.users.name", + "app_public.users.updated_at", + "app_public.users.username", + "avatar_url", + "code", + "created_at", + "details", + "email", + "id", + "identifier", + "is_admin", + "is_billing_contact", + "is_owner", + "is_primary", + "is_verified", + "name", + "organization_id", + "organization_invitations.code", + "organization_invitations.email", + "organization_invitations.id", + "organization_invitations.organization_id", + "organization_invitations.user_id", + "organization_memberships.created_at", + "organization_memberships.id", + "organization_memberships.is_billing_contact", + "organization_memberships.is_owner", + "organization_memberships.organization_id", + "organization_memberships.user_id", + "organizations.created_at", + "organizations.id", + "organizations.name", + "organizations.slug", + "service", + "slug", + "updated_at", + "user_authentications.created_at", + "user_authentications.details", + "user_authentications.id", + "user_authentications.identifier", + "user_authentications.service", + "user_authentications.updated_at", + "user_authentications.user_id", + "user_emails.created_at", + "user_emails.email", + "user_emails.id", + "user_emails.is_primary", + "user_emails.is_verified", + "user_emails.updated_at", + "user_emails.user_id", + "user_id", + "username", + "users.avatar_url", + "users.created_at", + "users.id", + "users.is_admin", + "users.is_verified", + "users.name", + "users.updated_at", + "users.username" + ] + } + }, + "constraint": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "organization_invitations_organization_id_email_key", + "organization_invitations_organization_id_fkey", + "organization_invitations_organization_id_user_id_key", + "organization_invitations_pkey", + "organization_invitations_user_id_fkey", + "organization_memberships_organization_id_fkey", + "organization_memberships_organization_id_user_id_key", + "organization_memberships_pkey", + "organization_memberships_user_id_fkey", + "organizations_pkey", + "organizations_slug_key", + "uniq_user_authentications", + "user_authentications_pkey", + "user_authentications_user_id_fkey", + "user_emails_pkey", + "user_emails_user_id_email_key", + "user_emails_user_id_fkey", + "users_pkey", + "users_username_key" + ] + } + }, + "procedure": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "accept_invitation_to_organization", + "app_public.accept_invitation_to_organization", + "app_public.change_password", + "app_public.confirm_account_deletion", + "app_public.create_organization", + "app_public.current_session_id", + "app_public.current_user", + "app_public.current_user_id", + "app_public.current_user_invited_organization_ids", + "app_public.current_user_member_organization_ids", + "app_public.delete_organization", + "app_public.forgot_password", + "app_public.invite_to_organization", + "app_public.logout", + "app_public.make_email_primary", + "app_public.organization_for_invitation", + "app_public.organizations_current_user_is_billing_contact", + "app_public.organizations_current_user_is_owner", + "app_public.remove_from_organization", + "app_public.request_account_deletion", + "app_public.resend_email_verification_code", + "app_public.reset_password", + "app_public.transfer_organization_billing_contact", + "app_public.transfer_organization_ownership", + "app_public.users_has_password", + "app_public.verify_email", + "change_password", + "confirm_account_deletion", + "create_organization", + "current_session_id", + "current_user", + "current_user_id", + "current_user_invited_organization_ids", + "current_user_member_organization_ids", + "delete_organization", + "forgot_password", + "invite_to_organization", + "logout", + "make_email_primary", + "organization_for_invitation", + "organizations_current_user_is_billing_contact", + "organizations_current_user_is_owner", + "remove_from_organization", + "request_account_deletion", + "resend_email_verification_code", + "reset_password", + "transfer_organization_billing_contact", + "transfer_organization_ownership", + "users_has_password", + "verify_email" + ] + } + } + } + } + }, + "definitions": { + "class:app_public.organization_invitations": { + "type": "object", + "properties": { + "tags": { + "$ref": "./pg-smart-tags.schema.json#/definitions/tags" + }, + "description": { + "type": "string" + }, + "attribute": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "id", + "organization_id", + "code", + "user_id", + "email" + ] + } + }, + "constraint": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "organization_invitations_pkey", + "organization_invitations_organization_id_fkey", + "organization_invitations_organization_id_user_id_key", + "organization_invitations_organization_id_email_key", + "organization_invitations_user_id_fkey" + ] + } + } + } + }, + "class:app_public.organization_memberships": { + "type": "object", + "properties": { + "tags": { + "$ref": "./pg-smart-tags.schema.json#/definitions/tags" + }, + "description": { + "type": "string" + }, + "attribute": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "id", + "organization_id", + "user_id", + "is_owner", + "is_billing_contact", + "created_at" + ] + } + }, + "constraint": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "organization_memberships_pkey", + "organization_memberships_organization_id_fkey", + "organization_memberships_organization_id_user_id_key", + "organization_memberships_user_id_fkey" + ] + } + } + } + }, + "class:app_public.organizations": { + "type": "object", + "properties": { + "tags": { + "$ref": "./pg-smart-tags.schema.json#/definitions/tags" + }, + "description": { + "type": "string" + }, + "attribute": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "id", + "slug", + "name", + "created_at" + ] + } + }, + "constraint": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "organizations_pkey", + "organizations_slug_key" + ] + } + } + } + }, + "class:app_public.user_authentications": { + "type": "object", + "properties": { + "tags": { + "$ref": "./pg-smart-tags.schema.json#/definitions/tags" + }, + "description": { + "type": "string" + }, + "attribute": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "id", + "user_id", + "service", + "identifier", + "details", + "created_at", + "updated_at" + ] + } + }, + "constraint": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "user_authentications_pkey", + "user_authentications_user_id_fkey", + "uniq_user_authentications" + ] + } + } + } + }, + "class:app_public.user_emails": { + "type": "object", + "properties": { + "tags": { + "$ref": "./pg-smart-tags.schema.json#/definitions/tags" + }, + "description": { + "type": "string" + }, + "attribute": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "id", + "user_id", + "email", + "is_verified", + "is_primary", + "created_at", + "updated_at" + ] + } + }, + "constraint": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "user_emails_pkey", + "user_emails_user_id_fkey", + "user_emails_user_id_email_key" + ] + } + } + } + }, + "class:app_public.users": { + "type": "object", + "properties": { + "tags": { + "$ref": "./pg-smart-tags.schema.json#/definitions/tags" + }, + "description": { + "type": "string" + }, + "attribute": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "id", + "username", + "name", + "avatar_url", + "is_admin", + "is_verified", + "created_at", + "updated_at" + ] + } + }, + "constraint": { + "type": "object", + "additionalProperties": { + "$ref": "./pg-smart-tags.schema.json#/definitions/pgEntity" + }, + "propertyNames": { + "enum": [ + "users_pkey", + "users_username_key" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/@app/server/pg-smart-tags.schema.json b/@app/server/pg-smart-tags.schema.json new file mode 100644 index 00000000..0bb977b8 --- /dev/null +++ b/@app/server/pg-smart-tags.schema.json @@ -0,0 +1,88 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "JSONPgSmartTags", + "type": "object", + "properties": { + "version": { + "type": "number", + "minimum": 1 + }, + "config": { + "type": "object", + "properties": { + "class": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pgClass" + } + }, + "attribute": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pgEntity" + } + }, + "constraint": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pgEntity" + } + }, + "procedure": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pgEntity" + } + } + } + } + }, + "definitions": { + "tags": { + "$id": "#tags", + "type": "object", + "$comment": "------ Add extra tags here, in `properties`. ------", + "properties": { + "omit": { "oneOf": [{ "type": "boolean" }, { "type": "string" }] }, + "notNull": { "type": "boolean" }, + "filterable": { "type": "boolean" }, + "sortable": { "type": "boolean" }, + "name": { "type": "string" }, + "fieldName": { "type": "string" }, + "foreignFieldName": { "type": "string" }, + "foreignSingleFieldName": { "type": "string" }, + "foreignKey": { "type": "string" }, + "primaryKey": { "type": "string" }, + "simpleCollections": { "type": "string" }, + "resultFieldName": { "type": "string" } + }, + "additionalProperties": false + }, + "pgEntity": { + "$id": "#pgEntity", + "type": "object", + "properties": { + "tags": { "$ref": "#/definitions/tags" }, + "description": { "type": "string" } + }, + "additionalProperties": false + }, + "pgClass": { + "type": "object", + "properties": { + "tags": { "$ref": "#/definitions/tags" }, + "description": { "type": "string" }, + "attribute": { + "type": "object", + "additionalProperties": { "$ref": "#/definitions/pgEntity" } + }, + "constraint": { + "type": "object", + "additionalProperties": { "$ref": "#/definitions/pgEntity" } + } + }, + "additionalProperties": false + } + } +} diff --git a/@app/server/postgraphile.tags.jsonc b/@app/server/postgraphile.tags.jsonc index 7b1c6f6f..f3508f19 100644 --- a/@app/server/postgraphile.tags.jsonc +++ b/@app/server/postgraphile.tags.jsonc @@ -7,7 +7,7 @@ * See https://www.graphile.org/postgraphile/smart-tags/ for more information. */ { - "$schema": "https://gist.githubusercontent.com/singingwolfboy/a7144db4e24b5d31ba81f28b878a4b51/raw/cd1e371baa41756e02f1d6f36a3dea86388d4084/pg-smart-tags-schema.json", + "$schema": "./pg-database-smart-tags.schema.json", "version": 1, "config": { "class": { diff --git a/@app/server/src/middleware/installPostGraphile.ts b/@app/server/src/middleware/installPostGraphile.ts index 705d899a..f4fae927 100644 --- a/@app/server/src/middleware/installPostGraphile.ts +++ b/@app/server/src/middleware/installPostGraphile.ts @@ -12,6 +12,7 @@ import { postgraphile, PostGraphileOptions, } from "postgraphile"; +import { createJsonSchema as CreateJsonSchemaPlugin } from "postgraphile-tag-json-tools"; import { makePgSmartTagsFromFilePlugin } from "postgraphile/plugins"; import { getHttpServer, getWebsocketMiddlewares } from "../app"; @@ -183,6 +184,14 @@ export function getPostGraphileOptions({ // Adds custom orders to our GraphQL schema OrdersPlugin, + + // Plugins for just Development mode + ...(!isDev + ? [] + : [ + // updates the `pg-database-smart-tags.schema.json` file based on the database + CreateJsonSchemaPlugin, + ]), ], /* @@ -201,6 +210,12 @@ export function getPostGraphileOptions({ // Makes all SQL function arguments except those with defaults non-nullable pgStrictFunctions: true, + + tagJsonPlugin: { + // tells the CreateJsonSchemaPlugin to create the `pg-database-smart-tags.schema.json` + // file in the base folder, next to the `postgraphile.tags.jsonc` file. + tagFileFolder: resolve(__dirname, "../.."), + }, }, /* diff --git a/yarn.lock b/yarn.lock index bc74deb3..2dfdddda 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12754,6 +12754,11 @@ postgraphile-core@4.9.0: graphile-build-pg "4.9.0" tslib "^2.0.1" +postgraphile-tag-json-tools@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postgraphile-tag-json-tools/-/postgraphile-tag-json-tools-0.1.1.tgz#3ddc102b11da00c9981d330ad3d5e38235a3fb97" + integrity sha512-iN1WDSQsX+BY7yqds4U/hT4EWkzh5UIi/8WhR2fvEPOkmT7CojmyYFhFd6FmFT60tNfJ3ktDeZPt+BmyjT+ALg== + postgraphile@^4.9.0: version "4.9.0" resolved "https://registry.yarnpkg.com/postgraphile/-/postgraphile-4.9.0.tgz#71bca224833f702803169802ce9b79e36b739be1"