Giter Club home page Giter Club logo

pg-simplify-inflector's Introduction

@graphile-contrib/pg-simplify-inflector

This plugin simplifies field names in the PostGraphile schema; e.g. allUsers becomes simply users, 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 the primary reason this isn't enabled by default in PostGraphile.

This plugin is recommended for all PostGraphile users.

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.

Changes:

Given these tables:

create table companies (
  id serial primary key,
  name text not null
);
create table beverages (
  id serial primary key,
  company_id int not null references companies,
  distributor_id int references companies,
  name text not null
);
  • Query.allCompanies πŸ‘‰ Query.companies (disable via pgSimplifyAllRows = false)
  • Query.allBeverages πŸ‘‰ Query.beverages
  • Beverage.companyByCompanyId πŸ‘‰ Beverage.company
  • Beverage.companyByDistributorId πŸ‘‰ Beverage.distributor
  • Company.beveragesByCompanyId πŸ‘‰ Company.beverages (because the company_id column follows the [table_name]_id naming convention)
  • 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
  • Fields where the singular and plural are the same and a distinct plural is required are force-pluralised ("fishes") to avoid conflicts (e.g. singularize("fish") === pluralize("fish")).

Note: Company.beveragesByDistributorId will remain, because distributor_id does not follow the [table_name]_id naming convention, but you could rename this yourself with a smart comment:

comment on constraint "beverages_distributor_id_fkey" on "beverages" is
  E'@foreignFieldName distributedBeverages';

or with a custom inflector:

module.exports = makeAddInflectorsPlugin(
  {
    getOppositeBaseName(baseName) {
      return (
        {
          // These are the default opposites
          parent: "child",
          child: "parent",
          author: "authored",
          editor: "edited",
          reviewer: "reviewed",

          // πŸ‘‡ Add/customise this line:
          distributor: "distributed",
        }[baseName] || null
      );
    },
  },
  true
);

Installation:

yarn add @graphile-contrib/pg-simplify-inflector

or

npm install --save @graphile-contrib/pg-simplify-inflector

Usage:

CLI:

postgraphile --append-plugins @graphile-contrib/pg-simplify-inflector

Library:

const PgSimplifyInflectorPlugin = require("@graphile-contrib/pg-simplify-inflector");

// ...

