GithubHelp home page GithubHelp logo

graphile / graphile-engine Goto Github PK

View Code? Open in Web Editor NEW
752.0 752.0 130.0 8.22 MB

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!

Home Page: https://www.graphile.org/

JavaScript 76.02% PLpgSQL 5.60% Shell 0.92% TypeScript 17.46%

graphile-engine's People

Contributors

0ff avatar ab-pm avatar alexppxela avatar benjie avatar bensalilijames avatar bradleyayers avatar commanderroot avatar d3k4r avatar dandv avatar deckstar avatar dependabot[bot] avatar enisdenjo avatar erichosick avatar ganlub avatar garcianavalon avatar hansololai avatar higherorderfunctor avatar jcgsville avatar jcrben avatar jemgillam avatar m0ar avatar mathroc avatar mattbretl avatar michaelbeaumont avatar none23 avatar penx avatar petetnt avatar singingwolfboy avatar speller avatar tsnobip avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphile-engine's Issues

Order by column on related table [plugin available]

I’ve tried to make a plugin to order by column on foreign table of a collection, I took inspiration from PgOrderAllColumnsPlugin.js however I don’t know how to go further, this is what I have:

    const attrs = introspectionResultsByKind.attribute.filter(attr => attr.classId === table.id);

    const res = introspectionResultsByKind.constraint
      .filter(con => con.type === "f")
      .filter(con => con.classId === table.id)
      .map(con => ({con, foreignTable: introspectionResultsByKind.classById[con.foreignClassId]}))
      .map(({con, foreignTable}) => ({
        con,
        foreignTable,
        foreignAttrs: introspectionResultsByKind.attribute
          .filter(attr => attr.classId === foreignTable.id)
          .filter(attr => attr.type.type === "b")
          .filter(attr => attr.type.name !== "uuid")
      })).filter(({foreignAttrs}) => foreignAttrs.length > 0);

    return extend(values, res.reduce((memo, r) => {
      return r.foreignAttrs.reduce((memo, ra) => {
        [true, false].reduce((memo, dir) => {
          const ascFieldName = inflection.orderByEnum(
            [...r.con.keyAttributeNums.map(i => attrs.filter(a => a.num === i)[0].name), ra.name].join("-"),
            dir,
            table.name,
            table.namespaceName
          );
          console.log(table.name, ascFieldName);
          memo[ascFieldName] = {
            value: {
              alias: ascFieldName.toLowerCase(),
              specs: [["???", dir]]
            }
          };
          return memo;
        }, memo)
        return memo;
      }, memo)
    }, {}));

when I inspect the schema, I can see it adds the order bys I want. but I don’t know what to put instead of "???".

for starters I’d be happy if it works only when another part of the graphql query adds the "join" on the related table, but here I can’t yet know the subquery for that related table alias. I can see here that I could put something else than a string but I can’t find example of that and can’t think of any pg-sql expr would be useful in my case

is this possible without patching PgConnectionArgOrderBy.js ?

(related: graphile/crystal#119 (comment))

jsonb fields not working

has something changed with jsonb fields?
i have just moved from:
"postgraphql": "^4.0.0-alpha1.3"
to:
"postgraphile": "^4.0.0-alpha2.20",
and now my json fields don't seem to be going thru to my postgres functions. no errors, just empty

i've isolated as follows:

create a function:

create function myschema.ping_json(
  _the_json jsonb default '[]'
) returns jsonb as $$
declare
begin
  RETURN _the_json;
end;
$$ language plpgsql strict security definer;
--||--
GRANT execute ON FUNCTION soro.ping_json(
  jsonb
) TO myschema_user;

calling this directly works fine:

select myschema.ping_json(
 '[{"amount":"44"},{"amount":null}]'
)

but calling through graphql:

mutation PingJson($theJson: JSON!) {
  pingJson(
    input: {
      _theJson: $theJson
    }
  ) {
    json
  }
}

with variables:

{
 "theJson": "[{\"amount\":\"44\"},{\"amount\":null}]"
}

gives error:

{
  "errors": [
    {
      "message": "malformed array literal: \"{{}}\"",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "pingJson"
      ],
      "stack": "error: malformed array literal: \"{{}}\"\n    at Connection.parseE (/Users/buckfactor/soro/bitbucket/soro-quoting-post-graph-ql/node_modules/pg/lib/connection.js:567:11)\n    at Connection.parseMessage (/Users/buckfactor/soro/bitbucket/soro-quoting-post-graph-ql/node_modules/pg/lib/connection.js:391:17)\n    at Socket.<anonymous> (/Users/buckfactor/soro/bitbucket/soro-quoting-post-graph-ql/node_modules/pg/lib/connection.js:129:22)\n    at emitOne (events.js:115:13)\n    at Socket.emit (events.js:210:7)\n    at addChunk (_stream_readable.js:252:12)\n    at readableAddChunk (_stream_readable.js:239:11)\n    at Socket.Readable.push (_stream_readable.js:197:10)\n    at TCP.onread (net.js:589:20)"
    }
  ],
  "data": {
    "pingJson": null
  }
}

image

mutation wrapper:

const clog = require('fbkt-clog')

module.exports = function CreateQuoteLineItemWrapPlugin (builder) {
  builder.hook(
    "GraphQLObjectType:fields:field",
    (field,
     {pgSql: sql},
     {scope: {isRootMutation, fieldName}, addArgDataGenerator}) => {
      if (!isRootMutation || fieldName !== "pingJson") {
        // If it's not the root mutation, or the mutation isn't the 'createLink'
        // mutation then we don't want to modify it - so return the input object
        // unmodified.
        return field;
      }

      // We're going to need link.id for our `performAnotherTask`; so we're going
      // to abuse addArgDataGenerator to make sure that this field is ALWAYS
      // requested, even if the user doesn't specify it. We're careful to alias
      // the result to a field that begins with `__` as that's forbidden by
      // GraphQL and thus cannot clash with a user's fields.
      // addArgDataGenerator(() => ({
      //   pgQuery: queryBuilder => {
      //     queryBuilder.select(
      //       // Select this value from the result of the INSERT:
      //       sql.query`${queryBuilder.getTableAlias()}.id`,
      //       // And give it this name in the result data:
      //       "__createdRecordId"
      //     );
      //   },
      // }));

      // It's possible that `resolve` isn't specified on a field, so in that case
      // we fall back to a default resolver.
      const defaultResolver = obj => obj[fieldName];

      // Extract the old resolver from `field`
      const {resolve: oldResolve = defaultResolver, ...rest} = field;

      return {
        // Copy over everything except 'resolve'
        ...rest,

        // Add our new resolver which wraps the old resolver
        async resolve(...resolveParams) {
          // Perform some validation (or any other action you want to do before
          // calling the old resolver)
          // const RESOLVE_ARGS_INDEX = 1;
          // const {input: {link: {title}}} = resolveParams[
          //   RESOLVE_ARGS_INDEX
          //   ];
          // if (title.length < 3) {
          //   throw new Error("Title is too short!");
          // }
clog('PING JSON PARAMS', resolveParams[3].variableValues)
          // Call the old resolver (you SHOULD NOT modify the arguments it
          // receives unless you also manipulate the AST it gets passed as the
          // 4th argument; which is quite a lot of effort) and store the result.
          const oldResolveResult = await oldResolve(...resolveParams);

          // Perform any tasks we want to do after the record is created.
          // await performAnotherTask(oldResolveResult.data.__createdRecordId);
          // await handler(oldResolveResult);

          // Finally return the result.
          return oldResolveResult;
        },
      };
    }
  );
};

