profile
viewpoint
Benjie Gillam benjie @graphile Southampton, UK https://graphile.org/sponsor Community-funded OSS developer/maintainer, working in the GraphQL, Node.js and PostgreSQL ecosystems. Maintainer of PostGraphile & related projects. He/him.

benjie/backbone-redis-store 11

NodeJS Backbone mode to enable Redis as data persistence layer

acao/graphiql-1-poc-monaco 2

GraphiQL 1.x POC monaco editor edition

benjie/awesome-postgres 2

A curated list of awesome PostgreSQL software, libraries, tools and resources, inspired by awesome-mysql

benjie/backbone-null-store 2

Null storage engine for Backbone.js

acao/graphiql 1

An in-browser IDE for exploring GraphQL.

benjie/AFOAuth1Client 0

AFNetworking Extension for OAuth 1.0a Authentication

benjie/ale 0

Asynchronous Lint Engine

benjie/apollo-client 0

:rocket: A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server

pull request commentgraphile/worker

feat(get_job): add index

Do you mean a partial index on locked_at? The issue with that is that we actually query locked_at for the expiry check.

I was thinking more about the exhausted tries/failed forever. But actually that number is dynamic anyway, so I don't think that's tenable. I wonder if we should migrate jobs to a different table when they fail forever.

I think if the index order doesn't match the sort order, it can't use the index for the sorting, and then you end up with a sort over all non locked rows (expensive).

Yeah, I was hoping the filtering would come into play first, but generally jobs shouldn't be locked, so that should be something we check very late. Though I'm not sure that it shouldn't come before id.

ben-pr-p

comment created time in 12 hours

pull request commentgraphile/worker

feat(get_job): add index

