Giter Club home page Giter Club logo

postgraphile-plugin-nested-mutations's Introduction

Package on npm CircleCI

postgraphile-plugin-nested-mutations

This plugin implements nested mutations based on both forward and reverse foreign key relationships in PostGraphile v4. Nested mutations can be of infinite depth.

Getting Started

CLI

postgraphile --append-plugins postgraphile-plugin-nested-mutations

See here for more information about loading plugins with PostGraphile.

Library

const express = require('express');
const { postgraphile } = require('postgraphile');
const PostGraphileNestedMutations = require('postgraphile-plugin-nested-mutations');

const app = express();

app.use(
  postgraphile(pgConfig, schema, {
    appendPlugins: [
      PostGraphileNestedMutations,
    ],
  })
);

app.listen(5000);

Plugin Options

When using PostGraphile as a library, the following plugin options can be passed via graphileBuildOptions:

nestedMutationsSimpleFieldNames

Use simple field names for nested mutations. Instead of names suffixed with tableBy<Key> and tableUsing<Key>, tables with a single foreign key relationship between them will have their nested relation fields named table. Defaults to false.

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    nestedMutationsSimpleFieldNames: true,
  }
});
nestedMutationsDeleteOthers

Controls whether the deleteOthers field is available on nested mutations. Defaults to true.

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    nestedMutationsDeleteOthers: false,
  }
});
nestedMutationsOldUniqueFields

If enabled, plural names for one-to-one relations will be used. For backwards compatibility. Defaults to false.

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    nestedMutationsOldUniqueFields: false,
  }
});

Usage

This plugin creates an additional field on each GraphQL Input type for every forward and reverse foreign key relationship on a table, with the same name as the foreign table.

Each nested mutation field will have the following fields. They will accept an array if the relationship is a one-to-many relationship, or a single input if they are one-to-one.

Connect to Existing Record

connectByNodeId

Connect using a nodeId from the nested table.

connectBy<K>

Connect using any readable primary key or unique constraint on the nested table.

Creating New Records

create

Create a new record in the nested table.

Delete existing Record

deleteByNodeId

Delete using a nodeId from the nested table.

deleteBy<K>

Delete using any readable primary key or unique constraint on the nested table.

Updating Records

updateByNodeId

Update a record using a nodeId from the nested table.

updatedBy<K>

Update a record using any readable primary key or unique constraint on the nested table.

Example

create table parent (
  id serial primary key,
  name text not null
);

create table child (
  id serial primary key,
  parent_id integer,
  name text not null,
  constraint child_parent_fkey foreign key (parent_id)
    references p.parent (id)
);

A nested mutation against this schema, using Parent as the base mutation would look like this:

mutation {
  createParent(input: {
    parent: {
      name: "Parent 1"
      childrenUsingId: {
        connectById: [{
          id: 1
        }]
        create: [{
          name: "Child 1"
        }, {
          name: "Child 2"
        }]
      }
    }
  }) {
    parent {
      id
      name
      childrenByParentId {
        nodes {
          id
          name
        }
      }
    }
  }
}

Or using Child as the base mutation:

mutation {
  createChild(input: {
    child: {
      name: "Child 1"
      parentToParentId: {
        create: {
          name: "Parent of Child 1"
        }
      }
    },
  }) {
    child {
      id
      name
      parentByParentId {
        id
        name
      }
    }
  }
}

Smart Comments

Smart comments are supported for renaming the nested mutation fields.

comment on constraint child_parent_fkey on child is
  E'@fieldName parent\n@foreignFieldName children';

postgraphile-plugin-nested-mutations's People

Contributors

andrisak avatar archlemon avatar dependabot[bot] avatar eldow avatar jchbh-duplicate avatar juliendangers avatar mlipscombe avatar msand avatar munksgaard avatar srp 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

postgraphile-plugin-nested-mutations's Issues

Cannot read property 'keyAttributeNums' of undefined when creating rows on table with inheritance

I have the following schema:

create schema test;

create table test.user (
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL
);

create table test.file (
  id SERIAL PRIMARY KEY,
  filename TEXT NOT NULL
);

create table test.user_file (
  user_id INTEGER NOT NULL REFERENCES test.user(id)
) inherits (test.file);

When running postgraphile with postgraphile-plugin-nested-mutations appended, I get this graphql schema.

I then try to run a query through graphiql:

mutation {
  createUser(input: {user: {name: "Philip", userFilesUsingId: {create: [{filename: "foo.txt"}]}}}) {
    user {
      id
      name
      userFilesByUserId {
        nodes {
          id
          filename
        }
      }
    }
  }
}