gives me:
image

and the error is thrown from the resolver

filter orderBy columns

This is mostly a reminder for me to extend support of the pgColumnFilter to the PgOrderAllColumnsPlugin

Because there’s a few columns where it does not make sens to allow sorting (eg: uuid v4, json, maybe even text in some cases)

Loosen up GraphQLNonNull on relations

e.g. it's possible to have a relation that must exist, but where the entry for that relation is not visible to the user. e.g. in BOTS we have Bundle ---< BundleSketch >--- Sketch; but it's possible that a bundle which a sketch belongs to is no longer available and hence not visible (blocked by RLS policy).

This needs fixing before we can release the beta.

Procedures pass null instead of default value for missing arguments

create function foo(a int default 1, b int default 1) returns int as $$
  return b / a;
$$ language plv8 immutable;
{
  foo(b: 22)
}

expected: 22 (because a should default to 1)

actual: 0 (because a is set to null incorrectly; thus 1/22 / null -> Infinity -> 0 ⁉️ ⁉️ )

[graphile-build-pg] Can't return setof composite type from procedure

A procedure can't return a setof some composite type at the moment.

Trying to start postgraphile with this schema:

create schema test;
create type test.some_type as (a int, b int);
create function test.do_something()
  returns setof test.some_type
  as $$ select 1, 2; $$
  language sql stable;

Gives this error:

Error: Do not have a connection type 'SomeTypesConnection' for 'SomeType' so cannot create procedure field

Postgraphile + graphile-build are looking awesome - thank you so much for your amazing work on them! ✨

PgColumnsPlugin overwrites conflicts (should throw)

Here:

https://github.com/graphile/graphile-build/blob/db30a95d80b5eca17b78cdedda63b778b9463c37/packages/graphile-build-pg/src/plugins/PgColumnsPlugin.js#L54-L61

If the inflector generates the same name for two separate fields (e.g. unique_id and uniqueId) then the former will be overwritten by the latter without throwing any errors/warnings. This is at odds with how other parts of the system work - we should throw an error and have the user override the inflector if necessary.

Spotted here:

#101 (comment)

Buffer error at install

Hi there,

I am facing an issue at install process with npm.

npm ERR! Buffer.alloc is not a function

npm version: 5.4.1
node version: 5.4.0
pgsql version: 9.3

Thanks.

Could not find JWT type '"my_private_schema"."jwt_token"'

I'm trying to take care of the TODO on https://github.com/graphile/graphile.github.io/blob/8d5948feaa251303ff2d98294394bd726e6a7189/src/pages/postgraphile/security.md but while going through it I'm getting an error that I think has to do with graphile-build.

First, I'm spinning up a postgres docker container: docker run --name postgres -p 5432:5432 -d postgres
Secondly, I'm running: psql -h localhost -U postgres -w -a -f jwt-example.sql with the contents of the file being:

create schema my_public_schema;
create schema my_private_schema;

create table my_private_schema.person_account (
  person_id     integer primary key,
  email         text not null unique check (email ~* '^.+@.+\..+$'),
  password_hash text not null,
  username text
);

/* added as from what is currently in example */
create type my_private_schema.jwt_token as (
  role text,
  exp integer,
  person_id integer,
  is_admin boolean,
  username varchar
);

/* changed forum_example.person_account to my_private_schema.person_account */
create function my_public_schema.authenticate(
  email text,
  password text
) returns my_private_schema.jwt_token as $$
declare
  account my_private_schema.person_account;
begin
  select a.* into account
    from my_private_schema.person_account as a
    where a.email = authenticate.email;

  if account.password_hash = crypt(password, account.password_hash) then
    return (
      'person_role',
      86400,
      account.person_id,
      account.is_admin,
      account.username
    )::my_private_schema.jwt_token;
  else
    return null;
  end if;
end;
$$ language plpgsql strict security definer;

This all runs fine.
Thirdly, I'm running: postgraphile -e super-secret -c postgres://postgres@localhost:5432/postgres -s my_public_schema -t my_private_schema.jwt_token which is giving me the following output

Error: Could not find JWT type '"my_private_schema"."jwt_token"'
    at PgJWTPlugin.builder.hook (/Users/Sloan/.nvm/versions/node/v8.2.1/lib/node_modules/postgraphile/node_modules/graphile-build-pg/node8plus/plugins/PgJWTPlugin.js:36:13)
    at SchemaBuilder.applyHooks (/Users/Sloan/.nvm/versions/node/v8.2.1/lib/node_modules/postgraphile/node_modules/graphile-build/node8plus/SchemaBuilder.js:149:20)
    at SchemaBuilder.createBuild (/Users/Sloan/.nvm/versions/node/v8.2.1/lib/node_modules/postgraphile/node_modules/graphile-build/node8plus/SchemaBuilder.js:183:10)
    at SchemaBuilder.buildSchema (/Users/Sloan/.nvm/versions/node/v8.2.1/lib/node_modules/postgraphile/node_modules/graphile-build/node8plus/SchemaBuilder.js:189:26)
    at exports.createPostGraphQLSchema (/Users/Sloan/.nvm/versions/node/v8.2.1/lib/node_modules/postgraphile/node_modules/postgraphile-core/node8plus/index.js:80:18)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)

I looked around in the PgJWTPlugin file to try to debug if there was an issue there, not sure if there's something wrong with these lines here: https://github.com/graphile/graphile-build/blob/415acd145992f25db492dab778cec0e7bc34eda8/packages/graphile-build-pg/src/plugins/PgJWTPlugin.js#L36-L45
I couldn't figure out where there pgIntrospectionResultsByKind comes from/what it does so couldn't take my debugging further tonight.

I would super appreciate any direction that you can give! Thanks a ton for your work 👍

