graphile / pg-simplify-inflector Goto Github PK
View Code? Open in Web Editor NEWSimplifies the graphile-build-pg inflector to trim the `ByFooIdAndBarId` from relations
License: MIT License
Simplifies the graphile-build-pg inflector to trim the `ByFooIdAndBarId` from relations
License: MIT License
Is there a minimum type definition that can be added to allow this to easily work with the rest of postgraphile via TypeScript?
e.g. media
pluralises to the same value, resulting in PostGraphile wishing to register two root media
fields - one for an individual media, the other for selecting all media.
Solution: if the plural is equal to the singular then:
ch
, s
, sh
, x
, z
then add es
: bench
-> benches
y
, change to ies
: baby
-> babies
s
: media
-> medias
(ugh)Make all the mutation result fields simply called result
I'm trying to use both this plugin and postgraphile-plugin-connection-filter but unfortunately
it doesn't work.
Using postgraphile --append-plugins postgraphile-plugin-connection-filter
I get the expected functionality via i.e. allUsers(filter: ...)
. Using postgraphile --append-plugins @graphile-contrib/pg-simplify-inflector
I get the expected functionality via i.e. users
. But using both plugins yields only users
without any access to the filter option.
I've looked through the issues here as well as on the other package, but I haven't found a similar issue.
I'm posting this here since I assume it's more likely the issue lies here (since the package is renaming things) rather than the opposite.
PS. I'm using 4.0.0-alpha.0 of the inflector plugin, 1.0.0-rc.1 of the filter plugin and 4.3.3 of PostGraphile itself.
https://github.com/graphile-contrib/pg-simplify-inflector#handling-field-conflicts
@benjie instead of having conflicting fields why not just create a union type that can then be extracted using an inline fragment?
version 3.0.0
Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "cmFields-list" does not.
e.g. like @jgzuke did in graphile/graphile-engine#471
Since they're the primary keys, listing them out seems kind of unnecessary. If people use database identifiers more than global node IDs (especially if they've disabled the node plugin!) then just having myTable
be the default single fetcher, and updateMyTable
update it makes sense.
i.e.:
user
-> userByNodeId
userById
-> user
updateUser
-> updateUserByNodeId
updateUserById
-> updateUser
Maybe make it an option.
Maybe change compound primary keys from userByOrganizationAndIdentifier
to userByPk
or similar.
I got error/stacktrace from postgraphile when trying to install pg-simplify-injector in the terminal. I trued both npm install --save
and yarn add
and the CLI would not start after either:
$ postgraphile -c postgres://user:pass@localhost:5432/db --append-plugins @graphile-contrib/pg-simplify-inflector --schema forum_example Failed to load plugin '@graphile-contrib/pg-simplify-inflector' /usr/local/lib/nodejs/node-v10.15.3-linux-x64/lib/node_modules/postgraphile/build/postgraphile/cli.js:270 throw e; ^ Error: Cannot find module '@graphile-contrib/pg-simplify-inflector'
I am not inside a Node project or anything, just trying to use it as a CLI.
After re-running the npm command with -g it worked:
npm install -g @graphile-contrib/pg-simplify-inflector
The documentation then is slightly wrong here (it omits the -g flag).
See the code snippet
Given this table design:
CREATE TABLE animal (
id SERIAL PRIMARY KEY
);
CREATE TABLE cat (
id integer PRIMARY KEY REFERENCES animal
);
An animal
query should expose cat
, a cat
query should expose animal
An animal
query exposes catById
, a cat
query exposes animalById
module.exports = function PgSimplifyInflectorPlugin(
builder,
{ pgSimpleCollections, pgOmitListSuffix, pgVirtualTables }
) {
const hasConnections = pgSimpleCollections !== 'only';
const hasSimpleCollections =
pgSimpleCollections === 'only' || pgSimpleCollections === 'both';
if (hasConnections && hasSimpleCollections && pgOmitListSuffix) {
throw new Error(
'Cannot omit -list suffix (`pgOmitListSuffix`) if both relay connections and simple collections are enabled.'
);
}
if (
hasSimpleCollections &&
!hasConnections &&
pgOmitListSuffix !== true &&
pgOmitListSuffix !== false
) {
console.warn(
'You can simplify the inflector further by adding `{graphileOptions: {pgOmitListSuffix: true}}` to the options passed to PostGraphile, however be aware that doing so will mean that later enabling relay connections will be a breaking change. To dismiss this message, set `pgOmitListSuffix` to false instead.'
);
}
builder.hook('inflection', inflection => {
return {
...inflection,
patchField() {
return this.camelCase('patch');
},
tableNode(table) {
return this.camelCase(
`${this._singularizedTableName(table)}-by-node-id`
);
},
allRows(table) {
return this.camelCase(
this.pluralize(this._singularizedTableName(table))
);
},
allRowsSimple(table) {
return this.camelCase(
`${this.pluralize(this._singularizedTableName(table))}-list`
);
},
singleRelationByKeys(detailedKeys, table, _foreignTable, constraint) {
if (constraint.tags.fieldName) {
return constraint.tags.fieldName;
} else if (detailedKeys.length === 1) {
return this.column(detailedKeys[0]).replace(/id$/i, '');
}
return this.camelCase(this._singularizedTableName(table));
},
singleRelationByKeysBackwards(
_detailedKeys,
table,
_foreignTable,
constraint
) {
if (constraint.tags.foreignSingleFieldName) {
return constraint.tags.foreignSingleFieldName;
} else if (constraint.tags.foreignFieldName) {
return constraint.tags.foreignFieldName;
} else if (constraint.tags.fieldName) {
return constraint.tags.fieldName;
}
return this.camelCase(this._singularizedTableName(table));
},
manyRelationByKeys(detailedKeys, table, foreignTable, constraint) {
const singularForeignTableName = this._singularizedTableName(
foreignTable
);
if (constraint.tags.foreignFieldName) {
return constraint.tags.foreignFieldName;
} else if (detailedKeys.length === 1) {
const first = this.column(detailedKeys[0]).replace(/id$/i, '');
let second = this._singularizedTableName(table);
if (second.startsWith(singularForeignTableName) && pgVirtualTables) {
second = second.replace(`${singularForeignTableName}_`, '');
}
return this.camelCase(
first !== singularForeignTableName
? [first, this.pluralize(second)].join('-')
: this.pluralize(second)
);
}
return this.camelCase(
`${this.pluralize(
this._singularizedTableName(table)
)}-by-${detailedKeys
.map(this.column.bind(this))
.filter(key => `${singularForeignTableName}Id` !== key)
.join('-and-')}`
);
},
rowByUniqueKeys(detailedKeys, table, constraint) {
if (constraint.tags.fieldName) {
return constraint.tags.fieldName;
} else if (
detailedKeys.length === 1 &&
this.column(detailedKeys[0]) === 'id'
) {
return this.camelCase(this._singularizedTableName(table));
}
return this.camelCase(
`${this._singularizedTableName(table)}-by-${detailedKeys
.map(key => this.column(key))
.join('-and-')}`
);
},
updateByKeys(detailedKeys, table, constraint) {
if (constraint.tags.updateFieldName) {
return constraint.tags.updateFieldName;
}
if (
detailedKeys.length === 1 &&
this.column(detailedKeys[0]) === 'id'
) {
return this.camelCase(`update-${this._singularizedTableName(table)}`);
}
return this.camelCase(
`update-${this._singularizedTableName(table)}-by-${detailedKeys
.map(key => this.column(key))
.join('-and-')}`
);
},
deleteByKeys(detailedKeys, table, constraint) {
if (constraint.tags.deleteFieldName) {
return constraint.tags.deleteFieldName;
} else if (
detailedKeys.length === 1 &&
this.column(detailedKeys[0]) === 'id'
) {
return this.camelCase(`delete-${this._singularizedTableName(table)}`);
}
return this.camelCase(
`delete-${this._singularizedTableName(table)}-by-${detailedKeys
.map(key => this.column(key))
.join('-and-')}`
);
},
updateByKeysInputType(detailedKeys, table, constraint) {
if (constraint.tags.updateFieldName) {
return this.upperCamelCase(
`${constraint.tags.updateFieldName}-input`
);
} else if (
detailedKeys.length === 1 &&
this.column(detailedKeys[0]) === 'id'
) {
return this.camelCase(
`update-${this._singularizedTableName(table)}-input`
);
}
return this.upperCamelCase(
`update-${this._singularizedTableName(table)}-by-${detailedKeys
.map(key => this.column(key))
.join('-and-')}-input`
);
},
deleteByKeysInputType(detailedKeys, table, constraint) {
if (constraint.tags.deleteFieldName) {
return this.upperCamelCase(
`${constraint.tags.deleteFieldName}-input`
);
} else if (
detailedKeys.length === 1 &&
this.column(detailedKeys[0]) === 'id'
) {
return this.camelCase(
`delete-${this._singularizedTableName(table)}-input`
);
}
return this.upperCamelCase(
`delete-${this._singularizedTableName(table)}-by-${detailedKeys
.map(key => this.column(key))
.join('-and-')}-input`
);
},
updateNode(table) {
return this.camelCase(
`update-${this._singularizedTableName(table)}-by-node-id`
);
},
deleteNode(table) {
return this.camelCase(
`delete-${this._singularizedTableName(table)}-by-node-id`
);
},
updateNodeInputType(table) {
return this.upperCamelCase(
`update-${this._singularizedTableName(table)}-by-node-id-input`
);
},
deleteNodeInputType(table) {
return this.upperCamelCase(
`delete-${this._singularizedTableName(table)}-by-node-id-input`
);
},
edgeField(table) {
return this.camelCase(`${this._singularizedTableName(table)}-edge`);
},
scalarFunctionConnection(proc) {
return this.upperCamelCase(`${this._functionName(proc)}-connection`);
},
scalarFunctionEdge(proc) {
return this.upperCamelCase(
`${this.singularize(this._functionName(proc))}-edge`
);
},
createField(table) {
return this.camelCase(`create-${this._singularizedTableName(table)}`);
},
createInputType(table) {
return this.upperCamelCase(
`create-${this._singularizedTableName(table)}-input`
);
},
createPayloadType(table) {
return this.upperCamelCase(
`create-${this._singularizedTableName(table)}-payload`
);
},
updatePayloadType(table) {
return this.upperCamelCase(
`update-${this._singularizedTableName(table)}-payload`
);
},
deletePayloadType(table) {
return this.upperCamelCase(
`delete-${this._singularizedTableName(table)}-payload`
);
}
};
});
};
Updating from ^4.0.0-alpha.0 to ^5.0.0-beta.1 is causing a lot of issues for me. Even after adding pgShortPk: false, there are several interface differences that I wasn't expecting.
The docs don't really help much, and the changelog stops at v2.
I get that neither version was released per-se, but it would be nice if there was somewhere where I could find a clear list of breaking changes.
Hi! I'm learning to love postgraphile!
Having some trouble installing this library:
$ yarn add @graphile-contrib/pg-simplify-inflector
...
$ npx postgraphile --append-plugins @graphile-contrib/pg-simplify-inflector
npx: installed 119 in 4.56s
Failed to load plugin '@graphile-contrib/pg-simplify-inflector'
Cannot find module '@graphile-contrib/pg-simplify-inflector'
Thank you so much!
G
There seems to be an incompatibility when using pg-simplify-inflector
with the simple-collections
flag.
postgraphile --append-plugins @graphile-contrib/pg-simplify-inflector --simple-collections both
and
postgraphile --append-plugins @graphile-contrib/pg-simplify-inflector --simple-collections only
yield the following error:
Error: Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "authoredPosts-list" does not.
Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "posts-list" does not.
at assertValidSchema (~/pg-test/node_modules/graphql/type/validate.js:71:11)
at Object.validate (~/pg-test/node_modules/graphql/validation/validate.js:55:35)
at parseQuery (~/pg-test/node_modules/postgraphile/build/postgraphile/http/createPostGraphileHttpRequestHandler.js:231:48)
at Promise.all.paramsList.map (~/pg-test/node_modules/postgraphile/build/postgraphile/http/createPostGraphileHttpRequestHandler.js:483:63)
at Array.map (<anonymous>)
at requestHandler (~/pg-test/node_modules/postgraphile/build/postgraphile/http/createPostGraphileHttpRequestHandler.js:450:52)
at process._tickCallback (internal/process/next_tick.js:68:7)
--simple-collections omit
seems to work fine with the plugin.
Now that Postgraphile is properly typed, it complains about passing undeclared options… I guess the following should be added to index.d.ts
:
declare module 'graphile-build' {
interface GraphileBuildOptions {
/**
* Set to true if you want simple collections to lose the 'List' suffix
* (and connections to gain a 'Connection' suffix).
*/
pgOmitListSuffix: boolean;
/**
* Set to false if you want 'userPatch' instead of 'patch' in update
* mutations.
*/
pgSimplifyPatch: boolean;
/**
* Set to false if you want 'allUsers' instead of 'users' at root level.
*/
pgSimplifyAllRows: boolean;
/**
* Set to true if you want primary key queries and mutations to have
* `ById` (or similar) suffix; and the `nodeId` queries/mutations
* to lose their `ByNodeId` suffix.
*/
pgShortPk: boolean;
}
}
I'm not exactly sure about the comments though, "set to false" is weird. Is there a @default
annotation or something?
Compiler errors (with a workaround) importing module from an ESM module. This issue affects both 6.x and 7.x alpha.
Create new ESM module and add this code to it
import PgSimplifyInflectorPlugin from '@graphile-contrib/pg-simplify-inflector';
import { createPostGraphileSchema } from 'postgraphile';
async function generateGraphQLSchema() {
await createPostGraphileSchema('', 'app_public_v2', {
appendPlugins: [PgSimplifyInflectorPlugin],
});
}
Then run
npx tsc --noemit
No compiler error
Compiler error
test.ts:8:19 - error TS2322: Type 'typeof import("/Users/iris/Software/folx/test/b/node_modules/@graphile-contrib/pg-simplify-inflector/index")' is not assignable to type 'Plugin'.
Type 'typeof import("/Users/iris/Software/folx/test/b/node_modules/@graphile-contrib/pg-simplify-inflector/index")' provides no match for the signature '(builder: SchemaBuilder, options: Options): void | Promise<void>'.
8 appendPlugins: [PgSimplifyInflectorPlugin],
~~~~~~~~~~~~~~~~~~~~~~~~~
Found 1 error in test.ts:8
This is due to package declaration problems outlined in
Either add export =
or switch this module from CJS to ESM in package.json
I'm submitting a ...
PostGraphile version:
4.3.3
Minimal SQL file that can be loaded into a clean database:
CREATE TABLE "user" (
id serial PRIMARY KEY
);
CREATE TABLE photo (
id serial PRIMARY KEY,
url TEXT
);
CREATE TABLE user_photo (
user_id integer references "user"(id),
photo_id integer references "photo"(id),
PRIMARY KEY (user_id, photo_id)
);
Steps to reproduce:
Current behavior:
Use PgSimplifyInflectorPlugin
in conjunction with simpleCollections: 'only'
without graphileBuildOptions: { pgOmitListSuffix: true }
will throw error:
Error: Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "userPhotos-list" does not.
Expected behavior:
It shouldn't throw error.
I'm submitting a ...
PostGraphile version: ^4.5.5
pg-simplify-inflector version: ^5.0.0-beta.1
Hi, so this is a question regarding the naming of fields based on foreign keys once the foreign key column is renamed.
I have these two tables:
create table something(
something_id integer not null primary key,
name text
);
create table something_data(
something_data_id integer not null primary key,
something_id integer not null references something on delete cascade,
data text
)
This creates the following schemas:
something(somethingId: 10) {
somethingId
somethingDataConnection {
totalCount
}
}
somethingDatum(somethingDataId: 10) {
data
something {
somethingId
}
}
, where somethingDataConnection and something are fields based on the foreign key on the column something_data.something_id.
But let's say I want to rename something.something_id to just something.some_id.
comment on column something.something_id is E'@name some_id';
This only changes that field:
something(someId: 10) {
someId
somethingDataConnection {
totalCount
}
}
somethingDatum(somethingDataId: 10) {
data
something {
someId
}
}
But now for consistency I also want to rename something_data.something_id:
comment on column something_data.something_id is E'@name some_id';
and this is the result:
something(someId: 10) {
someId
somethingDataBySomeIdConnection {
totalCount
}
}
somethingDatum(somethingDataId: 10) {
data
somethingBySomeId {
someId
}
}
So it seems that after renaming the foreign key column pg-simplify-inflector does not recognize it as such anymore, I think I can fix the first schema by doing:
comment on constraint something_data_something_id_fkey on something_data is E'@foreignFieldName somethingData';
But the second one (somethingBySomeId) would still be broken and I would prefer a way to fix this from the root since renaming is a thing I do a lot because the database has a differente naming convention than the client api.
Current behavior: After renaming foreing key column the suffix By[Field]Id is added again.
Expected behavior: The suffix should not be added.
I just spent a ton of time trying to work out how to do exactly what this smart tag does. I'm super glad that the functionality exists, but it was difficult to discover. I found it by reading the source (should have done that sooner) but that's not ideal.
Maybe a section on smart tags in the readme would be helpful. I'll try to put together a PR when I have time.
in additional to using smart comment on the constraint
comment on constraint "beverages_distributor_id_fkey" on "beverages" is
E'@foreignFieldName distributedBeverages';
can we also support smart comment on the field level?
comment on column beverages.distributor_id is '@foreignFieldName distributedBeverages';
I'm receiving incompatibility/warning messages when installing this plugin into a Docker container:
graphql/Dockerfile
FROM graphile/postgraphile:latest
RUN yarn add @graphile-contrib/pg-simplify-inflector
docker-compose.yml
version: "3.8"
services:
db:
build:
context: ./db
...
graphql:
build:
context: ./graphql
restart: unless-stopped
depends_on:
- db
...
Running docker-compose up -d graphql
I got:
Building graphql
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM graphile/postgraphile:latest
---> ee716b46ca89
Step 2/2 : RUN yarn add @graphile-contrib/pg-simplify-inflector
---> Running in 44503e6a05aa
yarn add v1.22.5
[1/5] Validating package.json...
[2/5] Resolving packages...
[3/5] Fetching packages...
info [email protected]: The platform "linux" is incompatible with this module.
info "[email protected]" is an optional dependency and failed compatibility check. Excluding it from installation.
info [email protected]: The platform "linux" is incompatible with this module.
info "[email protected]" is an optional dependency and failed compatibility check. Excluding it from installation.
[4/5] Linking dependencies...
warning " > [email protected]" has unmet peer dependency "graphile-build@^4.5.0".
warning " > [email protected]" has unmet peer dependency "graphile-build-pg@^4.5.0".
warning "postgraphile-core > graphile-build-pg > [email protected]" has incorrect peer dependency "graphql@^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0-b || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0".
[5/5] Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ @graphile-contrib/[email protected]
info All dependencies
└─ @graphile-contrib/[email protected]
Done in 37.24s.
Removing intermediate container 44503e6a05aa
---> 202c693d7e8d
Successfully built 202c693d7e8d
Successfully tagged cotizayape_postgraphile:latest
WARNING: Image for service graphql was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
cyp-db is up-to-date
Creating cyp-graphql ... done
No incompatibility/warning messages.
Several incompatibility/warning messages.
Host info:
We've deployed our GraphQL schema to production (though only for internal use) before enabling this plugin. It would be useful to have a graceful migration strategy, and I see two options:
both
/ only
option, and use both
to expose the old names and the new names. Deploy this to production, migrate all our consume code to use the new APIs, deploy that, and wait a week for old code to evaporate from production systems. Then switch the plugin to only
and deploy that./graphql2
, that uses the same postgraphile configuration as /graphql
but has this plugin installed. Then migrate all consumer clients to use /graphql2
, deploy that, wait a week for legacy production code to evaporate, then remove /graphql
.I'm leaning towards the second option, since it's something we can do without depending on changes to this plugin.
Thoughts?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.