@graphile-contrib/pg-simplify-inflector
Advanced tools
Comparing version 2.0.1 to 3.0.0
161
index.js
@@ -11,10 +11,37 @@ /* | ||
module.exports = function PgSimplifyInflectorPlugin(builder, { pgSimpleCollections, pgOmitListSuffix }) { | ||
function fixCapitalisedPlural(fn) { | ||
return function(str) { | ||
const original = fn.call(this, str); | ||
return original.replace(/[0-9]S(?=[A-Z]|$)/g, match => match.toLowerCase()); | ||
}; | ||
} | ||
module.exports = function PgSimplifyInflectorPlugin( | ||
builder, | ||
{ | ||
pgSimpleCollections, | ||
pgOmitListSuffix, | ||
pgSimplifyPatch = true, | ||
pgSimplifyAllRows = true, | ||
} | ||
) { | ||
const hasConnections = pgSimpleCollections !== "only"; | ||
const hasSimpleCollections = pgSimpleCollections === "only" || pgSimpleCollections === "both"; | ||
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."); | ||
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."); | ||
if ( | ||
hasSimpleCollections && | ||
!hasConnections && | ||
pgOmitListSuffix !== true && | ||
pgOmitListSuffix !== false | ||
) { | ||
console.warn( | ||
"You can simplify the inflector further by adding `{graphileBuildOptions: {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." | ||
); | ||
} | ||
@@ -26,16 +53,105 @@ | ||
singleRelationByKeys(detailedKeys, table, _foreignTable, constraint) { | ||
if (constraint.tags.fieldName) { | ||
return constraint.tags.fieldName; | ||
/* | ||
* This solves the issue with `blah-table1s` becoming `blahTable1S` | ||
* (i.e. the capital S at the end) or `table1-connection becoming `Table1SConnection` | ||
*/ | ||
camelCase: fixCapitalisedPlural(inflection.camelCase), | ||
upperCamelCase: fixCapitalisedPlural(inflection.upperCamelCase), | ||
getBaseName(columnName) { | ||
const matches = columnName.match( | ||
/^(.+?)(_row_id|_id|_uuid|_fk|RowId|Id|Uuid|UUID|Fk)$/ | ||
); | ||
if (matches) { | ||
return matches[1]; | ||
} | ||
return null; | ||
}, | ||
/* This is a good method to override. */ | ||
getOppositeBaseName(baseName) { | ||
return ( | ||
{ | ||
/* | ||
* Changes to this list are breaking changes and will require a | ||
* major version update, so we need to group as many together as | ||
* possible! Rather than sending a PR, please look for an open | ||
* issue called something like "Add more opposites" (if there isn't | ||
* one then please open it) and add your suggestions to the GitHub | ||
* comments. | ||
*/ | ||
parent: "child", | ||
child: "parent", | ||
author: "authored", | ||
editor: "edited", | ||
reviewer: "reviewed", | ||
}[baseName] || null | ||
); | ||
}, | ||
getBaseNameFromKeys(detailedKeys) { | ||
if (detailedKeys.length === 1) { | ||
const key = detailedKeys[0]; | ||
const columnName = this._columnName(key); | ||
const matches = columnName.match(/^(.*)(_id|_uuid|Id|Uuid)$/); | ||
if (matches) { | ||
return this.camelCase(matches[1]); | ||
return this.getBaseName(columnName); | ||
} | ||
return null; | ||
}, | ||
...(pgSimplifyPatch | ||
? { | ||
patchField() { | ||
return "patch"; | ||
}, | ||
} | ||
: null), | ||
...(pgSimplifyAllRows | ||
? { | ||
allRows(table) { | ||
return this.camelCase( | ||
this.pluralize(this._singularizedTableName(table)) | ||
); | ||
}, | ||
allRowsSimple(table) { | ||
return this.camelCase( | ||
`${this.pluralize(this._singularizedTableName(table))}-list` | ||
); | ||
}, | ||
} | ||
: null), | ||
singleRelationByKeys(detailedKeys, table, _foreignTable, constraint) { | ||
if (constraint.tags.fieldName) { | ||
return constraint.tags.fieldName; | ||
} | ||
const baseName = this.getBaseNameFromKeys(detailedKeys); | ||
if (baseName) { | ||
return this.camelCase(baseName); | ||
} | ||
return this.camelCase(`${this._singularizedTableName(table)}`); | ||
}, | ||
singleRelationByKeysBackwards( | ||
detailedKeys, | ||
table, | ||
_foreignTable, | ||
constraint | ||
) { | ||
if (constraint.tags.foreignSingleFieldName) { | ||
return constraint.tags.foreignSingleFieldName; | ||
} | ||
if (constraint.tags.foreignFieldName) { | ||
return constraint.tags.foreignFieldName; | ||
} | ||
const baseName = this.getBaseNameFromKeys(detailedKeys); | ||
const oppositeBaseName = baseName && this.getOppositeBaseName(baseName); | ||
if (oppositeBaseName) { | ||
return this.camelCase( | ||
`${oppositeBaseName}-${this._singularizedTableName(table)}` | ||
); | ||
} | ||
return this.camelCase(`${this._singularizedTableName(table)}`); | ||
}, | ||
manyRelationByKeys(detailedKeys, table, _foreignTable, constraint) { | ||
@@ -45,2 +161,11 @@ if (constraint.tags.foreignFieldName) { | ||
} | ||
const baseName = this.getBaseNameFromKeys(detailedKeys); | ||
const oppositeBaseName = baseName && this.getOppositeBaseName(baseName); | ||
if (oppositeBaseName) { | ||
return this.camelCase( | ||
`${oppositeBaseName}-${this.pluralize( | ||
this._singularizedTableName(table) | ||
)}` | ||
); | ||
} | ||
return this.camelCase( | ||
@@ -50,8 +175,14 @@ `${this.pluralize(this._singularizedTableName(table))}` | ||
}, | ||
manyRelationByKeysSimple(detailedKeys, table, _foreignTable, constraint) { | ||
if (constraint.tags.foreignFieldName) { | ||
return constraint.tags.foreignFieldName; | ||
if (constraint.tags.foreignSimpleFieldName) { | ||
return constraint.tags.foreignSimpleFieldName; | ||
} | ||
return this.camelCase( | ||
`${this.pluralize(this._singularizedTableName(table))}` + (pgOmitListSuffix ? '' : '-list') | ||
return ( | ||
this.manyRelationByKeys( | ||
detailedKeys, | ||
table, | ||
_foreignTable, | ||
constraint | ||
) + (pgOmitListSuffix ? "" : "-list") | ||
); | ||
@@ -58,0 +189,0 @@ }, |
{ | ||
"name": "@graphile-contrib/pg-simplify-inflector", | ||
"version": "2.0.1", | ||
"version": "3.0.0", | ||
"description": "Simplifies the graphile-build-pg inflector to trim the `ByFooIdAndBarId` from relations", | ||
@@ -31,3 +31,6 @@ "main": "index.js", | ||
"index.js" | ||
] | ||
], | ||
"devDependencies": { | ||
"prettier": "^1.14.3" | ||
} | ||
} |
115
README.md
# @graphile-contrib/pg-simplify-inflector | ||
Simplifies relation names; e.g. `postsByAuthorId` becomes simply `posts`. | ||
This plugin simplifies relation names in the PostGraphile schema; e.g. | ||
`User.postsByAuthorId` becomes simply `User.posts`, and `Post.userByAuthorId` | ||
becomes simply `Post.author`. | ||
_Adding this plugin to your schema is almost certainly a breaking change, so do | ||
it before you ship anything!_ | ||
This is recommended for most PostGraphile users, but it does require | ||
familiarity with smart comments to override certain potential naming conflicts | ||
(e.g. `Company.beveragesByManufacturerId` and | ||
`Company.beveragesByDistributorId` both want to become simply | ||
`Company.beverages` which would cause a conflict). | ||
## Customising | ||
This plugin is implemented as a single JS file that does not need to be | ||
compiled at all - you can simply copy it into your project and customise it as | ||
you see fit. | ||
Alternatively, you can [write your own inflection | ||
plugin](https://www.graphile.org/postgraphile/inflection/). | ||
## Changes: | ||
Given these tables: | ||
```sql | ||
create table companies ( | ||
id serial primary key, | ||
name text not null | ||
); | ||
create table beverages ( | ||
id serial primary key, | ||
name text not null, | ||
manufacturer_id int references companies, | ||
distributor_id int references companies | ||
); | ||
comment on constraint "beverages_distributor_id_fkey" on "beverages" is | ||
E'@foreignFieldName distributedBeverages'; | ||
``` | ||
- `Beverage.companyByManufacturerId` and `Beverage.companyByDistributorId` | ||
become `Beverage.manufacturer` and `Beverage.distributor` respectively | ||
- `Company.beveragesByManufacturerId` and `Company.beveragesByDistributorId` | ||
would both become `Company.beverages` and cause an error (but we have a smart | ||
comment above to rename the latter to `Company.distributedBeverages` to avoid | ||
the issue) | ||
- `Query.allCompanies` and `Query.allBeverages` become `Query.companies` and | ||
`Query.beverages` respectively (disable via `pgSimplifyAllRows = false`) | ||
- All update mutations now accept `patch` instead of `companyPatch` / | ||
`beveragePatch` (disable via `pgSimplifyPatch = false`) | ||
- If you are using `pgSimpleCollections = "only"` then you can set | ||
`pgOmitListSuffix = true` to omit the `List` suffix | ||
## Installation: | ||
@@ -28,4 +80,3 @@ | ||
```js | ||
const PgSimplifyInflectorPlugin = | ||
require('@graphile-contrib/pg-simplify-inflector'); | ||
const PgSimplifyInflectorPlugin = require("@graphile-contrib/pg-simplify-inflector"); | ||
@@ -36,5 +87,3 @@ // ... | ||
postgraphile(process.env.AUTH_DATABASE_URL, "app_public", { | ||
appendPlugins: [ | ||
PgSimplifyInflectorPlugin, | ||
], | ||
appendPlugins: [PgSimplifyInflectorPlugin], | ||
// ... other settings ... | ||
@@ -45,6 +94,54 @@ }) | ||
## Naming your foreign key fields | ||
By naming your foreign key along the lines of `author_id` or `author_fk`, e.g.: | ||
```sql | ||
CREATE TABLE posts ( | ||
id serial primary key, | ||
author_id int not null references users, | ||
... | ||
); | ||
``` | ||
We can automatically extract the field prefix: `author` and call the relation | ||
`author` rather than the default: `user`. This allows for a post to have an | ||
`author`, `editor`, `reviewer`, etc. all which point to `users`. | ||
The reverse, however, is not so easy - on the User type, we can't call the reverse | ||
of all these relations `posts`. The default inflector refers to these as | ||
`postsByAuthorId`, `postsByEditorId`, etc. but this plugin exists to simplify | ||
these relations. | ||
To this end, we introduce a new inflector: `getOppositeBaseName`. This | ||
inflector is passed a baseName (the part without the `_id`/`_fk` suffix, e.g. | ||
`author`, `editor`, `reviewer` above) and should return the opposite of that | ||
base name which will be prepended to the target type to produce, e.g. | ||
`authoredPosts`, `editedPosts`, `reviewedPosts`. Failing this, we'll just call | ||
the relation 'posts' and it will be up to you to add smart comments to handle | ||
the field conflicts. | ||
## Handling field conflicts: | ||
In most cases, the conflict errors will guide you on how to fix these issues: | ||
``` | ||
⚠️⚠️⚠️ An error occured when building the schema on watch: | ||
Error: A naming conflict has occurred - two entities have tried to define the same key 'beverages'. | ||
The first entity was: | ||
Backward relation (connection) for constraint "beverages_manufacturer_id_fkey" on table "a"."beverages". To rename this relation with smart comments: | ||
COMMENT ON CONSTRAINT "beverages_manufacturer_id_fkey" ON "a"."beverages" IS E'@foreignFieldName newNameHere'; | ||
The second entity was: | ||
Backward relation (connection) for constraint "beverages_distributor_id_fkey" on table "a"."beverages". To rename this relation with smart comments: | ||
COMMENT ON CONSTRAINT "beverages_distributor_id_fkey" ON "a"."beverages" IS E'@foreignFieldName newNameHere'; | ||
``` | ||
If you have two relations that will result in a conflict (e.g. | ||
`postsByAuthorId` and `postsByEditorId` would both become `posts` with this | ||
`postsByFooId` and `postsByBarId` would both become `posts` with this | ||
plugin) then you will need to rename one of them - you can do so using [smart | ||
@@ -54,4 +151,4 @@ comments](https://www.graphile.org/postgraphile/smart-comments/), e.g.: | ||
```sql | ||
comment on constraint posts_editor_id_fkey on posts is | ||
E'@foreignFieldName editedPosts\n@fieldName editor'; | ||
comment on constraint posts_foo_id_fkey on posts is | ||
E'@foreignFieldName fooPosts\n@fieldName editor'; | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
12426
174
151
1