Export easy way for plugins to add PG objects to response

e.g. a custom mutation might want to expose a DB object on its payload type; this requires importing queryFromResolveData from graphile-build-pg/node8plus/queryFromResolveData currently plus doing a bunch of boilerplate code - we should simplify this.

e.g.

const queryFromResolveData = require("graphile-build-pg/node8plus/queryFromResolveData");
const debugFactory = require("debug");
const debugSql = debugFactory("graphile-build-pg:sql");

// ...

async resolve(parent, args, context, resolveInfo) {
  // ...custom logic here...

  const {
    pgClient,
  } = context;
  const parsedResolveInfoFragment = parseResolveInfo(resolveInfo);
  const resolveData = getDataFromParsedResolveInfoFragment(
    parsedResolveInfoFragment,
    PayloadType
  );
  const query = queryFromResolveData(
    sql.fragment`app_public.teams`,
    undefined,
    resolveData,
    {},
    builder => {
      builder.where(
        sql.fragment`${builder.getTableAlias()}.uuid = ${sql.value(
          team.uuid
        )}`
      );
    }
  );
  const { text, values } = sql.compile(query);
  if (debugSql.enabled) debugSql(text);
  const { rows: [row] } = await pgClient.query(text, values);
  return {
    team: row,
  };
}

could be packaged up for us; it'd be nice to just pass in the table type, the where clause, the context and resolveInfo and have the rest done for us.

Fix issues with mutations and computed columns

e.g. a computed column that performs a select may query the pre-mutation data rather than the post-mutation data.

Most likely the best way to solve this is to do the mutation and the query in two different SQL statements.

Error: SAVEPOINT graphql_mutation outside transaction blocks

Previously I using this lib as a separate server with cli, then I try to use the schema with my express server, using express-graphql, but got problems with mutation (the query is ok), here the log from postgresql server:

2017-10-23 16:57:41.467 +07 [87004] LOG:  statement: begin
2017-10-23 16:57:41.469 +07 [87004] LOG:  execute <unnamed>: select set_config($1, $2, true)
2017-10-23 16:57:41.469 +07 [87004] DETAIL:  parameters: $1 = 'role', $2 = 'trackit_anonymous'
2017-10-23 16:57:41.471 +07 [87004] LOG:  statement: commit
2017-10-23 16:57:41.502 +07 [87005] LOG:  statement: SAVEPOINT graphql_mutation
2017-10-23 16:57:41.502 +07 [87005] ERROR:  SAVEPOINT can only be used in transaction blocks
2017-10-23 16:57:41.502 +07 [87005] STATEMENT:  SAVEPOINT graphql_mutation
2017-10-23 16:57:41.503 +07 [87005] LOG:  statement: ROLLBACK TO SAVEPOINT graphql_mutation
2017-10-23 16:57:41.503 +07 [87005] ERROR:  ROLLBACK TO SAVEPOINT can only be used in transaction blocks
2017-10-23 16:57:41.503 +07 [87005] STATEMENT:  ROLLBACK TO SAVEPOINT graphql_mutation

And the result return to the next middleware is this:

ROLLBACK TO SAVEPOINT can only be used in transaction blocks

with cryptics stack from TCP

I try to use both withPostGraphQLContext and without it, but nothing helps.

Is this issue start from here?: https://github.com/graphile/graphile-build/blob/ea5a44fe436482795ab6bda20fb57d984bf4b53a/packages/graphile-build-pg/src/plugins/makeProcField.js#L433-L456

View cursors might be non-unique when ordered

Ref #101

When ordered, a cursor is not sufficiently unique:

echo WyJoZWFydGJlYXRfZGVzYyIsWyIyMDE3LTA5LTIwVDE2OjMzOjMyLjQ0NiswMzowMCJdXQ== | base64 -d
["heartbeat_desc",["2017-09-20T16:33:32.446+03:00"]]%

Should probably have row_number() over (...) (where ... is grouping by the orderBy fields) appended to the inner array.

Unable to query array of enum type

Ref: graphile/crystal#527

query {
  myTableById(id: "SOME-UUID-VALUE") {
    id
    name
    myEnums # << Fails here
  }
}
{
  "errors": [
    {
      "message": "Expected Iterable, but did not find one for field MyTable.myEnums.",
      "locations": [
        {
          "line": 5,
          "column": 5
        }
      ],
      "path": [
        "myTableById",
        "name",
        "myEnums"
      ]
    }
  ],
  "data": {
    "myTableById": null
  }
}
CREATE TYPE my_enum_type AS ENUM (
  'Val1',
  'Val2'
);

CREATE TABLE my_table (
  id uuid UNIQUE NOT NULL DEFAULT uuid_generate_v1(),
  name text NOT NULL,
  my_enums my_enum_type[] NOT NULL,
  CONSTRAINT pk_my_table PRIMARY KEY (id)
);

Reported by: @stlbucket

If an SQL error occurs in a mutation, previous data may still be rendered

For example if you perform createUser1, mutationThatProducesError, createUser2; the response may contain { createUser1: { id: 1, name: ... }, mutationThatProducesError: null, createUser2: null}; however the details of user 1 will have been rolled back by the mutation and thus should not be displayed to the user.

I suspect the solution is to use transaction checkpoints, because GraphQL will resolve each mutation field to completion before executing the next mutation field we don't need to worry about errors thrown in later mutations causing issues for resolve values of earlier mutation fields.

Provide mutation example

I'm excited to see support for hooking additional resolvers to postgraphql. I tried the query example it works fine. I can't figure out how the mutation works.. Can you provide an example?

Thanks!

Throw error when pagination with `after`

https://github.com/graphile/graphile-build/blob/db30a95d80b5eca17b78cdedda63b778b9463c37/packages/graphile-build-pg/src/queryFromResolveData.js#L135-L137

With this simple query:

query getMyTasks($taskCondition: TaskCondition!) {
  allTasks(condition:$taskCondition, after:"WyJ0YXNrcyIsIndkTHdiIl0=") {
    totalCount
    nodes {
      name
      nodeId
      id
    }
  }
}

It will throw Error because cursorValue[getPgCursorPrefix().length] is (id) not [(id)].
Example cursorValue at this point is:

['tasks', 'wdLwb']

Simply change to:

const cursor = cursorValue[getPgCursorPrefix().length]
const sqlCursors = (Array.isArray(cursor) ? cursor : [cursor]).map(val =>
  sql.value(val)
);

work though. Have no idea whether I have set thing up wrongly or anything else.

Raise an error if the schema specified does not exist

Or maybe a warning... Depends how well we can achieve it with watch mode for when you're initially building your DB.

Issue was raised in https://discordapp.com/channels/102860784329052160/103315563073269760

