Giter VIP home page Giter VIP logo

pg-simplify-inflector's People

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

Watchers

 avatar  avatar  avatar

pg-simplify-inflector's Issues

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?

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)

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.

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.

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

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

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.

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

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.

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?

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

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.

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.

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.

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

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

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?

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.