But I get the following result:

{
  "errors": [
    {
      "message": "Cannot read property 'keyAttributeNums' of undefined",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "createUser"
      ]
    }
  ],
  "data": {
    "createUser": null
  }
}

Nested mutation not working (connectById)

When I run this mutation, the entity is updated correctly. PropertyId on the QuoteDay entity is set to the specified value.

  updateQuoteDay(
    input: {
      patch: {
        propertyToPropertyId: {
          connectById: { id: "36e59cbe-5900-4b94-a5e7-e0ffcdfcf40d" }
        }
      }
      id: "a8a63915-52b7-4295-92b3-a0cd31ff63df"
    }
  ) {
    clientMutationId
  }

However, if I run this mutation nested, the entity is not updated correctly. PropertyId on the QuoteDay entity remains unchanged.

  updateQuote(
    input: {
      patch: {
        quoteDaysUsingId: {
          updateById: {
            patch: {
              propertyToPropertyId: {
                connectById: { id: "36e59cbe-5900-4b94-a5e7-e0ffcdfcf40d" }
              }
            }
            id: "a8a63915-52b7-4295-92b3-a0cd31ff63df"
          }
        }
      }
      id: "259795ae-af62-4d19-a12e-dc59513af43c"
    }
  ) {
    clientMutationId
  }

Interestingly, if I specify an invalid UUID (with additional characters) in the example that does not work, GraphQL does not complain. If I specify an invalid UUID in the example that does work, GraphQL does complain.

Multiple instances of GraphQLSchema

I am using this module as a library, and I follow the instruction installations, but notice that my Graphiql Explorer is empty, and there is this error in the console:

Error: Cannot use GraphQLSchema "[object GraphQLSchema]" from another module or realm.

Ensure that there is only one instance of "graphql" in the node_modules
directory. If different versions of "graphql" are the dependencies of other
relied on modules, use "resolutions" to ensure only one version is installed.

https://yarnpkg.com/en/docs/selective-version-resolutions

Duplicate "graphql" modules cannot be used at the same time since different
versions may have different capabilities and behavior. The data from one
version used in the function from another could produce confusing and
spurious results.

Any insight on this issue?

How to use nested mutations with makeWrapResolversPlugin in filter matching mode

Hello everyone, this issue is more of a call for help.

I am trying to use this core plugin https://www.graphile.org/postgraphile/make-wrap-resolvers-plugin/ in conjunction with postgraphile nested mutations.

// Example: log before and after each mutation runs
module.exports = makeWrapResolversPlugin(
  context => {
    if (context.scope.isRootMutation) {
      return { scope: context.scope };
    }
    return null;
  },
  ({ scope }) => async (resolver, user, args, context, _resolveInfo) => {
    console.log(`Mutation '${scope.fieldName}' starting with arguments:`, args);
    const result = await resolver();
    console.log(`Mutation '${scope.fieldName}' result:`, result);
    return result;
  }
);

In this example, the plugin adds custom logic (console logs) to every root mutation resolvers. I was wondering if there was a way to add custom logic to, let's say, every mutation creating a user, regardless of whether it's a root mutation or a nested one created through this plugin.

Has anyone tried to achieve the same behavior - or has more knowledge than me about what to look for in the resolver context to filter out the right ones ?

Repo status

Hi @mlipscombe!

Thank you for creating this valuable plugin! Is it fair to say that you do no actively maintain this repo for public usage anymore?

I just tried to use it for our application and I immediately got bit by #15. When I tried to use the proposed workaround and call deleteByX first - the plugin created a syntax error (#31).

It would be nice to know if you are planning on maintaining PR etc. in the future or if I should go fork myself (which is totally fine).

Florian

deleteOthers before inserting

I have two tables foo and bar, with foo having a unique reference to bar. If I update a reference like below, the query fails, and I think it is because the create portion is executed before the deleteOthers portion. Is that really the case, and if so, is that on purpose?

mutation {
  updateFooById(
    input: {
      fooPatch: {
        fooBarUsingId: {
          create: [
            { barId: 15, value: 123412344 }
          ]
          deleteOthers:true
        }
      }
      id: 14
    }
  ) {
    foo {
      id
    }
  }
}

Supporting nested upsert mutations

I have a central entity that has many tags (via a many-many relationship). I would like to be able to easily update the list of tags in a single query. Nested upsert mutations would fix this. I currently have to manually write plpgsql functions to do so.

pgNestedFieldName and 1:1 relationships