Hi, I am trying to use postgraphql but it seems like my schema doesn't get loaded. This is my folder structure:
and via this I try to connect:

app.use(postgraphql(
  `postgres://loginname:${postgresLogin}@localhost:5432/tablename`,
  'postgresdb.sql', //Name of the Schema that should be used
  { graphiql: true }
));

It connects to the database, but when trying to do a query in graphiql, no data seems to be in the tables 😦

V4: Postgres extension `tablefunc` causes error

I love the direction you are taking postgraphql. It's just what I've been waiting for so thanks for taking it on. Below is an issue I've come across using the new beta version.

To reproduce, execute the following SQL in a database you are connecting to with postgraphql:

CREATE extension IF NOT EXISTS tablefunc;

Fire up postgraphql via the CLI tool and you get this error:

* * *

Error: Schema query must be Object Type but got: undefined.
    at invariant (/home/peter/Git/test/node_modules/graphql/jsutils/invariant.js:19:11)
    at new GraphQLSchema (/home/peter/Git/test/node_modules/graphql/type/schema.js:72:88)
    at Object.newWithHooks (/home/peter/Git/test/node_modules/graphql-build/src/makeNewBuild.js:466:14)
    at SchemaBuilder.buildSchema (/home/peter/Git/test/node_modules/graphql-build/src/SchemaBuilder.js:164:36)
    at exports.createPostGraphQLSchema (/home/peter/Git/test/node_modules/postgraphql-build/src/index.js:66:18)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:169:7)

Debugging the issue, it appears the problem is caused by one or more of these functions that are created by the extension:
2017-07-22-201517_428x281_scrot

Specifically the following exception is thrown:

Exception has occurred: Error
Error: Can only create NonNull of a Nullable GraphQLType but got: undefined.
    at invariant (/home/peter/Git/test/node_modules/graphql/jsutils/invariant.js:19:11)
    at new GraphQLNonNull (/home/peter/Git/test/node_modules/graphql/type/definition.js:780:84)
    at makeProcField (/home/peter/Git/test/node_modules/graphql-build-pg/src/plugins/makeProcField.js:121:16)
    at introspectionResultsByKind.procedure.filter.reduce (/home/peter/Git/test/node_modules/graphql-build-pg/src/plugins/PgQueryProceduresPlugin.js:62:31)
    at Array.reduce (native)
    at PgQueryProceduresPlugin.builder.hook (/home/peter/Git/test/node_modules/graphql-build-pg/src/plugins/PgQueryProceduresPlugin.js:19:12)
    at SchemaBuilder.applyHooks (/home/peter/Git/test/node_modules/graphql-build/src/SchemaBuilder.js:115:20)
    at fields (/home/peter/Git/test/node_modules/graphql-build/src/makeNewBuild.js:344:40)
    at resolveThunk (/home/peter/Git/test/node_modules/graphql/type/definition.js:169:40)
    at defineFieldMap (/home/peter/Git/test/node_modules/graphql/type/definition.js:350:18)
    at GraphQLObjectType.getFields (/home/peter/Git/test/node_modules/graphql/type/definition.js:311:44)
    at Object.newWithHooks (/home/peter/Git/test/node_modules/graphql-build/src/makeNewBuild.js:469:16)
    at QueryPlugin.builder.hook (/home/peter/Git/test/node_modules/graphql-build/src/plugins/QueryPlugin.js:18:25)
    at SchemaBuilder.applyHooks (/home/peter/Git/test/node_modules/graphql-build/src/SchemaBuilder.js:115:20)
    at Object.newWithHooks (/home/peter/Git/test/node_modules/graphql-build/src/makeNewBuild.js:146:27)
    at SchemaBuilder.buildSchema (/home/peter/Git/test/node_modules/graphql-build/src/SchemaBuilder.js:164:36)

Removing the extension removes the problem.

I no longer need the extension so don't have an issue myself but logging it in case you are interested.

not null domain column shown as nullable in graphql schema

I have not yet took a deep look at the issue, this is the sql migration that introduced this kind of domains:

-- Deploy beesquad:graphql to pg

BEGIN;

CREATE DOMAIN graphql.url AS CHARACTER VARYING(2048);
CREATE DOMAIN graphql.not_null_url AS graphql.url NOT NULL;
CREATE DOMAIN graphql.keywords AS CHARACTER VARYING[] NOT NULL;

CREATE TYPE graphql.page AS (
	url graphql.not_null_url,
	canonical_url graphql.url,
	keywords graphql.keywords
);

COMMENT ON COLUMN graphql.page.url IS 'The page url';
COMMENT ON COLUMN graphql.page.canonical_url IS 'The page canonical url';
COMMENT ON COLUMN graphql.page.keywords IS 'The page keywords';

ALTER TABLE graphql.discussion ADD COLUMN origin graphql.page NOT NULL;

COMMENT ON COLUMN graphql.discussion.origin IS 'The page where the discussion have been initiated';

COMMIT;

In the schema both the fields origin.url & origin.keywords are shown as nullable

Add support for caching introspection results (and other plugin values)

Should support generic storage for each plugin, the plugins should manage their own version information (e.g. PgIntrospectionPlugin might use a hash of the introspection query, or simply the version number of the plugin, others may factor in the arguments received).

Failure should be hard - e.g. if the options don't match but you're asked to import a cache then it should throw and should be called again without an input cache so that a fresh cache can be generated.

`Error: Can only create NonNull of a Nullable GraphQLType but got: String.`