app.use(
  postgraphile(process.env.AUTH_DATABASE_URL, "app_public", {
    appendPlugins: [PgSimplifyInflectorPlugin],

    // Optional customisation
    graphileBuildOptions: {
      /*
       * Uncomment if you want simple collections to lose the 'List' suffix
       * (and connections to gain a 'Connection' suffix).
       */
      //pgOmitListSuffix: true,
      /*
       * Uncomment if you want 'userPatch' instead of 'patch' in update
       * mutations.
       */
      //pgSimplifyPatch: false,
      /*
       * Uncomment if you want 'allUsers' instead of 'users' at root level.
       */
      //pgSimplifyAllRows: false,
      /*
       * Uncomment 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: true,
    },
    // ... other settings ...
  })
);

Naming your foreign key fields

By naming your foreign key along the lines of author_id or author_fk, e.g.:

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 different relations posts. The default inflector refers to these as postsByAuthorId, postsByEditorId, etc. However we'd rather use shorter names, so 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 just fall back to the default (verbose) inflector; it will be up to you to add smart comments or a custom inflector to override these.

Handling field conflicts:

In most cases, the conflict errors will guide you on how to fix these issues using smart comments.

Smart Tags

@foreignSimpleFieldName

@foreignSimpleFieldName was added to override the naming of the foreign-side of a one-to-many relationship's simple collections field (if you're using simple collections). By default we'll take the @foreignFieldName and add the "list suffix" ("List" by default, but "" if pgOmitListSuffix is set), but if you prefer you can override it entirely with @foreignSimpleFieldName. If you set @foreignSimpleFieldName and you're using simpleCollections 'both' then you should also set @foreignFieldName explicitly or unexpected things may occur.

Applies to:

  • foreign key constraints

@listSuffix

@listSuffix allows you to override the default naming on a per-entity basis, overriding pgOmitListSuffix. For example, with pgOmitListSuffix: true, you can apply @listSuffix include to have the -List suffix appended to the simple collection generated for that table, and remove the -Connection suffix from the Relay connection. When pgOmitListSuffix is not true, you can use @listSuffix omit to selectively omit the -List suffix on simple collections and append -Connection to the Relay connection instead.

If @listSuffix is set, the only valid values are "omit" and "include". Any other value will cause an error.

@listSuffix omit @listSuffix include
Relay Connection companiesConnection companies
Simple Collection companies companiesList

NOTE: @listSuffix will have no effect when using @foreignSimpleFieldName.

Applies to:

  • tables
  • foreign key constraints
  • computed column functions returning SETOF <record type>

pg-simplify-inflector's People

Contributors

benjie avatar coobaha avatar dependabot[bot] avatar enisdenjo avatar hfjallemark avatar hgl avatar jgzuke avatar keithlayne avatar leobakerhytch avatar lifeiscontent avatar romanhotsiy avatar vincenzo 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

Watchers

 avatar  avatar  avatar

pg-simplify-inflector's Issues

Consider making primary key fetchers/updaters shorter

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.

[v5] `ById` suffix being added even when `pgShortPk` is true

Summary

The plugin is adding ById to queries even when pgShortPk is set to true. Setting it to false doesn't seem to have any effect.

I've also noticed that we have what are essentially duplicate mutations, but I'm not sure that's related to this plugin. Both mutations below use the ID of the user:

updateUserById(input: UpdateUserByIdInput!): UpdateUserPayload
updateUser(input: UpdateUserInput!): UpdateUserPayload

Steps to reproduce

Below is my graphile.config.js file:

import { PostGraphileAmberPreset } from 'postgraphile/presets/amber';
import { makePgService } from 'postgraphile/adaptors/pg';
import { PostGraphileConnectionFilterPreset } from 'postgraphile-plugin-connection-filter';
import { PgSimplifyInflectionPreset } from '@graphile/simplify-inflection';
import { PgOmitArchivedPlugin } from '@graphile-contrib/pg-omit-archived';
import { PgManyToManyPreset } from '@graphile-contrib/pg-many-to-many';

const IS_DEV = process.env.NODE_ENV === 'dev';

/** @var {GraphileConfig.Preset} preset */
const preset = {
    extends: [
        PostGraphileAmberPreset,
        PostGraphileConnectionFilterPreset,
        PgSimplifyInflectionPreset,
        PgManyToManyPreset,
    ],
    disablePlugins: [
        'QueryQueryPlugin',
        'RegisterQueryNodePlugin',
        'PgRowByUniquePlugin',
        'PgMutationPayloadEdgePlugin',
    ],
    plugins: [
        PgOmitArchivedPlugin,
    ],
    pgServices: [
        makePgService({
            connectionString: process.env.DATABASE_URL,
            schemas: ['public'],
        }),
    ],
    gather: {
        installWatchFixtures: IS_DEV,
        pgJwtTypes: 'public.jwt',
    },
    schema: {
        dontSwallowErrors: IS_DEV,
        pgForbidSetofFunctionsToReturnNull: true,
        pgJwtSecret: process.env.JWT_SECRET,
        exportSchemaSDLPath: IS_DEV ? `${process.cwd()}/schema.graphql` : void 0,
        defaultBehavior: '+list',

        pgOmitListSuffix: true,
        pgShortPk: true,
    },
    grafast: {
        explain: IS_DEV,
    },
    grafserv: {
        watch: IS_DEV,
        graphiql: IS_DEV,
        graphiqlOnGraphQLGET: IS_DEV,
    },
};

export default preset;

Expected results

Assuming a single public users table:

user(id: ID!): User
users(...): [User!]
usersConnection(...): UserConnection

Actual results

userById(id: ID!): User
users(...): [User!]
usersConnection(...): UserConnection

Additional context

Node: 21-alpine3.18
Postgres: 15

"dependencies": {
  "@dataplan/json": "0.0.1-beta.22",
  "@dataplan/pg": "0.0.1-beta.24",
  "@graphile-contrib/pg-many-to-many": "2.0.0-beta.3",
  "@graphile-contrib/pg-omit-archived": "v4.0.0-beta.3",
  "@graphile/simplify-inflection": "8.0.0-beta.5",
  "express": "^4.19.2",
  "graphile-build": "5.0.0-beta.23",
  "graphile-build-pg": "5.0.0-beta.27",
  "graphile-worker": "0.16.6",
  "postgraphile": "5.0.0-beta.28",
  "postgraphile-plugin-connection-filter": "v3.0.0-beta.3"
}

Possible Solution

Not a solution, but it might have something to do with me disabling PgRowByUniquePlugin. If I re-enable it the schema looks like:

user(userId: ID!): User
userById(id: ID!): User
users(...): [User!]
usersConnection(...): UserConnection

But it could be that all it's doing is adding the user(userId) query.

Using alongside postgraphile-plugin-connection-filter

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.

Field name simplification broken after renaming with Smart Tag

I'm submitting a ...

  • bug report
  • feature request
  • question

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.

Using pgSimpleCollections: 'only' without pgOmitListSuffix: true will throw error

I'm submitting a ...

  • bug report
  • feature request
  • question

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.

Incompatible with --simple-collections (both|only)

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.

Supporting "both" / "only" option for graceful migration

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:

  • Add support to the plugin for a 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.
  • Deploy a second GraphQL API to /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?

Handle issues with plural === singular

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:

  • If it ends in ch, s, sh, x, z then add es: bench -> benches
  • If it ends in y, change to ies: baby -> babies
  • Otherwise, add s: media -> medias (ugh)

Import problems from ESM to this module

Summary

Compiler errors (with a workaround) importing module from an ESM module. This issue affects both 6.x and 7.x alpha.

Steps to reproduce

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

Expected results

No compiler error

Actual results

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

Additional context

This is due to package declaration problems outlined in

Possible Solution

Either add export = or switch this module from CJS to ESM in package.json

Error: Cannot find module '@graphile-contrib/pg-simplify-inflector'

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

Incompatibility/warning messages when installing at a Docker container

Summary

I'm receiving incompatibility/warning messages when installing this plugin into a Docker container:

Steps to reproduce

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

Expected results

No incompatibility/warning messages.

Actual results

Several incompatibility/warning messages.

Additional context

Host info:

  • OS: Manjaro Linux x86_64
  • Kernel: 5.14.2-1-MANJARO
  • Shell: zsh 5.8
  • Memory: 4212MiB / 15927MiB
  • docker 1:20.10.8-1
  • docker-compose 1.29.2-1
  • postgresql image: latest
  • postgraphile image: latest

Document `foreignSimpleFieldName`

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.

Change log very out of date

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.

Update to PGSimplifyInflectorPlugin

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`
        );
      }
    };
  });
};

Lacking Typescript declarations

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?

Typescript Support

Is there a minimum type definition that can be added to allow this to easily work with the rest of postgraphile via TypeScript?

npm install -g for CLI use

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).

Primary keys that are also foreign keys not simplified

Summary

See the code snippet

Steps to reproduce

Given this table design:

CREATE TABLE animal (
  id SERIAL PRIMARY KEY
);

CREATE TABLE cat (
  id integer PRIMARY KEY REFERENCES animal
);

Expected results

An animal query should expose cat, a cat query should expose animal

Actual results

An animal query exposes catById, a cat query exposes animalById

Additional context

Possible Solution

handling field conflict

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';

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.