Ok, I realise that 1:1 relationships are not an everyday thing (at least not for me), but as I was testing stuff I realised that pgNestedFieldName in this plugin will provide a somewhat erroneous name for a reverse mutation. When we go straight to this point:

return isForward
          ? forwardMutationName || inflection.camelCase(`${tableFieldName}_to_${keyNames.join('_and_')}`)
          : reverseMutationName || inflection.camelCase(`${inflection.pluralize(tableFieldName)}_using_${foreignKeyNames.join('_and_')}`);

when the relationship is 1:1, the reverse will still be using <plural-Field-Name>-using-FK. It's probably more adequate that reverseMutationName follows the same naming convention of forwardMutationName when the relationship is 1:1?

Hopefully I made some sense.

Nested Mutation is not working

@mlipscombe : Nested mutation is not working with PostGraphileNestedMutations.

const options = { 
  watchPg: false,
  graphiql: true, 
  appendPlugins: [
    PostGraphileNestedMutations,    
  ],
  graphileBuildOptions: {
    nestedMutationsSimpleFieldNames: true
  },
  skipPlugins: [NodePlugin]
};

app.use(postgraphile(database, SchemaName, options));
create table t1.discussion
(
	discussion_id       uuid primary key default uuid_generate_v1mc(),	
	trail_order         integer,
	message_text        text,
	message_date        timestamp with time zone not null default now(),	
	status              varchar (5)   not null default 'ACTV',
	status_change_date  timestamp with time zone not null default now()
); 	 	



CREATE TABLE t1.discussion_question
(
    discussion_question_id uuid NOT NULL DEFAULT uuid_generate_v1mc(),
    discussion_id uuid,
    question_result character varying(5) COLLATE pg_catalog."default" NOT NULL,
    question_other_result text COLLATE pg_catalog."default",
    CONSTRAINT discussion_question_pkey PRIMARY KEY (discussion_question_id),
    CONSTRAINT discussion_question_discussion_id_fkey FOREIGN KEY (discussion_id)
        REFERENCES t1.discussion (discussion_id) 
);

Do I need to change any other thing in database for smart comments?
anything else need to be added for plugin options.

deleteBy<key> does not seem to delete related records

I'm not sure if I'm not using it correctly, but I cannot get it to work as I'm expecting.

I have two tables:

create table app.asset (
  id uuid primary key default public.uuid_generate_v4(),
);
create table app.person (
  id uuid primary key default public.uuid_generate_v4(),
  avatar_id uuid references app.asset_id on delete set null
);

Running the following mutation, I'd expect the asset to be deleted. (I'm also trying to set avatarId to null, for debugging the queries.):

mutation {
  updatePersonById(input:{
    id:"49b78602-b633-4f95-afc6-e279fa6de581"
    personPatch:{
      assetToAvatarId:{
        deleteById:{
          id:"1bb2f88c-3969-4400-9274-d1f467aea3bd"
        }
      }
      avatarId:null
    }
  }) {
    clientMutationId
  }
}

This generates the following:

graph_1     | 2019-08-15T11:53:40.519Z postgraphile:postgres begin
graph_1     | 2019-08-15T11:53:40.520Z postgraphile:postgres SAVEPOINT graphql_nested_mutation
graph_1     | 2019-08-15T11:53:40.521Z postgraphile:postgres select "id"
graph_1     | from "app"."asset"
graph_1     | where "id" = $1
graph_1     | 2019-08-15T11:53:40.524Z postgraphile:postgres update "app"."person" set "avatar_id" = $1
graph_1     | where (
graph_1     |   "id" = $2
graph_1     | ) returning *
graph_1     | 2019-08-15T11:53:40.526Z postgraphile:postgres with __local_0__ as (
graph_1     |   select *
graph_1     |   from "app"."person"
graph_1     |   where "id" = $1
graph_1     | )
graph_1     | select (
graph_1     |   (
graph_1     |     case when __local_0__ is null then null else __local_0__ end
graph_1     |   )
graph_1     | )::text
graph_1     | from __local_0__
graph_1     | 2019-08-15T11:53:40.527Z postgraphile:postgres with __local_0__ as (
graph_1     |   select (
graph_1     |     str::"app"."person"
graph_1     |   ).*
graph_1     |   from unnest(
graph_1     |     (
graph_1     |       $1
graph_1     |     )::text[]
graph_1     |   ) str
graph_1     | )
graph_1     | select to_json(__local_0__."id") as "__pk__id"
graph_1     | from __local_0__ as __local_0__
graph_1     | where (TRUE) and (TRUE)
graph_1     | 2019-08-15T11:53:40.529Z postgraphile:postgres RELEASE SAVEPOINT graphql_nested_mutation
graph_1     | 2019-08-15T11:53:40.530Z postgraphile:postgres commit
graph_1     | 0 error(s) in 20.86ms :: mutation { updatePersonById(input: {id: "49b78602-b633-4f95-afc6-e279fa6de581", personPatch: {assetToAvatarId: {deleteById: {id: "1bb2f88c-3969-4400-9274-d1f467aea3bd"}}, avatarId: null}}) { clientMutationId } }