In postgraphile@^4.0.0-alpha2.24. I am printing what is passed to type at the location of the error:

} : null, keys.reduce((memo, key) => {
  memo[inflection.column(key.name, key.class.name, key.class.namespace.name)] = {
    type: new GraphQLNonNull(pgGetGqlInputTypeByTypeId(key.typeId))
  };
{ key:
   { kind: 'attribute',
     classId: '207155',
     num: 1,
     name: 'id',
     description: null,
     typeId: '1043',
     isNotNull: true,
     hasDefault: false,
     class:
      { kind: 'class',
        id: '207155',
        name: 'address',
        classKind: 'r',
        description: null,
        namespaceId: '2200',
        namespaceName: 'public',
        typeId: '207157',
        isSelectable: true,
        isInsertable: true,
        isUpdatable: true,
        isDeletable: true,
        namespace: [Object],
        type: [Object] },
     type:
      { kind: 'type',
        id: '1043',
        name: 'varchar',
        description: 'varchar(length), non-blank-padded string, variable storage length',
        namespaceId: '11',
        namespaceName: 'pg_catalog',
        type: 'b',
        category: 'S',
        domainIsNotNull: false,
        arrayItemTypeId: null,
        classId: null,
        domainBaseTypeId: null,
        enumVariants: null,
        rangeSubTypeId: null,
        class: undefined,
        domainBaseType: undefined,
        arrayItemType: undefined } } }
An error occurred, it might be okay but it doesn't look like the error we were expecting... run with envvar 'DEBUG="graphile-build:warn"' to view the error
  graphile-build:warn Error: Can only create NonNull of a Nullable GraphQLType but got: String.
  graphile-build:warn     at invariant (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphql/0.11.7/node_modules/graphql/jsutils/invariant.js:18:11)
  graphile-build:warn     at new GraphQLNonNull (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphql/0.11.7/node_modules/graphql/type/definition.js:779:84)
  graphile-build:warn     at newWithHooks.fields.keys.reduce (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build-pg/0.1.0-alpha.35/[email protected][email protected]/node_modules/graphile-build-pg/src/plugins/PgMutationUpdateDeletePlugin.js:421:33)
  graphile-build:warn     at Array.reduce (<anonymous>)
  graphile-build:warn     at uniqueConstraints.forEach.constraint (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build-pg/0.1.0-alpha.35/[email protected][email protected]/node_modules/graphile-build-pg/src/plugins/PgMutationUpdateDeletePlugin.js:412:32)
  graphile-build:warn     at Array.forEach (<anonymous>)
  graphile-build:warn     at introspectionResultsByKind.class.filter.filter.reduce (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build-pg/0.1.0-alpha.35/[email protected][email protected]/node_modules/graphile-build-pg/src/plugins/PgMutationUpdateDeletePlugin.js:367:37)
  graphile-build:warn     at Array.reduce (<anonymous>)
  graphile-build:warn     at extend.reduce (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build-pg/0.1.0-alpha.35/[email protected][email protected]/node_modules/graphile-build-pg/src/plugins/PgMutationUpdateDeletePlugin.js:61:16)
  graphile-build:warn     at Array.reduce (<anonymous>)
  graphile-build:warn     at PgMutationUpdateDeletePlugin.builder.hook (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build-pg/0.1.0-alpha.35/[email protected][email protected]/node_modules/graphile-build-pg/src/plugins/PgMutationUpdateDeletePlugin.js:52:30)
  graphile-build:warn     at SchemaBuilder.applyHooks (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build/0.1.0-alpha.32/[email protected]/node_modules/graphile-build/src/SchemaBuilder.js:289:20)
  graphile-build:warn     at fields (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build/0.1.0-alpha.32/[email protected]/node_modules/graphile-build/src/makeNewBuild.js:440:40)
  graphile-build:warn     at resolveThunk (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphql/0.11.7/node_modules/graphql/type/definition.js:168:40)
  graphile-build:warn     at defineFieldMap (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphql/0.11.7/node_modules/graphql/type/definition.js:349:18)
  graphile-build:warn     at GraphQLObjectType.getFields (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphql/0.11.7/node_modules/graphql/type/definition.js:310:44)
  graphile-build:warn     at Object.newWithHooks (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build/0.1.0-alpha.32/[email protected]/node_modules/graphile-build/src/makeNewBuild.js:579:18)
  graphile-build:warn     at MutationPlugin.builder.hook (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build/0.1.0-alpha.32/[email protected]/node_modules/graphile-build/src/plugins/MutationPlugin.js:8:24)
  graphile-build:warn     at SchemaBuilder.applyHooks (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build/0.1.0-alpha.32/[email protected]/node_modules/graphile-build/src/SchemaBuilder.js:289:20)
  graphile-build:warn     at Object.newWithHooks (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build/0.1.0-alpha.32/[email protected]/node_modules/graphile-build/src/makeNewBuild.js:225:27)
  graphile-build:warn     at SchemaBuilder.buildSchema (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/graphile-build/0.1.0-alpha.32/[email protected]/node_modules/graphile-build/src/SchemaBuilder.js:341:37)
  graphile-build:warn     at Object.exports.createPostGraphQLSchema (xxx/packages/intranav-graphql/node_modules/.registry.npmjs.org/postgraphile-core/0.1.0-alpha.35/[email protected][email protected]/node_modules/postgraphile-core/src/index.js:138:18)
  graphile-build:warn     at <anonymous>
  graphile-build:warn     at process._tickCallback (internal/process/next_tick.js:188:7) +1ms

override keys with PgForwardRelationPlugin

Hi,

I’m trying to override a key with it’s single relation, for that I’ve, overridden the inflector like this:

defaultInflection.singleRelationByKeys = ([{column}]) => column;

but I have this error:

Error: Overwriting key '…' is not allowed!

I’d like to override to avoid having, for example, both owner and accountByOwner and instead just have owner resolve to the Account type.
So my question is, is extend complaining because it can’t override a key? or because it thinks it’s probably an error?

If the error is just to prevent accidental overriding, I could duplicate and adapt PgForwardRelationPlugin but it’ll make it harder to maintain it in the future

maybe I can avoid overriding those fields by not adding them or removing them, but it will probably mean at duplicating some logic form PgForwardRelationPlugin into a new plugin (if I can add a plugin before PgForwardRelationPlugin that would remove those fields) or duplicating the plugin that add the fields and make it more complex and hard to maintain too

do you have suggestion on how to approach that ? would you be open to a PR that adds an option to PgForwardRelationPlugin so that it overrides the keys instead of calling the inflector ?

append mutation hook multiple invocations

function appendMutationPlugin (options) {
  return (builder) => {
    const handler = options.handler

    builder.hook('field',
      (field, {}, {
         scope: {
           isRootMutation,
           fieldName
         }
       }) => {
        if (!isRootMutation || fieldName !== options.mutationName || !field.resolve) {
          return field;
        }
        const oldResolve = field.resolve
        return Object.assign(field, {
          async resolve(...args) {
            const result = await oldResolve.apply(this, args)
            await handler(args, result)
            return result
          }
        });
      });
  }
}
async function handler(args, result) {
  console.log('authenticate')
}

const plugin = appendMutationPlugin({
  mutationName: 'authenticate',
  handler: handler
})

the authenticate function is basically the same as the tutuorial at:
https://github.com/postgraphql/postgraphql/blob/master/examples/forum/TUTORIAL.md#authentication-and-authorization

when executing, mutation hook logs twice to console

Sorting by multiple fields

I'm interested in adding support for sorting Connections by multiple fields. @benjie already provided some great insight here. I started working on this, but realized that I needed to drop the defaultValue assignment in the existing PgConnectionArgOrderBy plugin to get it to work:

https://github.com/graphile/graphile-build/blob/1c53afbbcb862ef4abe55195e00be5d62af62e29/packages/graphile-build-pg/src/plugins/PgConnectionArgOrderBy.js#L102-112

@benjie, would you consider a PR that only applies that default when the orderBy and orderByMany arguments are null/undefined? Also, would you rather see an orderByMany plugin implemented as a PR or maintained separately?

Confusing relationship to `postgraphql`

NOTE: you might actually be looking for PostGraphile (previously 'PostGraphQL') which can be found here; the schema building tools in this repository are used to power PostGraphile among other things.

Unless you want to use the low-level API you probably want to go to the PostGraphile (previously 'PostGraphQL') repository instead: postgraphql/postgraphql

  • The link here points to PostGraphQL, whilst you say previously PostGraphQL.
  • There is no mention of postgraphile on postgraphql/postgraphql.

Then you have graphile as the name of the github org and placeholder library. I found out about postgraphile from someone on reddit saying "this is the successor to postgraphql". But there is not indication of this from postgraph repo.


So it looks like in branch https://github.com/postgraphql/postgraphql/tree/postgraphile the name changes to postgraphile. But still very confusing.


Why not call it graphile while you're at it? I'm guessing there is perhaps the intention to support other databases at some point...and the usage instructions of npm i -g graphile graphile-pg; graphile --driver pg seem more elegant.

EDIT: Seems like this is the plan here!

NUMERIC function parameters: Float! => String!

did numeric fields begin expressing as String! in the schema? today i upgraded from 4.0.0-alpha2.24 to 4.0.0-alpha2.26 so that i could add this library: https://github.com/mattbretl/postgraphile-plugin-connection-filter

at this point my graphQL mutations all now expect String! where previously they expected Float! when my function parameter type is NUMERIC.

unfortunately, if I try to rollback to 2.24, the problem is still there, so it seems that maybe this postgraphile is not the problem.

but maybe i had an old dependence or something?

Example project

Hey @benjie I'm wondering how to use graphile-build / postgraphql together. I'm planing on wrapping postgraphql in a node app, and writing some custom plugins for the validation we talked about over google hangouts but unsure how to tie it all together.

are there any docs currently that illustrate working examples?

Improve error message for field clashes

I had attempted to create a computed field called like my_schema.funding_round_status(funding_round my_schema.funding_round). After restarting the process, I encountered the following error during introspection:

Error: Overwriting key 'status' is not allowed!
    at Object.extend (/usr/src/app/node_modules/graphile-build/node8plus/makeNewBuild.js:141:17)
    at PgComputedColumnsPlugin.builder.hook (/usr/src/app/node_modules/graphile-build-pg/node8plus/plugins/PgComputedColumnsPlugin.js:35:12)
    at SchemaBuilder.applyHooks (/usr/src/app/node_modules/graphile-build/node8plus/SchemaBuilder.js:149:20)
    at fields (/usr/src/app/node_modules/graphile-build/node8plus/makeNewBuild.js:308:40)
    at resolveThunk (/usr/src/app/node_modules/graphql/type/definition.js:169:40)
    at defineFieldMap (/usr/src/app/node_modules/graphql/type/definition.js:350:18)
    at GraphQLObjectType.getFields (/usr/src/app/node_modules/graphql/type/definition.js:311:44)

Use join for single relations

Instead of

select ..., (select json_build_object(...) from relation where ...) alias from source

do

select ..., (json_build_object(...)) alias from source left join relation on (...)

`getTypeByName` does not seem to work anymore

I supposed this is a related to the statement “The internals for types now work slightly differently; if you've written any plugins this may be problematic for you - reach out and I'll help you update to the new syntax (it's not too hard!)” statement on the PostgraphQL V4 thread

I managed to replace its usage with some introspection and pgGetGqlTypeByTypeId but now I don’t know how to solve this part: I am adding a mutation manually and I want to reuse an existing input type but getTypeByName(pgInflection.inputType("page")) returns undefined

however, if I try to manually build a GraphQLInputObjectType like so:

page: {
	type: new GraphQLNonNull(new GraphQLInputObjectType({
		name: pgInflection.inputType("page"),
		fields: {
			page_url: { type: new GraphQLNonNull(GraphQLString) },
			page_canonical_url: { type: GraphQLString },
			page_keywords: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) },
		},
	})),
}

It throws an error:

Error: Schema must contain unique named types but contains multiple types named "PageInput".

So the type does exists but I can’t get it back with getTypeByName. Is it intended or is getTypeByName currently broken ? If it’s intended, should I try to get it with pgGetGqlTypeByTypeId instead ?

graphile-build-pg chokes on enums that contain the empty string as a value

To reproduce:

$ createdb issue-76
$ echo "create type foo as enum('', 'one', 'two');" | psql issue-76
SET
Time: 29.167 ms
CREATE TYPE
Time: 4.223 ms
$ postgraphile --version
4.0.0-alpha2.19
$ postgraphile -c postgres://localhost/issue-76

PostGraphQL server listening on port 5000 🚀

  ‣ Connected to Postgres instance postgres://localhost:5432/issue-76
  ‣ Introspected Postgres schema(s) public
  ‣ GraphQL endpoint served at http://localhost:5000/graphql
  ‣ GraphiQL endpoint served at http://localhost:5000/graphiql

* * *

Error: Must be named. Unexpected name: .
    at assertValidName (/Users/benjiegillam/.nvm/versions/node/v8.4.0/lib/node_modules/postgraphile/node_modules/graphql/utilities/assertValidName.js:32:11)
    at /Users/benjiegillam/.nvm/versions/node/v8.4.0/lib/node_modules/postgraphile/node_modules/graphql/type/definition.js:632:42
    at Array.map (<anonymous>)
    at defineEnumValues (/Users/benjiegillam/.nvm/versions/node/v8.4.0/lib/node_modules/postgraphile/node_modules/graphql/type/definition.js:631:21)
    at new GraphQLEnumType (/Users/benjiegillam/.nvm/versions/node/v8.4.0/lib/node_modules/postgraphile/node_modules/graphql/type/definition.js:549:20)
    at enforceGqlTypeByPgType (/Users/benjiegillam/.nvm/versions/node/v8.4.0/lib/node_modules/postgraphile/node_modules/graphile-build-pg/node8plus/plugins/PgTypesPlugin.js:343:36)
    at Array.forEach (<anonymous>)
    at PgTypesPlugin.builder.hook.build (/Users/benjiegillam/.nvm/versions/node/v8.4.0/lib/node_modules/postgraphile/node_modules/graphile-build-pg/node8plus/plugins/PgTypesPlugin.js:497:37)
    at SchemaBuilder.applyHooks (/Users/benjiegillam/.nvm/versions/node/v8.4.0/lib/node_modules/postgraphile/node_modules/graphile-build/node8plus/SchemaBuilder.js:149:20)
    at SchemaBuilder.createBuild (/Users/benjiegillam/.nvm/versions/node/v8.4.0/lib/node_modules/postgraphile/node_modules/graphile-build/node8plus/SchemaBuilder.js:177:24)

Error: Schema must contain unique named types but contains multiple types named "Profile".

I appologize if this is in the wrong repo. Might be more of a graphile-build-pg ticket...

I get this error for the following Schema, which you can clearly see only contains one Profile type:

# Account types determine feature sets (i.e. feature flags)
enum AccountType {
  FREE
  BASIC
  PROFESSIONAL
  ENTERPRISE
}

# All input for the `authenticate` mutation.
input AuthenticateInput {
  # An arbitrary string value with no semantic meaning. Will be included in the
  # payload verbatim. May be used to track mutations by the client.
  clientMutationId: String
  email: String!
  password: String!
}

# The output of our `authenticate` mutation.
type AuthenticatePayload {
  # The exact same `clientMutationId` that was provided in the mutation input,
  # unchanged and unused. May be used by a client to track mutations.
  clientMutationId: String
  jwt: Jwt

  # Our root query field type. Allows us to run any query from our mutation payload.
  query: Query
}

# All input for the `createAccount` mutation.
input CreateAccountInput {
  # An arbitrary string value with no semantic meaning. Will be included in the
  # payload verbatim. May be used to track mutations by the client.
  clientMutationId: String
  firstName: String!
  lastName: String!
  email: String!
  password: String!
}

# The output of our `createAccount` mutation.
type CreateAccountPayload {
  # The exact same `clientMutationId` that was provided in the mutation input,
  # unchanged and unused. May be used by a client to track mutations.
  clientMutationId: String
  profile: Profile

  # An edge for the type. May be used by Relay 1.
  profileEdge(
    # The method to use when ordering `Profile`.
    orderBy: ProfilesOrderBy = PRIMARY_KEY_ASC
  ): ProfilesEdge

  # Our root query field type. Allows us to run any query from our mutation payload.
  query: Query
}

# All input for the `createProfile` mutation.
input CreateProfileInput {
  # An arbitrary string value with no semantic meaning. Will be included in the
  # payload verbatim. May be used to track mutations by the client.
  clientMutationId: String

  # The `Profile` to be created by this mutation.
  profile: ProfileInput!
}

# The output of our `createProfile` mutation.
type CreateProfilePayload {
  # The exact same `clientMutationId` that was provided in the mutation input,
  # unchanged and unused. May be used by a client to track mutations.
  clientMutationId: String

  # The `Profile` that was created by this mutation.
  profile: Profile

  # An edge for our `Profile`. May be used by Relay 1.
  profileEdge(
    # The method to use when ordering `Profile`.
    orderBy: ProfilesOrderBy = PRIMARY_KEY_ASC
  ): ProfilesEdge

  # Our root query field type. Allows us to run any query from our mutation payload.
  query: Query
}

# A location in a connection that can be used for resuming pagination.
scalar Cursor

# A point in time as described by the [ISO
# 8601](https://en.wikipedia.org/wiki/ISO_8601) standard. May or may not include a timezone.
scalar Datetime

# All input for the `deleteProfileById` mutation.
input DeleteProfileByIdInput {
  # An arbitrary string value with no semantic meaning. Will be included in the
  # payload verbatim. May be used to track mutations by the client.
  clientMutationId: String
  id: Uuid!
}

# All input for the `deleteProfile` mutation.
input DeleteProfileInput {
  # An arbitrary string value with no semantic meaning. Will be included in the
  # payload verbatim. May be used to track mutations by the client.
  clientMutationId: String

  # The globally unique `ID` which will identify a single `Profile` to be deleted.
  nodeId: ID!
}

# The output of our `deleteProfile` mutation.
type DeleteProfilePayload {
  # The exact same `clientMutationId` that was provided in the mutation input,
  # unchanged and unused. May be used by a client to track mutations.
  clientMutationId: String
  profile: Profile
  deletedProfileId: ID

  # Our root query field type. Allows us to run any query from our mutation payload.
  query: Query
}

# A JSON Web Token defined by [RFC 7519](https://tools.ietf.org/html/rfc7519)
# which securely represents claims between two parties.
scalar Jwt

# The root mutation type which contains root level fields which mutate data.
type Mutation {
  # The authentication method takes two paramenters -- an email address and a
  # password -- and returns a JWT if successful or otherwise returns an error
  authenticate(
    # The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
    input: AuthenticateInput!
  ): AuthenticatePayload

  # Registers a single user account with profile and credentials. firstName /
  # lastName are limited to 80 chars.  Password will be hashed and stored.  Email
  # will be checked against a basic regex.
  createAccount(
    # The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
    input: CreateAccountInput!
  ): CreateAccountPayload

  # The first and last name, space separated, of the account profile
  profileFullName(
    # The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
    input: ProfileFullNameInput!
  ): ProfileFullNamePayload

  # Creates a single `Profile`.
  createProfile(
    # The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
    input: CreateProfileInput!
  ): CreateProfilePayload

  # Updates a single `Profile` using its globally unique id and a patch.
  updateProfile(
    # The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
    input: UpdateProfileInput!
  ): UpdateProfilePayload

  # Updates a single `Profile` using a unique key and a patch.
  updateProfileById(
    # The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
    input: UpdateProfileByIdInput!
  ): UpdateProfilePayload

  # Deletes a single `Profile` using its globally unique id.
  deleteProfile(
    # The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
    input: DeleteProfileInput!
  ): DeleteProfilePayload

  # Deletes a single `Profile` using a unique key.
  deleteProfileById(
    # The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
    input: DeleteProfileByIdInput!
  ): DeleteProfilePayload
}

# An object with a globally unique `ID`.
interface Node {
  # A globally unique identifier. Can be used in various places throughout the system to identify this single value.
  nodeId: ID!
}

# Information about pagination in a connection.
type PageInfo {
  # When paginating forwards, are there more items?
  hasNextPage: Boolean!

  # When paginating backwards, are there more items?
  hasPreviousPage: Boolean!

  # When paginating backwards, the cursor to continue.
  startCursor: Cursor

  # When paginating forwards, the cursor to continue.
  endCursor: Cursor
}

# Account profiles contain display information for accounts.  Every account has
# one profile.  Profiles serve as a central reference point for accounts across datatypes.
type Profile implements Node {
  # A globally unique identifier. Can be used in various places throughout the system to identify this single value.
  nodeId: ID!
  id: Uuid!

  # The first name of the user of the account.  This will show up on the site.
  firstName: String!

  # The last name of the account holder.  This will show up on the site.
  lastName: String

  # An account's type is determined by the payment package selected for that
  # account.  Different account types are associated with different sets of feature flags.
  accountType: AccountType!
  createdAt: Datetime!
  updatedAt: Datetime!
  image: String
}

# A condition to be used against `Profile` object types. All fields are tested for equality and combined with a logical ‘and.’
input ProfileCondition {
  # Checks for equality with the object’s `id` field.
  id: Uuid

  # Checks for equality with the object’s `firstName` field.
  firstName: String

  # Checks for equality with the object’s `lastName` field.
  lastName: String

  # Checks for equality with the object’s `accountType` field.
  accountType: AccountType

  # Checks for equality with the object’s `createdAt` field.
  createdAt: Datetime

  # Checks for equality with the object’s `updatedAt` field.
  updatedAt: Datetime

  # Checks for equality with the object’s `image` field.
  image: String
}

# All input for the `profileFullName` mutation.
input ProfileFullNameInput {
  # An arbitrary string value with no semantic meaning. Will be included in the
  # payload verbatim. May be used to track mutations by the client.
  clientMutationId: String
  profile: ProfileInput
}

# The output of our `profileFullName` mutation.
type ProfileFullNamePayload {
  # The exact same `clientMutationId` that was provided in the mutation input,
  # unchanged and unused. May be used by a client to track mutations.
  clientMutationId: String
  string: String

  # Our root query field type. Allows us to run any query from our mutation payload.
  query: Query
}

# Account profiles contain display information for accounts.  Every account has
# one profile.  Profiles serve as a central reference point for accounts across datatypes.
input ProfileInput {
  id: Uuid

  # The first name of the user of the account.  This will show up on the site.
  firstName: String!

  # The last name of the account holder.  This will show up on the site.
  lastName: String

  # An account's type is determined by the payment package selected for that
  # account.  Different account types are associated with different sets of feature flags.
  accountType: AccountType
  createdAt: Datetime
  updatedAt: Datetime
  image: String
}

# Represents an update to a `Profile`. Fields that are set will be updated.
input ProfilePatch {
  id: Uuid

  # The first name of the user of the account.  This will show up on the site.
  firstName: String

  # The last name of the account holder.  This will show up on the site.
  lastName: String

  # An account's type is determined by the payment package selected for that
  # account.  Different account types are associated with different sets of feature flags.
  accountType: AccountType
  createdAt: Datetime
  updatedAt: Datetime
  image: String
}

# A connection to a list of `Profile` values.
type ProfilesConnection {
  # Information to aid in pagination.
  pageInfo: PageInfo!

  # The count of *all* `Profile` you could get from the connection.
  totalCount: Int

  # A list of edges which contains the `Profile` and cursor to aid in pagination.
  edges: [ProfilesEdge]

  # A list of `Profile` objects.
  nodes: [Profile!]
}

# A `Profile` edge in the connection.
type ProfilesEdge {
  # A cursor for use in pagination.
  cursor: Cursor

  # The `Profile` at the end of the edge.
  node: Profile!
}

# Methods to use when ordering `Profile`.
enum ProfilesOrderBy {
  PRIMARY_KEY_ASC
  PRIMARY_KEY_DESC
  NATURAL
  ID_ASC
  ID_DESC
  FIRST_NAME_ASC
  FIRST_NAME_DESC
  LAST_NAME_ASC
  LAST_NAME_DESC
  ACCOUNT_TYPE_ASC
  ACCOUNT_TYPE_DESC
  CREATED_AT_ASC
  CREATED_AT_DESC
  UPDATED_AT_ASC
  UPDATED_AT_DESC
  IMAGE_ASC
  IMAGE_DESC
}

# The root query type which gives access points into the data universe.
type Query implements Node {
  # Fetches an object given its globally unique `ID`.
  node(
    # The globally unique `ID`.
    nodeId: ID!
  ): Node
  currentProfile: Profile

  # Reads and enables paginatation through a set of `Profile`.
  allProfiles(
    # The method to use when ordering `Profile`.
    orderBy: ProfilesOrderBy = PRIMARY_KEY_ASC

    # Read all values in the set before (above) this cursor.
    before: Cursor

    # Read all values in the set after (below) this cursor.
    after: Cursor

    # Only read the first `n` values of the set.
    first: Int

    # Only read the last `n` values of the set.
    last: Int

    # Skip the first `n` values from our `after` cursor, an alternative to cursor
    # based pagination. May not be used with `last`.
    offset: Int

    # A condition to be used in determining which values should be returned by the collection.
    condition: ProfileCondition
  ): ProfilesConnection

  # Reads a single `Profile` using its globally unique `ID`.
  profile(
    # The globally unique `ID` to be used in selecting a single `Profile`.
    nodeId: ID!
  ): Profile
  profileById(id: Uuid!): Profile

  # Exposes the root query type nested one level down. This is helpful for Relay 1
  # which can only query top level fields if they are in a particular form.
  query: Query!

  # The root query type must be a `Node` to work well with Relay 1 mutations. This just resolves to `query`.
  nodeId: ID!
}

# All input for the `updateProfileById` mutation.
input UpdateProfileByIdInput {
  # An arbitrary string value with no semantic meaning. Will be included in the
  # payload verbatim. May be used to track mutations by the client.
  clientMutationId: String
  id: Uuid!

  # An object where the defined keys will be set on the `Profile` identified by our unique key.
  profilePatch: ProfilePatch!
}

# All input for the `updateProfile` mutation.
input UpdateProfileInput {
  # An arbitrary string value with no semantic meaning. Will be included in the
  # payload verbatim. May be used to track mutations by the client.
  clientMutationId: String

  # The globally unique `ID` which will identify a single `Profile` to be updated.
  nodeId: ID!

  # An object where the defined keys will be set on the `Profile` identified by our globally unique `ID`.
  profilePatch: ProfilePatch!
}

# The output of our `updateProfile` mutation.
type UpdateProfilePayload {
  # The exact same `clientMutationId` that was provided in the mutation input,
  # unchanged and unused. May be used by a client to track mutations.
  clientMutationId: String
  profile: Profile

  # Our root query field type. Allows us to run any query from our mutation payload.
  query: Query
}

# A universally unique identifier as defined by [RFC 4122](https://tools.ietf.org/html/rfc4122).
scalar Uuid

needs foreign table info in inflector.manyRelationByKeys

I’d like to know the foreign table name when building the manyRelationByKeys name

use case, column site on table discussion targets a row in table site

on the type Site I’d like to call the field discussions instead of discussionBySite because there is only one column on the key and that this column is named as the foreign key target table

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.