(I also wonder if using a partial index instead might be a good solution; but I'm not sure of the cost of removing/adding items to the index so frequently.)

ben-pr-p

comment created time in 15 hours

pull request commentgraphile/worker

feat(get_job): add index

I wonder if locked_at should be the first field in the index? I've not checked over the code to validate this gut feeling.

ben-pr-p

comment created time in 15 hours

issue commentgraphql/graphql-spec

Expose user-defined meta-information via introspection API in form of directives

Everything that's representable via introspection should be representable via the GraphQL IDL and vice-versa. GraphQL tooling such as linting, change detection, etc often uses the IDL as the interchange format, rather than using the JSON of a GraphQL introspection query; this is sensible because the IDL should always be parseable and complete, whereas the introspection query may differ for different tools due to the flexibility of GraphQL's query language. It's important to consider both how this metadata will be exposed through GraphQL introspection (e.g. via the proposed "extensions" fields) and through the IDL (e.g. we're proposing here to use the directive syntax to expose this metadata in IDL, but we could come up with a different IDL representation for it if it's determined that using directive syntax for this does not make sense). Personally, I'm a fan of using directive syntax for this, because it already has form with deprecationReason being exposed as the @deprecated tag, though I do worry about how to differentiate user-defined metadata with future GraphQL official metadata.

OlegIlyenko

comment created time in 16 hours

pull request commentgraphile/pg-simplify-inflector

chore: convert to typescript

The types that are in the repo are IMO wrong.

Can you explain this? This module can be imported as either require('...') or require('...').default and it includes the __esModule special non-enumerable property, so it should be importable in either way. It works fine from TypeScript independent of your settings (I think?)... Why do you feel it would be better to make a less convenient import, or require esModuleInterop?

https://github.com/graphile/pg-simplify-inflector/blob/1dd49a877b53901b6c5b15091fddcf9164a1ef73/index.js#L490-L493

keithlayne

comment created time in 20 hours

issue commentgraphile/postgraphile

There is no description about the method of xxxById in the postgraphile document, it shows ‘No Description’

Issue does not indicate which field this is, but I'm guessing it's the fields on the query root rather than the mutation root. Solveable by adding description entry here:

https://github.com/graphile/graphile-engine/blob/9ed5c9ab3bd484236773bf20d50c22db0c493632/packages/graphile-build-pg/src/plugins/PgRowByUniqueConstraint.js#L103

PR welcome.

mywin

comment created time in 20 hours

issue closedgraphile/postgraphile

Cannot use the keyword'xxx_conditions' to define the table name, when there is a table called'xxx'

Summary

First create a table named'xxx', then create another table called'xxx_conditions', start postgraphile, it will report an error, Adding condition type for table "xxx". You can rename the table's GraphQL type via a'Smart Comment':

closed time in 20 hours

mywin

issue commentgraphile/postgraphile

Cannot use the keyword'xxx_conditions' to define the table name, when there is a table called'xxx'

Indeed, this is a naming conflict and is working as designed.

You can rename the table's GraphQL type via a 'Smart Tag' to avoid the conflict.

https://www.graphile.org/postgraphile/smart-tags/#name

mywin

comment created time in 20 hours

Pull request review commentgraphql/graphql-over-http

Specify concrete error status codes

 may not be the GraphQL server but instead some intermediary such as API gateways  ## Status Codes -If the `data` entry in the response has any value other than `null` (when the operation has successfully executed-without error) then the response should use the 200 (OK) status code.-If the operation failed before or during execution, due to a syntax error, missing information, validation error-or any other reason, then response should use the 4XX or 5XX status codes.-It is recommended to use the same error codes as the [reference implementation](https://github.com/graphql/express-graphql).+If the response has Content-Type GraphQL and contains a non-null `data` entry,+then it MUST have status code `2xx`, and it SHOULD have status code `200`.++If the response has Content-Type GraphQL and has status code `2xx`,+the entry `data` entry must be either:+- equal to `null` (in case of an execution error)+- not present (in case of a validation error)++The result of executing a GraphQL operation may contain partial data as well as encountered errors.+Errors that happen during execution of the GraphQL operation typically become part of the result,+as long as the server is still able to produce a well-formed response.++In case of errors that completely prevent the successful execution of the request,+the server SHOULD respond with the appropriate status code depending on the concrete error condition.++There are four types of validation errors:++1. Unparseable or invalid request body, for example `NONSENSE` or `{"qeury": "{__typename}"}`+2. GraphQL Specification document validation errors (valid syntax, no loops in fragments, etc)+3. Custom document validations that can be performed before execution (e.g. graphql-depth-limit)+4. Runtime validations performed by resolvers (i.e. during the execution of the GraphQL operation).++The error types 1. and 2. prevent execution of the request entirely and MUST result in+status code `400` (Bad Request). Types 3. and 4. may still allow partial execution and+MUST result in status code `200`.

(I note "baseline Spec compliant GraphQL servers" because some implementations (GraphQL Java?) have experimental support for indicating directives applied to fields within the introspection results.)

spawnia

comment created time in 20 hours

PullRequestReviewEvent

Pull request review commentgraphql/graphql-over-http

Specify concrete error status codes

 may not be the GraphQL server but instead some intermediary such as API gateways  ## Status Codes -If the `data` entry in the response has any value other than `null` (when the operation has successfully executed-without error) then the response should use the 200 (OK) status code.-If the operation failed before or during execution, due to a syntax error, missing information, validation error-or any other reason, then response should use the 4XX or 5XX status codes.-It is recommended to use the same error codes as the [reference implementation](https://github.com/graphql/express-graphql).+If the response has Content-Type GraphQL and contains a non-null `data` entry,+then it MUST have status code `2xx`, and it SHOULD have status code `200`.++If the response has Content-Type GraphQL and has status code `2xx`,+the entry `data` entry must be either:+- equal to `null` (in case of an execution error)+- not present (in case of a validation error)++The result of executing a GraphQL operation may contain partial data as well as encountered errors.+Errors that happen during execution of the GraphQL operation typically become part of the result,+as long as the server is still able to produce a well-formed response.++In case of errors that completely prevent the successful execution of the request,+the server SHOULD respond with the appropriate status code depending on the concrete error condition.++There are four types of validation errors:++1. Unparseable or invalid request body, for example `NONSENSE` or `{"qeury": "{__typename}"}`+2. GraphQL Specification document validation errors (valid syntax, no loops in fragments, etc)+3. Custom document validations that can be performed before execution (e.g. graphql-depth-limit)+4. Runtime validations performed by resolvers (i.e. during the execution of the GraphQL operation).++The error types 1. and 2. prevent execution of the request entirely and MUST result in+status code `400` (Bad Request). Types 3. and 4. may still allow partial execution and+MUST result in status code `200`.

Both of the above mutations are always executed independently but never together

This seems to be a human rule rather than a computer rule. How can the computer know this? How would it know the following operation is forbidden?

mutation TakePaymentAndCreateAccount ($accountDetails: CreateAccountInput!, $paymentDetails: TakePaymentInput!) {
  takePayment(input: $paymentDetails) { success errors { code message } }
  createAccount(input: $accountDetails) { userId }
}

It's very common during a checkout process for the website to also invite you to create an account. How can we expand this to GraphQL in general that's not specific to your server's setup?


Regarding your example of the @idempotent directive on a mutation, there's currently no way for baseline Spec compliant GraphQL servers to indicate that a mutation has this tag; it's something that I hope we will have in the coming years but work on that is ongoing. So the server cannot indicate this; and further even if the server did indicate this through GraphQL then for an intermediary service to understand it, it would need to understand GraphQL, at which point using the HTTP status code for auto-retry becomes unnecessary since the gateway can extract the information it needs from the GraphQL request directly (although slight rewriting of the query might be necessary if the user did not request sufficient information). The same "proxy server needs to understand GraphQL" applies also to the case of a client-indicated @idempotent directive which is what your example demonstrates (although I'm not sure if it is what you meant?).

spawnia

comment created time in 20 hours

PullRequestReviewEvent

issue commentgraphile/graphile.github.io

Benjie's responses megathread

Personally I simplify things by using only one PostgreSQL role at runtime (I call this the "visitor" role, since anyone (authenticated or otherwise) who visits my site/app is a visitor) and then using RLS policies to govern what said visitor can do based on who they are. I split tables on permission boundaries, so the permissions granted (e.g. ability to update "name", "bio" field) are common to a table (e.g. "organizations") and the only decision is whether or not the current user can do those things (e.g. create policy update_admins on organizations for update using (id in (select list_of_org_ids_current_user_is_admin_of()))). Note the policy only chooses which rows can be affected; the role chooses which columns can be affected. If there are situations where you cannot split on permission boundaries and some role needs to also be able to manipulate a particular field where others can manipulate that record but not that field, then you can use a SECURITY DEFINER custom mutation function to enable operations that wouldn't normally be allowed by RBAC/RLS.

-- https://discord.com/channels/489127045289476126/498852330754801666/770223393499774996

benjie

comment created time in 20 hours

issue commentgraphile/postgraphile

pgSubscription async iterator isn't stopped by client unsubscribe or disconnect

By replacing the iterator returned from MinimalSubscribeWrapper with this:

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

async function* iterator() {
  try {
    for (let i = 0;;i++) {
      yield {value: String(i)};
      await sleep(1000);
    }
  } finally {
    console.log("ENDED");
  }
}

I can see that subscription ending normally works. So PostGraphile's server works fine and it's definitely a bug in or around @graphile/pg-pubsub. Moving to the engine repo.

gdeOo

comment created time in 4 days

push eventgraphile/pg-simplify-inflector

Keith Layne

commit sha 1dd49a877b53901b6c5b15091fddcf9164a1ef73

feat: `@listSuffix omit/include` smart tag (#32)

view details

push time in 4 days

PR merged graphile/pg-simplify-inflector

feat: `@listSuffix omit/include` smart tag

Description

I've been using this as a patch for several months, and kept forgetting to make a PR or open an issue about it. The use case is for when you are not using pgSimpleCollections = "only" and pgOmitListSuffix, but want to essentially apply those selectively.

I don't exactly remember the motivating case, or if it covers much behavior. Obviously needs tests, but throwing it out there for comment.

Performance impact

Looks like it has a small perf impact, it adds a couple of ternary expressions.

Security impact

unknown

Checklist

  • [x] My code matches the project's code style and yarn lint:fix passes.
  • [x] I've added tests for the new feature, and yarn test passes.
  • [x] I have detailed the new feature in the relevant documentation.
  • [x] I have added this feature to 'Pending' in the RELEASE_NOTES.md file (if one exists).
  • [x] If this is a breaking change I've explained why.
+2443 -22

11 comments

14 changed files

keithlayne

pr closed time in 4 days

PullRequestReviewEvent

push eventkeithlayne/pg-simplify-inflector

Benjie Gillam

commit sha 6b8344cb602f0aecbf2308d8730829dd0d29c2b1

Apply suggestions from code review

view details

push time in 4 days

Pull request review commentgraphile/pg-simplify-inflector

Add `omitListSuffix` tag

 suffix" ("List" by default, but "" if `pgOmitListSuffix` is set), but if you prefer you can override it entirely with `@foreignSimpleFieldName`. If you set `@foreignSimpleFieldName` and you're using `simpleCollections 'both'` then you should also set `@foreignFieldName` explicitly or unexpected things may occur.++Applies to:++- foreign key constraints++### `@listSuffix`++`@listSuffix` allows you to override the default naming on a per-entity basis,+overriding `pgOmitListSuffix`. For example, with `pgOmitListSuffix: true`, you+can apply `@listSuffix include` to have the list suffix appended to the simple+collection generated for that table. When `pgOmitListSuffix` is not `true`, then+you can use `@listSuffix omit` to selectively omit the list suffix on entities.++If `@listSuffix` is set, the only valid values are `"omit"` and `"include"`. Any+other value will cause an error.
other value will cause an error.

|                   | @listSuffix omit    | @listSuffix include |
| ----------------: | :------------------ | :------------------ |
|  Relay Connection | companiesConnection | companies           |
| Simple Collection | companies           | companiesList       |
keithlayne

comment created time in 4 days

Pull request review commentgraphile/pg-simplify-inflector

Add `omitListSuffix` tag

 suffix" ("List" by default, but "" if `pgOmitListSuffix` is set), but if you prefer you can override it entirely with `@foreignSimpleFieldName`. If you set `@foreignSimpleFieldName` and you're using `simpleCollections 'both'` then you should also set `@foreignFieldName` explicitly or unexpected things may occur.++Applies to:++- foreign key constraints++### `@listSuffix`++`@listSuffix` allows you to override the default naming on a per-entity basis,+overriding `pgOmitListSuffix`. For example, with `pgOmitListSuffix: true`, you+can apply `@listSuffix include` to have the list suffix appended to the simple+collection generated for that table. When `pgOmitListSuffix` is not `true`, then+you can use `@listSuffix omit` to selectively omit the list suffix on entities.
`@listSuffix` allows you to override the default naming on a per-entity basis,
overriding `pgOmitListSuffix`. For example, with `pgOmitListSuffix: true`, you
can apply `@listSuffix include` to have the `-List` suffix appended to the simple
collection generated for that table, and remove the `-Connection` suffix from the
Relay connection. When `pgOmitListSuffix` is not `true`, you can use
`@listSuffix omit` to selectively omit the `-List` suffix on simple collections
and append `-Connection` to the Relay connection instead.
keithlayne

comment created time in 4 days

PullRequestReviewEvent
PullRequestReviewEvent

release graphile/postgraphile

v4.9.2

released time in 4 days

created taggraphile/postgraphile

tagv4.9.2

Execute one command (or mount one Node.js middleware) and get an instant high-performance GraphQL API for your PostgreSQL database!

created time in 4 days

push eventgraphile/postgraphile

Benjie Gillam

commit sha 1da44d37c18afb3627cd8e9fc41b275514faee7a

4.9.2

view details

push time in 4 days

delete branch graphile/postgraphile

delete branch : upgrade-core

delete time in 4 days

push eventgraphile/postgraphile

Benjie Gillam

commit sha 660682ac5de873f7796816b1c15590c2e3f04a59

fix: upgrade postgraphile-core (#1369)

view details

push time in 4 days

PR merged graphile/postgraphile

fix: upgrade postgraphile-core
+15 -15

0 comment

2 changed files

benjie

pr closed time in 4 days

PR opened graphile/postgraphile

fix: upgrade postgraphile-core
+15 -15

0 comment

2 changed files

pr created time in 4 days

create barnchgraphile/postgraphile

branch : upgrade-core

created branch time in 4 days

push eventgraphile/graphile-engine

Benjie Gillam

commit sha 9ed5c9ab3bd484236773bf20d50c22db0c493632

chore: update CHANGELOG

view details

push time in 4 days

push eventgraphile/graphile-engine

Benjie Gillam

commit sha 042f35e5c7f26af8b4ed4cc82af1236ec797c234

v4.9.2

view details

push time in 4 days

created taggraphile/graphile-engine

tagv4.9.2

Monorepo home of graphile-build, graphile-build-pg, graphile-utils, postgraphile-core and graphql-parse-resolve-info. Build a high-performance easily-extensible GraphQL schema by combining plugins!

created time in 4 days

push eventgraphile/graphile-engine

Benjie Gillam

commit sha 5679a7f8d63851cd5e16bb13aa25584288eeb288

chore: upgrade yarn.lock

view details

push time in 4 days

PullRequestReviewEvent

delete branch graphile/graphile-engine

delete branch : no-lru-for-interval

delete time in 4 days

push eventgraphile/graphile-engine

Benjie Gillam

commit sha 234d5472374eba12e8bec188a1f181520adc2be3

refactor: no need for LRU for interval (2.2m parsed/s) (#683)

view details

push time in 4 days

PR merged graphile/graphile-engine

refactor: no need for LRU for interval (2.2m parsed/s)

Description

Remove unnecessary LRU; now we've optimised parseInterval there's no need to wrap it in an LRU. I've benchmarked parseInterval at 2.2 million parses per second, so it's unlikely an LRU is going to make a big difference here (and in fact might hurt performance?)

Performance impact

Slightly reduces memory usage.

Security impact

None

+13 -36

0 comment

2 changed files

benjie

pr closed time in 4 days

issue commentgraphql/graphql-spec

Expose user-defined meta-information via introspection API in form of directives

I'd love this feature to exist, and I always imagined it being exposed through SDL as directives no matter what the underlying introspection JSON results are; just like how deprecationReason is the introspection field, but that's exposed as @deprecated(reason: "...").

As for planning to work on it... I can't commit to anything right now (years or months); I've taken on far too many responsibilities and need to see some of those through before I can think about other things.

OlegIlyenko

comment created time in 4 days

PR opened graphile/graphile-engine

refactor: no need for LRU for interval (2.2m parsed/s)

Description

Remove unnecessary LRU; now we've optimised parseInterval there's no need to wrap it in an LRU. I've benchmarked parseInterval at 2.2 million parses per second, so it's unlikely an LRU is going to make a big difference here (and in fact might hurt performance?)

Performance impact

Slightly reduces memory usage.

Security impact

None

+13 -36

0 comment

2 changed files

pr created time in 4 days

create barnchgraphile/graphile-engine

branch : no-lru-for-interval

created branch time in 4 days

push eventbenjie/postgres-interval

dependabot[bot]

commit sha 6daf023ee3883d86a375482daae23ca72290ba3c

Bump standard from 14.3.4 to 15.0.0 (#35) Bumps [standard](https://github.com/standard/standard) from 14.3.4 to 15.0.0. - [Release notes](https://github.com/standard/standard/releases) - [Changelog](https://github.com/standard/standard/blob/master/CHANGELOG.md) - [Commits](https://github.com/standard/standard/compare/v14.3.4...v15.0.0) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

view details

Benjie Gillam

commit sha 40c712d243076e55ebf7ac6d83d500eee8a6b1cb

Refactor to improve performance (#34) * Export parse function * Add benchmark script

view details

push time in 4 days

push eventgraphql/graphql-wg

Mark Larah

commit sha c1dbb9b27158744a7de287496712ab0ad42a9c42

Add mark to November WG meeting (#521)

view details

push time in 4 days

PR merged graphql/graphql-wg

Add mark to November WG meeting

To discuss next steps on Schema Coordinates (https://github.com/graphql/graphql-spec/pull/746)

Thanks!

+2 -0

0 comment

1 changed file

magicmark

pr closed time in 4 days

PullRequestReviewEvent

delete branch benjie/postgres-interval

delete branch : refactor

delete time in 4 days

issue commentgraphile/postgraphile

Fastify plugins integration

Of course I don't mind! Writing a Fastify-v3 specific plugin seems out of scope of PostGraphile's server so it definitely makes sense to keep separately, I'm just thinking your plugin could wrap the new methods I'm adding to PostGraphile so it makes it easier for you to maintain :+1:

madflow

comment created time in 5 days

delete branch graphile/graphile-engine

delete branch : fix-enum-name

delete time in 5 days

push eventgraphile/graphile-engine

Benjie Gillam

commit sha 10b5bc12ab101775c178ea119394703c5bcb8466

chore: revert breaking fake enum name change (#682)

view details

push time in 5 days

PR merged graphile/graphile-engine

chore: revert breaking fake enum name change

Description

Adding _fake_enum to the type name would have been a breaking change; this didn't show up in the tests because we explicitly named all of our enums. Have removed explicit naming from a couple enums and reverted the change.

(The change was only added to make errors from this more obvious, but it's safer to not have this change.)

Performance impact

Negligible

Security impact

None

+21 -22

0 comment

3 changed files

benjie

pr closed time in 5 days

PR opened graphile/graphile-engine

chore: revert breaking fake enum name change

Description

Adding _fake_enum to the type name would have been a breaking change; this didn't show up in the tests because we explicitly named all of our enums. Have removed explicit naming from a couple enums and reverted the change.

(The change was only added to make errors from this more obvious, but it's safer to not have this change.)

Performance impact

Negligible

Security impact

None

+21 -22

0 comment

3 changed files

pr created time in 5 days

create barnchgraphile/graphile-engine

branch : fix-enum-name

created branch time in 5 days

issue closedgraphile/postgraphile

Run makeWrapResolversPlugin on succesful insert and access JWT inside

Summary

First of all, thanks for the awesome work on this project! For a set of web applications I'm working on atm, some users need to be able to register new users. To do so, we need to insert them into the database (which postgraphile handles wonderfully), but also register new users with an external vendor via their REST API. I thought I'd use makeWrapResolversPlugin to write a little function to do so, but encounter some questions which I can't really seem to figure out:

  1. From my understanding, any functions passed via makeWrapResolversPlugin is run before hitting the database. Are there options available to have it run after it does so? Only if the transaction commited successfully?
  2. Could I access a JWT from inside such a function for verification? (Users are added by existing users, who require the right permissions for this).

Thanks in advance!

Additional context

Postgres 13 Node 14.14.0 Postgraphile 14.9.0

closed time in 5 days

MoltenCoffee

issue commentgraphile/postgraphile

Run makeWrapResolversPlugin on succesful insert and access JWT inside

The approach you outline (performing the action after the transaction completes) is very vulnerable to temporary failures. It can mean that the user is successfully created but due to a network error never gets created on the external vendor.

If it's not reasonable to do this as part of the transaction (so that you can roll back on failure), a much better approach would be to use a job queue for this. Create a trigger on the users table that queues a job when a user is inserted, and then have the worker upload to the external vendor API, automatically retrying with exponential backoff in the case of errors.

Check out: https://github.com/graphile/worker


[semi-automated message] Thanks for your question; hopefully we're well on the way to helping you solve your issue. This doesn't currently seem to be a bug in the library so I'm going to close the issue, but please feel free to keep requesting help below and if it does turn out to be a bug we can definitely re-open it 👍

You can also ask for help in the #help-and-support channel in our Discord chat.

MoltenCoffee

comment created time in 5 days

pull request commentgraphile/postgraphile

feat: HTTP server overhaul / restify support

(And thanks so much for taking the time to test this!)

benjie

comment created time in 5 days

pull request commentgraphile/postgraphile

feat: HTTP server overhaul / restify support

Missing line added; thanks! That GraphiQL isn't working means that the originalUrl trickery is not working; and that makes sense because I couldn't track down how to accomplish that in Fastify. I'll see if I can add that to the tests so we can detect whether or not GraphiQL should work. That'd be a neat trick!

benjie

comment created time in 5 days

issue commentgraphile/postgraphile

Fastify plugins integration

The tests now enable compression middleware on express and fastify3 and all the tests still pass; so this specific issue will be closed when that PR is merged :raised_hands:

madflow

comment created time in 6 days

push eventgraphile/postgraphile

Benjie Gillam

commit sha 2c400b3af738269ebfcbc59c8c1fbf2b3b6bc1c4

Add compression middleware to tests

view details

push time in 6 days

issue commentgraphql/graphql-wg

[2020-09-03] GitHub infrastructure for managing Working groups

@Urigo Do you want me to add this for review at next WG, or hold off until the extra things requiring permissions are addressed?

IvanGoncharov

comment created time in 6 days

Pull request review commentgraphile/starter

Windows Support

 require("@app/config/env"); const { execSync, spawnSync: rawSpawnSync } = require("child_process"); const concurrently = require("concurrently"); +// Dear graphile-migrate, please treat the test DB as if it were the shadow DB+process.env.SHADOW_DATABASE_URL = process.env.TEST_DATABASE_URL;++// Signal to graphile-migrate scripts that we're in the tests+process.env.IN_TESTS = "1";+process.env.NODE_ENV = "test";++const cmdArgs = process.argv;

This is fine for now. No need for the extra weight of minimist for a couple flags.

MegaCookie

comment created time in 6 days

PullRequestReviewEvent

Pull request review commentgraphile/starter

Windows Support

 const spawnSync = (cmd, args, options) => {   return result; }; -// Dear graphile-migrate, please treat the test DB as if it were the shadow DB-process.env.SHADOW_DATABASE_URL = process.env.TEST_DATABASE_URL;--// Signal to graphile-migrate scripts that we're in the tests-process.env.IN_TESTS = "1";-process.env.NODE_ENV = "test";+function main() {+  const opts = {+    stdio: "inherit",+    cwd: process.cwd(),+  }; -const opts = {-  stdio: "inherit",-  cwd: process.cwd(),-};--// Reset the test database-execSync("yarn db gm reset --shadow --erase", opts);-execSync("yarn db watch --once --shadow", opts);+  // Reset the test database+  execSync("yarn db gm reset --shadow --erase", opts);+  execSync("yarn db watch --once --shadow", opts); -// If we're in watch mode-const arg = process.argv[2];-if (process.argv.length > 3) {-  throw new Error(-    `Extra arguments not understood '${process.argv.join("' '")}'`-  );-} else if (arg === "--watch" || arg === "--watchAll") {-  // We're in watch mode, so keep watching the `current.yml` file-  concurrently(-    [-      {-        name: "jest",-        command: `npx --no-install -n \"--inspect=9876\" jest -i ${arg}`,-        prefixColor: "greenBright",-      },+  if (watchMode) {+    // We're in watch mode, so keep watching the `current.yml` file+    concurrently(+      [+        {+          name: "jest",+          command: `npx --no-install -n \"--inspect=9876\" jest -i ${watchMode}`,+          prefixColor: "greenBright",+        },+        {+          name: "testdb",+          command: "yarn db watch --shadow",+          prefixColor: "blue",+        },+      ],       {-        name: "testdb",-        command: "yarn db watch --shadow",-        prefixColor: "blue",-      },-    ],-    {-      killOthers: ["failure"],-    }-  );-} else {-  // Run once, so just run the tests-  spawnSync("jest", ["-i", ...process.argv.slice(2)], opts);+        killOthers: ["failure"],+      }+    );+  } else {+    // Run once, so just run the tests+    const argsWithoutDelayArg = process.argv+      .slice(2)
    const argsWithoutDelayArg = cmdArgs
MegaCookie

comment created time in 6 days

Pull request review commentgraphile/starter

Windows Support

 require("@app/config/env"); const { execSync, spawnSync: rawSpawnSync } = require("child_process"); const concurrently = require("concurrently"); +// Dear graphile-migrate, please treat the test DB as if it were the shadow DB+process.env.SHADOW_DATABASE_URL = process.env.TEST_DATABASE_URL;++// Signal to graphile-migrate scripts that we're in the tests+process.env.IN_TESTS = "1";+process.env.NODE_ENV = "test";++const cmdArgs = process.argv;+const watchMode = cmdArgs.find(+  (arg) => arg === "--watch" || arg === "--watchAll"+);+const delayArg = cmdArgs.indexOf("--delay");+let delaySeconds = null;+if (delayArg > 0) {+  delaySeconds = parseInt(cmdArgs[delayArg + 1]);

Actually, let's use parseFloat here so you could do e.g. 0.25 seconds

  delaySeconds = parseFloat(cmdArgs[delayArg + 1]);
MegaCookie

comment created time in 6 days

Pull request review commentgraphile/starter

Windows Support

 require("@app/config/env"); const { execSync, spawnSync: rawSpawnSync } = require("child_process"); const concurrently = require("concurrently"); +// Dear graphile-migrate, please treat the test DB as if it were the shadow DB+process.env.SHADOW_DATABASE_URL = process.env.TEST_DATABASE_URL;++// Signal to graphile-migrate scripts that we're in the tests+process.env.IN_TESTS = "1";+process.env.NODE_ENV = "test";++const cmdArgs = process.argv;+const watchMode = cmdArgs.find(+  (arg) => arg === "--watch" || arg === "--watchAll"+);+const delayArg = cmdArgs.indexOf("--delay");+let delaySeconds = null;+if (delayArg > 0) {+  delaySeconds = parseInt(cmdArgs[delayArg + 1]);+  if (isNaN(delaySeconds)) {+    throw new Error("Did not get a valid delay argument in seconds.");+  }+  setTimeout(main, delaySeconds * 1000);+}

I think we're missing a main call if no delay is set?

} else {
  main()
}
MegaCookie

comment created time in 6 days

Pull request review commentgraphile/starter

Windows Support

 require("@app/config/env"); const { execSync, spawnSync: rawSpawnSync } = require("child_process"); const concurrently = require("concurrently"); +// Dear graphile-migrate, please treat the test DB as if it were the shadow DB+process.env.SHADOW_DATABASE_URL = process.env.TEST_DATABASE_URL;++// Signal to graphile-migrate scripts that we're in the tests+process.env.IN_TESTS = "1";+process.env.NODE_ENV = "test";++const cmdArgs = process.argv;+const watchMode = cmdArgs.find(+  (arg) => arg === "--watch" || arg === "--watchAll"+);+const delayArg = cmdArgs.indexOf("--delay");+let delaySeconds = null;+if (delayArg > 0) {

--delay being the first arg would be fine (note: this wasn't an issue before because argv had 2 elements at the start).

if (delayArg > -1) {
MegaCookie

comment created time in 6 days

Pull request review commentgraphile/starter

Windows Support

 require("@app/config/env"); const { execSync, spawnSync: rawSpawnSync } = require("child_process"); const concurrently = require("concurrently"); +// Dear graphile-migrate, please treat the test DB as if it were the shadow DB+process.env.SHADOW_DATABASE_URL = process.env.TEST_DATABASE_URL;++// Signal to graphile-migrate scripts that we're in the tests+process.env.IN_TESTS = "1";+process.env.NODE_ENV = "test";++const cmdArgs = process.argv;+const watchMode = cmdArgs.find(+  (arg) => arg === "--watch" || arg === "--watchAll"+);+const delayArg = cmdArgs.indexOf("--delay");+let delaySeconds = null;+if (delayArg > 0) {+  delaySeconds = parseInt(cmdArgs[delayArg + 1]);

When using parseInt always pass the radix:

  delaySeconds = parseInt(cmdArgs[delayArg + 1], 10);
MegaCookie

comment created time in 6 days

Pull request review commentgraphile/starter

Windows Support

 require("@app/config/env"); const { execSync, spawnSync: rawSpawnSync } = require("child_process"); const concurrently = require("concurrently"); +// Dear graphile-migrate, please treat the test DB as if it were the shadow DB+process.env.SHADOW_DATABASE_URL = process.env.TEST_DATABASE_URL;++// Signal to graphile-migrate scripts that we're in the tests+process.env.IN_TESTS = "1";+process.env.NODE_ENV = "test";++const cmdArgs = process.argv;

The first two args are node and the script name, so we should skip those

const cmdArgs = process.argv.slice(2);
MegaCookie

comment created time in 6 days

PullRequestReviewEvent
PullRequestReviewEvent

issue commentgraphile/postgraphile

Fastify plugins integration

Interesting; I think a middie-based approach would give users the easiest path - the whole app.use(postgraphile(...)) pattern is well liked!

Not sure if it helps regarding your issue, but I've created a Fastify v3 adaptor that's working with our tests; but setup for it is a little verbose; see this comment: https://github.com/graphile/postgraphile/pull/1361#issuecomment-713696181

I was considering adding a helper function that does all that boilerplate, so you can basically do something like:

const { postgraphile } = require('postgraphile');
const fastify = require('fastify');

const middleware = postgraphile(...);
const app = fastify();
middleware.addToFastify(app);
app.listen(3000);

As you can see from the code in the comment above it's pretty much all boilerplate, so abstracting it should be straightforward. However, doing this wouldn't give quite the same flexibility, especially when it comes to installing route handlers under a subpath, so I'm not really sure if it's the right direction.

madflow

comment created time in 6 days

pull request commentgraphile/postgraphile

feat: HTTP server overhaul / restify support

So it turns out we only had fastify v2 support; and v3 removes the middleware support we were relying on, so I've done fastify v3 support in the same way that restify support will work (using an adaptor much like the one @madflow wrote above).

A fastify v3 PostGraphile server will look something like:

const fastify = require('fastify');
const { postgraphile, PostGraphileResponseFastify3 } = require('postgraphile');

const handler = postgraphile(...);

const app = fastify();

// Natively, Fastify only supports 'application/json' and 'text/plain' content types
// Add application/graphql:
app.addContentTypeParser(
  'application/graphql',
  { parseAs: 'string' },
  (request, payload, done) => done(null, payload),
);
// And application/x-www-data-urlencoded:
app.register(fastifyFormBodyParser);

// Takes a PostGraphile route handler and turns it into a fastify route handler
const convertHandler = handler => (request, reply) =>
  handler(new PostGraphileResponseFastify3(request, reply));

// Wrapping in a function allows you to register these routes under a subpath should you wish
const routes = (router, opts, done) => {
  router.options(handler.graphqlRoute, convertHandler(handler.graphqlRouteHandler));
  router.post(handler.graphqlRoute, convertHandler(handler.graphqlRouteHandler));
  if (handler.graphiqlRouteHandler) {
    router.head(handler.graphiqlRoute, convertHandler(handler.graphiqlRouteHandler));
    router.get(handler.graphiqlRoute, convertHandler(handler.graphiqlRouteHandler));
  }
  if (handler.faviconRouteHandler) {
    router.get('/favicon.ico', convertHandler(handler.faviconRouteHandler));
  }
  if (handler.eventStreamRouteHandler) {
    router.options(handler.eventStreamRoute, convertHandler(handler.eventStreamRouteHandler));
    router.get(handler.eventStreamRoute, convertHandler(handler.eventStreamRouteHandler));
  }
  done();
};

app.register(routes, { prefix: "/api" });

app.listen(3000);

(I wrote this in the GitHub comment box, so let me know if I made mistakes.)

benjie

comment created time in 6 days

push eventgraphile/postgraphile

Benjie Gillam

commit sha 1bf7b77687b1dee2e6d3a6b4aa0ad7d13b3445cd

PostGraphileResponseNode only works with fastify v2

view details

push time in 6 days

push eventgraphile/postgraphile

Benjie Gillam

commit sha 281eb95ada7c228556f2dc7e381334c1b7822a16

Fastify v3 tests (with and without subroute)

view details

Benjie Gillam

commit sha 088e211ab8d3f83fd9db2280dbc9cfcceea5a6ea

Use fastify v3 types

view details

push time in 6 days

issue commentgraphile/postgraphile

Fastify plugins integration

Ah; if middlewares are no longer supported, #1361 also adds a broken down version; I'll have a go at getting that working. Apparently our tests are still on fastify v2! Thanks for the heads up.

madflow

comment created time in 6 days

delete branch graphile/migrate

delete branch : readme-tweak-production

delete time in 6 days

push eventgraphile/migrate

Benjie Gillam

commit sha 2cf20cd6e69c01942ccd60723f8358458315bded

docs(README): clarify production further (#98)

view details

push time in 6 days

PR merged graphile/migrate

docs(README): clarify production further

Description

As suggested by @hegelstad, this slight tweak makes it clearer that most users do not need to concern themselves with other graphile-migrate commands in production.

Performance impact

None

Security impact

None

+2 -2

0 comment

1 changed file

benjie

pr closed time in 6 days

PR opened graphile/migrate

docs(README): clarify production further

Description

As suggested by @hegelstad, this slight tweak makes it clearer that most users do not need to concern themselves with other graphile-migrate commands in production.

Performance impact

None

Security impact

None

+2 -2

0 comment

1 changed file

pr created time in 6 days

create barnchgraphile/migrate

branch : readme-tweak-production

created branch time in 6 days

fork benjie/migrate

Opinionated SQL-powered productive roll-forward migration tool for PostgreSQL.

fork in 6 days

delete branch graphile/graphile-engine

delete branch : fix-unique-tag

delete time in 6 days

push eventgraphile/graphile-engine

Benjie Gillam

commit sha 07475622372a42e1f2105abb50a8a4d8ceb0ba61

fix(tags): fix non-null for multiple @unique tags (#681)

view details

push time in 6 days

PR merged graphile/graphile-engine

fix(tags): fix non-null for multiple @unique tags

Description

Typo meant that if you added more than one @unique tag to a table the fields would be treated as primary keys rather than uniques, which caused nullability to change. Thanks to @felixyz for spotting.

Fixes graphile/postgraphile#1366

Performance impact

Negligible

Security impact

None

+2 -2

0 comment

2 changed files

benjie

pr closed time in 6 days

issue closedgraphile/postgraphile

Two @unique smart comments result in non-nullable references

Summary

I'm using the @unique smart comment to create 1-to-1 relations between views. It works for two views - they both get a direct reference to the other (foo has a bar instead of a barList and likewise in the other direction). However, when I do the very same thing for another view that foo references, namely baz, additional unwanted changes to the schema occur: both barId and bazId become non-optional in foo, and the only root query for foo is by bazId.

Steps to reproduce

See the commits in this repo for a minimal case illustrating the bug:

https://github.com/felixyz/postgraphile-unique-smart-comment-bug

Expected results

Adding one more @unique column constraint to a view that already has one should result in one more 1-to-many relation changing into a 1-to-1 relation. Nothing else should change.

Actual results

A 1-to-1 relation is created, but in addition to that

  • foo.barId and foo.bazId become non-optional (Int!)
    • The root queries for foo disappear
      • foo(id: Int!) : Foo
      • fooByBarId(barId: Int!): Foo
    • The only root query for foo is now foo(bazId: Int!): Foo

Additional context

Schemas generated using

postgraphile --connection postgres://postgraphile:xyz@localhost:5431/my_db --schema gql --watch --export-schema-graphql schema.graphql --port 5123 --enhance-graphiql --append-plugins @graphile-contrib/pg-simplify-inflector --simple-collections only --disable-default-mutations

postgraphile 4.9.1 postgres 12 (official docker image) OS: Pop!_OS 20.04 LTS (Ubuntu-derived)

closed time in 6 days

felixyz

issue commentgraphile/postgraphile

Fastify plugins integration

Our fastify tests are working with basically this:

const { postgraphile } = require('postgraphile');
const fastify = require('fastify');

const middleware = postgraphile(...);
const app = fastify(...);
app.use(middleware);
app.listen(3000);

Would you like to set up a more complex example (e.g. with plugins, other middleware, etc) and see if it works? Instructions on how to install a pre-merged PR here: https://github.com/graphile/postgraphile/pull/1361#issuecomment-707825119 It'd be really good to get some more confirmation.

madflow

comment created time in 6 days

PullRequestReviewEvent

Pull request review commentgraphile/migrate

docs(README): clarify shadow database isn't needed in production

 And please give some love to our featured sponsors 🤩:  ## Setup -`graphile-migrate` uses two databases in development mode. A development database and -a "shadow" database. The "shadow" database is used by `graphile-migrate` to test the -consistency of the migrations. Only a single database is used in production environments.+In development, `graphile-migrate` uses two databases: the main database and a+"shadow" database. The "shadow" database is used internally by+`graphile-migrate` to test the consistency of the migrations and perform various+other tasks.++In production, you'll likely only need to run `graphile-migrate migrate` which

Yeah; I hear you. That said, it's not guaranteed that you'll only want to run migrate - in rare circumstances or setups you might want to run status or other commands. Would saying "most users" instead clarify? It basically means the same, but the semantics may be clearer.

hegelstad

comment created time in 6 days

issue commentgraphile/postgraphile

Two @unique smart comments result in non-nullable references

Hah; such a simple copy/paste error. Fixed in https://github.com/graphile/graphile-engine/pull/681

felixyz

comment created time in 6 days

PR opened graphile/graphile-engine

fix(tags): fix non-null for multiple @unique tags

Description

Typo meant that if you added more than one @unique tag to a table the fields would be treated as primary keys rather than uniques, which caused nullability to change. Thanks to @felixyz for spotting.

Fixes graphile/postgraphile#1366

Performance impact

None

Security impact

None

+2 -2

0 comment

2 changed files

pr created time in 6 days

create barnchgraphile/graphile-engine

branch : fix-unique-tag

created branch time in 6 days

push eventgraphile/postgraphile

Benjie Gillam

commit sha 1bd5cd5497a10364aa664c5f97516d4e87e284e4

Note that PostGraphileResponseNode should handle fastify too

view details

push time in 6 days

pull request commentgraphile/postgraphile

feat: HTTP server overhaul / restify support

@madflow Fastify should work with the built in PostGraphileResponseNode; we have tests that show it working but I'd like some confirmation that it works in a real project that uses fastify plugins/middleware/etc or is basically more complex than our very simple tests. If you've an old project you can resurrect and put it into, that'd be really helpful!

Our test is basically just:

const { postgraphile } = require('postgraphile');
const fastify = require('fastify');

const middleware = postgraphile(...);
const app = fastify(...);
app.use(middleware);
app.listen(3000);
benjie

comment created time in 6 days

issue commentgraphile/postgraphile

Add support for Restify framework

(The other heavy edits are now complete; and #1361 is ready to merge once we've had some :+1:'s from testers.)

PaulMcMillan

comment created time in 6 days

Pull request review commentgraphile/pg-simplify-inflector

Add `omitListSuffix` tag

 function PgSimplifyInflectorPlugin(     );   } -  const connectionSuffix = pgOmitListSuffix ? "-connection" : "";-  const ConnectionSuffix = pgOmitListSuffix ? "Connection" : "";-  const listSuffix = pgOmitListSuffix ? "" : "-list";-  const ListSuffix = pgOmitListSuffix ? "" : "List";+  function omitListSuffix(tag) {+    return tag != null ? tag === "omit" : !!pgOmitListSuffix;

Lets treat @listSuffix (i.e. true) as if it's @listSuffix include. I think that's fairly intuitive. Or throw; I'm happy either way.

keithlayne

comment created time in 6 days

PullRequestReviewEvent

delete branch graphile/graphile-engine

delete branch : fix-1365

delete time in 6 days

push eventgraphile/graphile-engine

Benjie Gillam

commit sha ed75ce23e711d5f1190991faabc52cf610e9c482

fix(types): fix enum tables when used in custom mutations (#680)

view details

push time in 6 days

issue closedgraphile/postgraphile

malformed record literal with table-based enums and functions

Summary

I have a function that accepts a table record type as a parameter. This type has a field that references a table-based enum.

When calling a function passing the table record type parameter with an enum value I am given the error message "malformed record literal"

In the example below, new_thing() fails, but new_thing2() works fine.

Steps to reproduce

I created this simple test case for reproduction.

create table testenum ( enumval text primary key );
insert into testenum (enumval) values ('A');
comment on table testenum is '@enum';

create table things( id text primary key,
  enumval text not null references testenum (enumval)
);

create or replace function new_thing(f_input things) returns text
  language plpgsql
  volatile
as
$$
declare
  v_out text;
begin
  insert into things (id, enumval) values (f_input.id, f_input.enumval) returning id into v_out;
  return v_out;
end;
$$;

create or replace function new_thing2(f_id text, f_enumval text) returns text
  language plpgsql
  volatile
as
$$
declare
  v_out text;
begin
  insert into things (id, enumval) values (f_id, f_enumval) returning id into v_out;
  return v_out;
end;
$$;

Expected results

Expected to see the function called successfully without the error message.

Actual results

malformed record literal is thrown when passing an enum value.

Additional context

  • Postgraphile 4.9.0
  • PostgreSQL 12.4

Possible Solution

Work around is to pass the parameter as text rather than enum.

closed time in 6 days

brendanmckenzie

PR merged graphile/graphile-engine

fix(types): fix enum tables when used in custom mutations

Description

@brendanmckenzie reported graphile/postgraphile#1365 pointing out there's an issue when types that reference enum types are used in custom mutations. This is because we explicitly cast the type of each column, and when we were casting the enum to it's fake name it happened to overlap with the table name, so PostgreSQL thought it was a table reference. Really it should have been a cast to ::text or whatever type the enum column is.

For now I've solved this by casting to ::unknown if the type is fake; but we should solve it better in the long run by having the PgType have a sqlFullyQualifiedTypeName field that we can override for fake types to be the type of the column on the enum (e.g. pg_catalog.text).

Fixes graphile/postgraphile#1365

Performance impact

Negligible.

Security impact

Fixes a bug.

+82 -17

0 comment

9 changed files

benjie

pr closed time in 6 days

PR opened graphile/graphile-engine

fix(types): fix enum tables when used in custom mutations

Description

@brendanmckenzie reported graphile/postgraphile#1365 pointing out there's an issue when types that reference enum types are used in custom mutations. This is because we explicitly cast the type of each column, and when we were casting the enum to it's fake name it happened to overlap with the table name, so PostgreSQL thought it was a table reference. Really it should have been a cast to ::text or whatever type the enum column is.

For now I've solved this by casting to ::unknown if the type is fake; but we should solve it better in the long run by having the PgType have a sqlFullyQualifiedTypeName field that we can override for fake types to be the type of the column on the enum (e.g. pg_catalog.text).

Performance impact

Negligible.

Security impact

Fixes a bug.

+82 -17

0 comment

9 changed files

pr created time in 6 days

more