Additionally, if I set the avatar_id to null, and then rerun the above query, it connects the two records and sets the avatar_id to the id in deleteById.

Plugin removes non-null requirement on inputs

I have the following tables:

CREATE TABLE post (
  id SERIAL PRIMARY KEY,
  user_id INTEGER NOT NULL REFERENCES user(id) ON DELETE CASCADE,
  description TEXT NOT NULL
);

CREATE TABLE post_file (
  id SERIAL PRIMARY KEY,
  post_id INTEGER NOT NULL REFERENCES post(id) ON DELETE CASCADE,
  uuid UUID NOT NULL
);

Before adding the postgraphile-plugin-nested-mutations plugin to my postgraphile setup, the user_id was required in the PostInput type, but after adding the plugin the user_id is nullable when creating a new post.

Impossible to connect many-to-many relation in one step

When i create record datum, which has connection to user, i cannot connect it in same mutation where I create it, because nested mutation wants dataId and i dont have that yet, until datum is created.

const req: Request<MutationCreateDatumArgs> = {
		mutation: CreateDatum,
		variables: {
			input:{
			userData: {
				connectByUserIdAndDataId: [
					{
						dataId: null, // I dont have this yet
						userId: 'Hn67I7nMY9TcBnXsH32UOgFToBf1'
					}
				]
			}
		}
	}

nestedMutationsSimpleFieldNames inflection conflict with multiple FKEY relations to same table

Given the table:

CREATE TABLE hsm_public.gage_calibration_record (
  id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
  -- ...
  gage_id uuid NOT NULL REFERENCES hsm_public.gage(id) ON DELETE RESTRICT,
  standard_id uuid REFERENCES hsm_public.gage(id) ON DELETE RESTRICT
);

The following input type is generated:

input CreateGageCalibrationRecordInput {
  id: UUID
  # ...
  gageId: UUID
  standardId: UUID
  gageToGageId: GageCalibrationRecordGageIdFkeyInput
  gageToStandardId: GageCalibrationRecordStandardIdFkeyInput
}

So far, so good. But when I use the nestedMutationsSimpleFieldNames: true option, it simplifies to this:

input CreateGageCalibrationRecordInput {
  id: UUID
  # ...
  gageId: UUID
  standardId: UUID
  gage: GageCalibrationRecordStandardIdFkeyInput
}

This convention steamrolls the second relation to the gage table, and it removes the clarity provided by the column's original name. Perhaps it could use the column name for inflection rather than the foreign table? At the very least, if more than one FKEY points to the same table, it should skip simplification. Since [relation]_id is a pretty common convention, FKEY columns that match that format could simply strip _id from the column name, producing for my example:

input CreateGageCalibrationRecordInput {
  id: UUID
  # ...
  gageId: UUID
  standardId: UUID
  gage: GageCalibrationRecordGageIdFkeyInput
  standard: GageCalibrationRecordStandardIdFkeyInput
}

Delete by multiple primary keys

I found an issue while performing delete mutations from multiple primary keys :

       deleteByCarIdAndBrandId: [
          {
            carId: 514,
            brandId: 43
          }
        ]

Pg compiler outputs an error : Unpexcted character ')'

After some reseach, I looked at the computed query and it looks like this :

delete from "public"."Cars"\n' +
                where \n' +
                    "car_id" = $1\n' +
                  ) and (\n' +
                    "brand_id" = $2\n' +

Fixed it by wrapping the where clause inside parenthesis.

Let me know if I should make a PR for this !

Created relation field is not optional

Hi, first thanks for taking the time to develop this plugin. I'm seeing an issue where I have a 1:many relationship. The problem I'm encountering is that the nested mutation field on the create (and possibly update, didn't test) seems to be required. If I don't include the nested mutation field on the create of the primary entity, the plugin throws errors. If you need me to, I can add the plugin back into the project and provide more details on the error (stack, etc..). Thanks.

Add support for --classic-ids

When using the --classic-ids option which makes id the global identifier instead of nodeId, updateById, deleteById and connectById become unusable because they overlap with the definitions of updateBy<K>, deleteBy<K> and connectBy<K> of primary keys called id.

Postgraphile deals with this issue by renaming those fields rowId, but this plugin doesn't.

It leads the plugin to send two mutations instead of one because the name is found twice, and the one using the id primary key fails.

(the plugin will throw this error)

if (!updatedRow) {
  throw new Error('unmatched update');
}

I think the appropriate naming for mutations using primary keys named id when the --classic-ids option is true should be updateByRowId, deleteByRowId and connectByRowId, following the same conventions as postgraphile.

Here is the log that made me realize what was happening :

{
  isNodeIdUpdater: false,
  fieldName: 'updateById',
  field: SharingPermissionStructureOnSharingPermissionStructureForFkXXXUpdate
} [
  {
    id: 'WyJTaGFyaW5nUGVybWlzc2lvblN0cnVjdHVyZXMiLDU3XQ==',
    patch: [Object: null prototype] { sharingPermissionId: 2 }
  }
]
{
  isNodeIdUpdater: true,
  fieldName: 'updateById',
  field: SharingModuleOnSharingPermissionStructureForYYYFacNodeIdUpdate
} [
  {
    id: 'WyJTaGFyaW5nUGVybWlzc2lvblN0cnVjdHVyZXMiLDU3XQ==',
    patch: [Object: null prototype] { sharingPermissionId: 2 }
  }
]

Those two connector fields share the same fieldName although one is referencing the nodeId and the other one the id primary key, making a single update go through both of them.

I'm unsure how to implement this, any guidelines would be truly appreciated :)

Broken Inverse FK Input types generated when keys based on same column names

Please see repo with test cases in my fork:
jkantr@ec32fa0#diff-8779a91145b9f410e28c1bab4672b961

There are two test cases, the first is passing and is to show that the problem is not related to it being a text key (non serial, no defaults). The second test case shows the failing case - it fails with:

Received value: [[GraphQLError: null value in column "parent_id" violates not-null constraint]]

You may notice in this test we omit the parentId on the parentToParentId: { create } object. You may also notice that this is a pg error, not graphql complaining about the data not matching the type. This is because the type generated by postgraphile-plugin-nested-mutations appears to just be wrong in this case. Notice what happens if we try to add the parentId to the inverse create input:

Received value: [[GraphQLError: Field "parentId" is not defined by type ChildParentFkeyParentCreateInput.]]

And again, notice the working case right above it. The only difference is the FK and PK having the same column identifier.

As a stop gap i guess I could... rename my identifier on one of the two tables? It's pretty inconvenient as it won't match the rest of my schema... but I imagine it should at least work.

I'd submit a PR here but I'd need some help pointing me in the direction of what might be causing this. Thank you!

Delete mutations not visible

The delete mutations aren't available in the Update patch relation, even though there is a top-level delete mutation available for the relation.

Available nested mutations:

connectByCurrencyIdAndCountryId: [CurrencyCountryCurrencyCountryPkeyConnect!]

connectByNodeId: [CurrencyCountryNodeIdConnect!]

updateByCurrencyIdAndCountryId: [CurrencyCountryOnCurrencyCountryForCurrencyCountryCountryIdFkeyUsingCurrencyCountryPkeyUpdate!]

updateByNodeId: [CountryOnCurrencyCountryForCurrencyCountryCountryIdFkeyNodeIdUpdate!]

create: [CurrencyCountryCountryIdFkeyCurrencyCountryCreateInput!]

Available root delete mutation:

Mutation.deleteCurrencyCountry

The table schema:

create table if not exists pivot.currency_country (
  currency_id uuid references public.currency(id) on delete cascade,
  country_id  uuid references public.country(id) on delete cascade,
  primary key (currency_id, country_id)
);

comment on table pivot.currency_country is E'@omit all';

comment on constraint currency_country_currency_id_fkey on pivot.currency_country is E'@fieldName currency\n@foreignFieldName countries';
comment on constraint currency_country_country_id_fkey on pivot.currency_country is E'@fieldName country\n@foreignFieldName currencies';

alter table pivot.currency_country enable row level security;

revoke all on table pivot.currency_country from anonymous, member;
grant select on table pivot.currency_country to anonymous, member;

revoke all on table pivot.currency_country from admin;
grant all on table pivot.currency_country to admin;

drop policy if exists pivot_currency_country_select on pivot.currency_country;
create policy pivot_currency_country_select on pivot.currency_country for select
  using (true);

drop policy if exists pivot_currency_country_all on pivot.currency_country;
create policy pivot_currency_country_all on pivot.currency_country for all
  using (util.is_admin());

The above should give admin users permissions to not only create but also delete relations.

Tables that relate to themselves

I have noticed tables that reference themselves will only work one way with this plugin.

In the example scenario, you can nested insert Child -> Parent OR Parent -> Child

create table app.parent (
  id serial primary key,
  name text not null
);

create table app.child (
  id serial primary key,
  parent_id integer,
  name text not null,
  constraint child_parent_fkey foreign key (parent_id)
    references app.parent (id)
);

But if you make it a single person table it will only let you nested insert Child -> Parent

create table app.person (
  id serial primary key,
  parent_id integer,
  name text not null,
  constraint person_parent_fkey foreign key (parent_id)
    references app.person(id)
);

Error: Can only create NonNull of a Nullable GraphQLType but got: UUID

I'm getting Error: Can only create NonNull of a Nullable GraphQLType but got: UUID, which I guess is because I'm using an UUID, but might also be from having a composite primary key.

Here's a simplified version of the base table everything in my schema inherits from:

create table base (
  entity_id       uuid default uuid_generate_v1mc(),
  valid_from       timestamp with time zone not null default now(),
  valid_until      timestamp with time zone not null default 'infinity',
  primary key (entity_id, valid_until)
);

As well as the full log:

DEBUG="graphile-build:warn" yarn postgraphile --append-plugins postgraphile-plugin-nested-mutations -c 'postgres://localhost:5432/my_app' -s
 my_app -X -p 5001 --export-schema-json ./schema/__generated__/schema.json --expor
t-schema-graphql ./schema/__generated__/schema.gql
yarn run v1.7.0
$ /Path/to/app/node_modules/.bin/postgraphile --append-plugins postgraphile-plugin-nested-mutations -c postgres://localhost:5432/my_app -s my_app -X -p 5001 --export-schema-json ./schema/__generated__/schema.json --export-schema-graphql ./schema/__generated__/schema.gql
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: UUID.
  graphile-build:warn     at invariant (/Path/to/app/node_modules/graphile-build/node_modules/graphql/jsutils/invariant.js:18:11)
  graphile-build:warn     at new GraphQLNonNull (/Path/to/app/node_modules/graphile-build/node_modules/graphql/type/definition.js:779:84)
  graphile-build:warn     at args.keys.reduce (/Path/to/app/node_modules/postgraphile/node_modules/graphile-build-pg/node8plus/plugins/PgRowByUniqueConstraint.js:67:25)
  graphile-build:warn     at Array.reduce (<anonymous>)
  graphile-build:warn     at memo.(anonymous function).fieldWithHooks (/Path/to/app/node_modules/postgraphile/node_modules/graphile-build-pg/node8plus/plugins/PgRowByUniqueConstraint.js:61:26)
  graphile-build:warn     at fieldWithHooks (/Path/to/app/node_modules/graphile-build/node8plus/makeNewBuild.js:372:29)
  graphile-build:warn     at uniqueConstraints.forEach.constraint (/Path/to/app/node_modules/postgraphile/node_modules/graphile-build-pg/node8plus/plugins/PgRowByUniqueConstraint.js:58:29)
  graphile-build:warn     at Array.forEach (<anonymous>)
  graphile-build:warn     at introspectionResultsByKind.class.filter.filter.reduce (/Path/to/app/node_modules/postgraphile/node_modules/graphile-build-pg/node8plus/plugins/PgRowByUniqueConstraint.js:46:27)
  graphile-build:warn     at Array.reduce (<anonymous>) +0ms
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 TypeError: getGqlInputTypeByTypeIdAndModifier is not a function
  graphile-build:warn     at PostGraphileNestedMutationPlugin.builder.hook (/Path/to/app/node_modules/postgraphile-plugin-nested-mutations/src/PostgraphileNestedMutationsPlugin.js:60:28)
  graphile-build:warn     at SchemaBuilder.applyHooks (/Path/to/app/node_modules/graphile-build/node8plus/SchemaBuilder.js:145:20)
  graphile-build:warn     at fieldWithHooks (/Path/to/app/node_modules/graphile-build/node8plus/makeNewBuild.js:374:35)
  graphile-build:warn     at pgIntrospectionResultsByKind.class.filter.filter.filter.reduce (/Path/to/app/node_modules/postgraphile/node_modules/graphile-build-pg/node8plus/plugins/PgMutationCreatePlugin.js:110:25)
  graphile-build:warn     at Array.reduce (<anonymous>)
  graphile-build:warn     at PgMutationCreatePlugin.builder.hook (/Path/to/app/node_modules/postgraphile/node_modules/graphile-build-pg/node8plus/plugins/PgMutationCreatePlugin.js:55:210)
  graphile-build:warn     at SchemaBuilder.applyHooks (/Path/to/app/node_modules/graphile-build/node8plus/SchemaBuilder.js:145:20)
  graphile-build:warn     at fields (/Path/to/app/node_modules/graphile-build/node8plus/makeNewBuild.js:391:40)
  graphile-build:warn     at resolveThunk (/Path/to/app/node_modules/graphile-build/node_modules/graphql/type/definition.js:168:40)
  graphile-build:warn     at defineFieldMap (/Path/to/app/node_modules/graphile-build/node_modules/graphql/type/definition.js:349:18) +4ms
Error: Schema query must be Object Type but got: undefined.
    at invariant (/Path/to/app/node_modules/graphile-build/node_modules/graphql/jsutils/invariant.js:18:11)
    at new GraphQLSchema (/Path/to/app/node_modules/graphile-build/node_modules/graphql/type/schema.js:71:88)
    at Object.newWithHooks (/Path/to/app/node_modules/graphile-build/node8plus/makeNewBuild.js:476:20)
    at SchemaBuilder.buildSchema (/Path/to/app/node_modules/graphile-build/node8plus/SchemaBuilder.js:190:37)
    at Object.exports.createPostGraphileSchema (/Path/to/app/node_modules/postgraphile/node_modules/postgraphile-core/node8plus/index.js:219:26)
    at process._tickCallback (internal/process/next_tick.js:68:7)

"Unable to update/select parent row." Doesn't Provide Enough Information

The error:

Unable to update/select parent row.

(from line 484 of https://github.com/mlipscombe/postgraphile-plugin-nested-mutations/blob/940c6ec412c7d4dcc1e69e5bb0e0be528fe538f1/src/PostgraphileNestedMutationsPlugin.js) doesn't explain what "parent" it's talking about.

It would be helpful if the error could instead say something more like:

Unable to update/select a row from table_b, the parent of table_a.

I would be happy to submit a PR for this if desired (I don't know Postgraphile or Postgraphile plug-in code at all, but this seems like it should be straightforward enough that I don't need to).

Is this library still maintained?

Just asking, as there hasn't been an issue closed since 2021, and there are 6 PRs awaiting approval (including three from the bot that should be no-brainers).

support for @omit?

Is there anyway currently to prevent (omit) a nested mutation from appearing within the schema of certain parent mutations?

deleteBy creates a syntax error

Consider the following relations:

CREATE TABLE a (a_id SERIAL PRIMARY KEY, name TEXT);
CREATE TABLE b (b_id SERIAL PRIMARY KEY, name TEXT);
CREATE TABLE a_b (a_id INT REFERENCES a(a_id), b_id INT REFERENCES b(b_id), PRIMARY KEY (a_id, b_id));

When run through a mutation:

mutation MyMutation {
  createA(
    input: { a: { aBsUsingAId: { deleteByAIdAndBId: { a_id: 10, b_id: 10 } } } }
  ) {
    clientMutationId
  }
}

it yields:

db_1       |                insert into "public"."a"
db_1       |                  default values returning *
db_1       | 2020-01-22 07:45:30.397 UTC [125] ERROR:  syntax error at or near ")" at character 102
db_1       | 2020-01-22 07:45:30.397 UTC [125] STATEMENT:
db_1       |                delete from "public"."a_b"
db_1       |                where
db_1       |                    "a_id" = $1
db_1       |                  ) and (
db_1       |                    "b_id" = $2

This is addressed here #24 I believe. In this issue I tried to provide a test case (since the PR is missing tests.)

Issue updating 3rd nested level

Hello,

I have a problem with a nested mutation but I'm not sure if I'm missing something or if it can't work, this is the schema I have:

CREATE TABLE media (
  uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),
  url text,
  alt text
);

CREATE TABLE media_gallery (
  uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4 ()
);

CREATE TABLE media_gallery_item (
  uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),
  gallery_uuid uuid NOT NULL REFERENCES media_gallery (uuid) ON DELETE CASCADE,
  media_uuid uuid NOT NULL REFERENCES media (uuid) ON DELETE CASCADE,
  title text
);

and this is the body I'm sending:

{
    "input": {
      "patch": {
        "mediaGalleryItems": {
          "updateByUuid": [
            {
              "uuid": "938449fd-7173-42f5-b973-94c48fc61f81",
              "patch": {
                "title": "rewq 123",
                "media": {
                  "updateByUuid": {
                    "uuid": "a5d08fc0-30ea-44a3-a379-20bbb3a703e0",
                    "patch": { "alt": "ciao" }
                  }
                }
              }
            }
          ]
        }
      },
      "uuid": "09248c26-864e-46a0-8e0b-7ab0a69f2e59"
    }
  }

the title rewq 123 work fine, the alt doesn't....am I missing something?

many thanks

nested-mutations plugin adds omitted reference column

when this plugin is enabled, a column that otherwise is omitted from the generated api is still present

to reproduce

create table articles
(
    id  int generated by default as identity primary key
);

create table article_drafts
(
    id     int references articles (id) on delete cascade,
    title  text
);
comment on column article_drafts.id is E'@omit update';

when plugins-nested-mutations is disabled the resulting updateArticleDraft mutation's path type ArticleDraftPatch does not have an id

when it is enabled, ArticleDraftPatch has a field id (but it gets ignored on updates)

expectation

i'd say if the id column is omitted from the update mutation by a smart tag, also this plugin should respect that.

the ArticleDraftPatch should neither have a field id: Int nor articleToId: ArticleDraftsIdFkeyInput

versions used

using version 1.1.0 with postgraphile 4.11.0

Table referencing itself

Hello,

I have a question about a case where a table has a parent_id like this:

CREATE TABLE item (
  uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),
  parent_uuid uuid REFERENCES item (uuid) ON DELETE CASCADE,
  title text
);

is it possible from this to create the parent item and also the child item (wo where parent_uuid is the uuid of the parent item)?

many thanks

Nested mutation to tables with 1:1 relationships allows creation with multiple nested rows.

I have the following two tables:

CREATE TABLE post.post (
  id SERIAL PRIMARY KEY,
  text TEXT NOT NULL
);

CREATE TABLE post.post_image (
  id SERIAL PRIMARY KEY,
  post_id INTEGER UNIQUE NOT NULL REFERENCES post.post(id) ON DELETE CASCADE,
  url TEXT NOT NULL
);

The plugin allows the user to add a list of images to a post instead of just one image, even though it's clearly not allowed by the database.

Here's the resulting graphql schema:
post.txt

Error: Field deleteOthers: Couldn't find type undefined in any of the schemas

Installed latest version [email protected] with yarn and getting Error: Field deleteOthers: Couldn't find type undefined in any of the schemas. error during Postgraphile start.

Any ideas what's the reason and how to fix?

Using ts-node version 8.0.2, typescript version 3.3.3
Error: Field deleteOthers: Couldn't find type undefined in any of the schemas.
    at collectNode (C:\dev\workspace\packages\dmb-server\node_modules\graphql-import\dist\definition.js:101:23)
    at Array.forEach (<anonymous>)
    at collectNewTypeDefinitions (C:\dev\workspace\packages\dmb-server\node_modules\graphql-import\dist\definition.js:50:30)
    at Object.completeDefinitionPool (C:\dev\workspace\packages\dmb-server\node_modules\graphql-import\dist\definition.js:23:41)
    at Object.importSchema (C:\dev\workspace\packages\dmb-server\node_modules\graphql-import\dist\index.js:99:67)
    at Object.<anonymous> (C:\dev\workspace\packages\dmb-server\src\schema.ts:21:69)
    at Module._compile (internal/modules/cjs/loader.js:738:30)
    at Module.m._compile (C:\Users\Alex\AppData\Local\Temp\ts-node-dev-hook-7511520876709679.js:55:25)
    at Module._extensions..js (internal/modules/cjs/loader.js:749:10)
    at require.extensions.(anonymous function) (C:\Users\Alex\AppData\Local\Temp\ts-node-dev-hook-7511520876709679.js:57:14)
[ERROR] 17:00:31 Error: Field deleteOthers: Couldn't find type undefined in any of the schemas.

Missing relationships from tables with multiple foreign-keys to the same related table

Hi there, thanks for making a great plugin for PostGraphile, it's very useful.

I've noticed that when I use the plugin for tables that contain multiple connections to the same related table, only one of the reverse connections appears as a possible input to the mutation. As an example consider the following tables that might be used to represent a job and a relationship between jobs:

create table job (
  id serial primary key,
);

create table job_relationship (
  type text,
  from_job_id integer references job(id),
  to_job_id integer references job(id)
);

In this scenario, the GraphQL type produced for creating a "job" would include the reverse input for the "to_job_id" relationship only; the "from_job_id" input is not available.

Breaks on tables with column-level permissions

This plugin uses * to return all columns on selected/inserted/updated rows. When executed by an unprivileged user who only has permission to certain columns, this of course causes the request to fail as it tries to access inaccessible columns.

If possible, the query should be generated to only return the columns actually requested rather than using *.

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.