├── .eslintignore ├── .gitattributes ├── .gitignore ├── .prettierrc.js ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── index.d.ts ├── index.js ├── index.ts ├── package.json ├── test.js ├── tests ├── animal │ ├── schema.graphql.diff │ ├── schema.simplified.graphql │ ├── schema.sql │ └── schema.unsimplified.graphql ├── companies │ ├── schema.graphql.diff │ ├── schema.simplified.graphql │ ├── schema.sql │ └── schema.unsimplified.graphql ├── fish │ ├── schema.graphql.diff │ ├── schema.simplified.graphql │ ├── schema.sql │ └── schema.unsimplified.graphql ├── issue_29 │ ├── schema.graphql.diff │ ├── schema.simplified.graphql │ ├── schema.sql │ └── schema.unsimplified.graphql ├── list_suffix_omit │ ├── schema.graphql.diff │ ├── schema.simplified.graphql │ ├── schema.sql │ ├── schema.unsimplified.graphql │ └── settings.json ├── multikey_refs │ ├── schema.graphql.diff │ ├── schema.simplified.graphql │ ├── schema.sql │ └── schema.unsimplified.graphql └── pg_omit_list_suffix │ ├── schema.graphql.diff │ ├── schema.simplified.graphql │ ├── schema.sql │ ├── schema.unsimplified.graphql │ └── settings.json ├── tsconfig.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | tests/*/schema.simplified.graphql 3 | tests/*/schema.unsimplified.graphql 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | tests/*/schema.graphql linguist-generated=true 2 | tests/*/schema.simplified.graphql linguist-generated=true 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: "es5", 3 | proseWrap: "always", 4 | }; 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ALL MAJOR RELEASES ARE BREAKING. 2 | 3 | When the names in your GraphQL schema change you have to rewrite all your 4 | queries. You're not really meant to update major versions. Pick a major and 5 | stick to it. 6 | 7 | It's unlikely we'll have much in the way of minor releases since all naming 8 | changes are breaking. 9 | 10 | # v7.0.0 11 | 12 | ### Simplify primary key to primary key references 13 | 14 | If you have two tables that share a primary key value, this version will 15 | simplify the reference. For example with the following database schema: 16 | 17 | ```sql 18 | create table animals ( 19 | id serial primary key, 20 | name text 21 | ); 22 | 23 | create table dogs ( 24 | animal_id int primary key references animals, 25 | wags_tail bool 26 | ); 27 | ``` 28 | 29 | This will create `Animal.dog` (rather than `Animal.dogById`) and `Dog.animal` 30 | (rather than `Dog.animalById`). 31 | 32 | # v6.1.0 33 | 34 | ### Add `@listSuffix` smart tag 35 | 36 | Previously there was no easy way to override `pgOmitListSuffix` on a per-entity 37 | basis. With the `@listSuffix` tag you can selectively control naming of both 38 | collection types: 39 | 40 | ```sql 41 | create table companies (id serial primary key); 42 | comment on table companies is E'@listSuffix omit'; 43 | ``` 44 | 45 | By default (`pgOmitListSuffix = null` and `simpleCollections = 'both'`) this 46 | produces: 47 | 48 | ```diff 49 | - allCompanies( 50 | + companiesConnection( 51 | ``` 52 | 53 | ```diff 54 | - allCompaniesList( 55 | + companies( 56 | ``` 57 | 58 | # v6.0.0 59 | 60 | #### Pluralization fixes 61 | 62 | Previously we fed the entire table/etc name into `pluralize` but this causes 63 | issues for special cases, for example while `pluralize('genus')` correctly gives 64 | `genera`, `pluralize('old_genus')` would give `old_genus` which is not correct. 65 | 66 | Now we segment on underscores/capitals and only pluralize the final segment, so 67 | we're more likely to get the correct result. 68 | 69 | This affects everywhere in your entire GraphQL schema where 70 | pluralize/singularize is used. 71 | 72 | #### Simplify multi-key relationships. 73 | 74 | ```sql 75 | foreign key (organization_id, team_id, goal_uuid) references goals 76 | ``` 77 | 78 | Now named better: 79 | 80 | ```diff 81 | - goalByOrganizationIdAndTeamIdAndGoalUuid: Goal 82 | + organizationTeamGoal: Goal 83 | ``` 84 | 85 | #### Unique relations get shorter-named reverse field. 86 | 87 | This was a bug (or, really, an omission) in v5. 88 | 89 | For this table: 90 | 91 | ```sql 92 | create table mascots ( 93 | id serial primary key, 94 | company_id int unique not null references companies, 95 | name text not null 96 | ); 97 | ``` 98 | 99 | Previously we had the plural relationship simplified: 100 | 101 | ```diff 102 | - mascotsByCompanyId( 103 | + mascots( 104 | ``` 105 | 106 | But the singular was not. This update changes the singular too: 107 | 108 | ```diff 109 | - mascotByCompanyId: Mascot 110 | + mascot: Mascot 111 | ``` 112 | 113 | # v5.0.0-beta.1 114 | 115 | # v5.0.0-beta.0 116 | 117 | More advanced guesses at field names for reverse relations. Ability to omit list 118 | suffix, simplify patch names, turn on/off simplifying of the 'all' from 119 | 'allUsers', ability to change the 'ById' primary key fields to not have that 120 | suffix and instead have the node ID fetchers have a suffix. 121 | 122 | # v3.0.0 123 | 124 | Simplifies naming in more of the schema. 125 | 126 | # v2.0.0 127 | 128 | Breaking change: single relation names based on a single key are now named after 129 | the key rather than the target table so long as the key is of the form `foo_id`, 130 | `foo_uuid`. 131 | 132 | ```sql 133 | create table posts ( 134 | id serial primary key, 135 | author_id int not null references users, 136 | body text not null 137 | ); 138 | ``` 139 | 140 | ```diff 141 | type Post { 142 | nodeId: ID! 143 | id: Int! 144 | authorId: Int! 145 | - user: User 146 | + author: User 147 | body: String! 148 | } 149 | ``` 150 | 151 | # v1.0.0 152 | 153 | Initial release 154 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Benjie Gillam 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the “Software”), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @graphile-contrib/pg-simplify-inflector 2 | 3 | (For PostGraphile v5, use [@graphile/simplify-inflection](https://www.npmjs.com/package/@graphile/simplify-inflection)) 4 | 5 | This plugin simplifies field names in the PostGraphile schema; e.g. `allUsers` 6 | becomes simply `users`, `User.postsByAuthorId` becomes simply `User.posts`, and 7 | `Post.userByAuthorId` becomes simply `Post.author`. 8 | 9 | **Adding this plugin to your schema is almost certainly a breaking change, so do 10 | it before you ship anything!** This is the primary reason this isn't enabled by 11 | default in PostGraphile. 12 | 13 | _This plugin is recommended for all PostGraphile users._ 14 | 15 | ## Customising 16 | 17 | This plugin is implemented as a single JS file that does not need to be compiled 18 | at all - you can simply copy it into your project and customise it as you see 19 | fit. 20 | 21 | Alternatively, you can 22 | [write your own inflection plugin](https://www.graphile.org/postgraphile/inflection/). 23 | 24 | ## Changes: 25 | 26 | Given these tables: 27 | 28 | ```sql 29 | create table companies ( 30 | id serial primary key, 31 | name text not null 32 | ); 33 | create table beverages ( 34 | id serial primary key, 35 | company_id int not null references companies, 36 | distributor_id int references companies, 37 | name text not null 38 | ); 39 | ``` 40 | 41 | - `Query.allCompanies` 👉 `Query.companies` (disable via 42 | `pgSimplifyAllRows = false`) 43 | - `Query.allBeverages` 👉 `Query.beverages` 44 | - `Beverage.companyByCompanyId` 👉 `Beverage.company` 45 | - `Beverage.companyByDistributorId` 👉 `Beverage.distributor` 46 | - `Company.beveragesByCompanyId` 👉 `Company.beverages` (because the 47 | `company_id` column follows the `[table_name]_id` naming convention) 48 | - All update mutations now accept `patch` instead of `companyPatch` / 49 | `beveragePatch` (disable via `pgSimplifyPatch = false`) 50 | - If you are using `pgSimpleCollections = "only"` then you can set 51 | `pgOmitListSuffix = true` to omit the `List` suffix 52 | - Fields where the singular and plural are the same and a distinct plural is 53 | required are force-pluralised ("fishes") to avoid conflicts (e.g. 54 | `singularize("fish") === pluralize("fish")`). 55 | 56 | Note: `Company.beveragesByDistributorId` will remain, because `distributor_id` 57 | does not follow the `[table_name]_id` naming convention, but you could rename 58 | this yourself with a smart comment: 59 | 60 | ```sql 61 | comment on constraint "beverages_distributor_id_fkey" on "beverages" is 62 | E'@foreignFieldName distributedBeverages'; 63 | ``` 64 | 65 | or with a custom inflector: 66 | 67 | ```js 68 | module.exports = makeAddInflectorsPlugin( 69 | { 70 | getOppositeBaseName(baseName) { 71 | return ( 72 | { 73 | // These are the default opposites 74 | parent: "child", 75 | child: "parent", 76 | author: "authored", 77 | editor: "edited", 78 | reviewer: "reviewed", 79 | 80 | // 👇 Add/customise this line: 81 | distributor: "distributed", 82 | }[baseName] || null 83 | ); 84 | }, 85 | }, 86 | true 87 | ); 88 | ``` 89 | 90 | ## Installation: 91 | 92 | ```bash 93 | yarn add @graphile-contrib/pg-simplify-inflector 94 | ``` 95 | 96 | or 97 | 98 | ```bash 99 | npm install --save @graphile-contrib/pg-simplify-inflector 100 | ``` 101 | 102 | ## Usage: 103 | 104 | CLI: 105 | 106 | ```bash 107 | postgraphile --append-plugins @graphile-contrib/pg-simplify-inflector 108 | ``` 109 | 110 | Library: 111 | 112 | ```js 113 | const PgSimplifyInflectorPlugin = require("@graphile-contrib/pg-simplify-inflector"); 114 | 115 | // ... 116 | 117 | app.use( 118 | postgraphile(process.env.AUTH_DATABASE_URL, "app_public", { 119 | appendPlugins: [PgSimplifyInflectorPlugin], 120 | 121 | // Optional customisation 122 | graphileBuildOptions: { 123 | /* 124 | * Uncomment if you want simple collections to lose the 'List' suffix 125 | * (and connections to gain a 'Connection' suffix). 126 | */ 127 | //pgOmitListSuffix: true, 128 | /* 129 | * Uncomment if you want 'userPatch' instead of 'patch' in update 130 | * mutations. 131 | */ 132 | //pgSimplifyPatch: false, 133 | /* 134 | * Uncomment if you want 'allUsers' instead of 'users' at root level. 135 | */ 136 | //pgSimplifyAllRows: false, 137 | /* 138 | * Uncomment if you want primary key queries and mutations to have 139 | * `ById` (or similar) suffix; and the `nodeId` queries/mutations 140 | * to lose their `ByNodeId` suffix. 141 | */ 142 | // pgShortPk: true, 143 | }, 144 | // ... other settings ... 145 | }) 146 | ); 147 | ``` 148 | 149 | ## Naming your foreign key fields 150 | 151 | By naming your foreign key along the lines of `author_id` or `author_fk`, e.g.: 152 | 153 | ```sql 154 | CREATE TABLE posts ( 155 | id serial primary key, 156 | author_id int not null references users, 157 | ... 158 | ); 159 | ``` 160 | 161 | We can automatically extract the field prefix: `author` and call the relation 162 | `author` rather than the default: `user`. This allows for a post to have an 163 | `author`, `editor`, `reviewer`, etc. all which point to `users`. 164 | 165 | The reverse, however, is not so easy. On the User type, we can't call the 166 | reverse of all these different relations `posts`. The default inflector refers 167 | to these as `postsByAuthorId`, `postsByEditorId`, etc. However we'd rather use 168 | shorter names, so we introduce a new inflector: `getOppositeBaseName`. This 169 | inflector is passed a baseName (the part without the `_id`/`_fk` suffix, e.g. 170 | `author`, `editor`, `reviewer` above) and should return the opposite of that 171 | base name which will be prepended to the target type to produce, e.g. 172 | `authoredPosts`, `editedPosts`, `reviewedPosts`. Failing this, we just fall back 173 | to the default (verbose) inflector; it will be up to you to add smart comments 174 | or a custom inflector to override these. 175 | 176 | ## Handling field conflicts: 177 | 178 | In most cases, the conflict errors will guide you on how to fix these issues 179 | using [smart comments](https://www.graphile.org/postgraphile/smart-comments/). 180 | 181 | ## Smart Tags 182 | 183 | ### `@foreignSimpleFieldName` 184 | 185 | `@foreignSimpleFieldName` was added to override the naming of the foreign-side 186 | of a one-to-many relationship's simple collections field (if you're using simple 187 | collections). By default we'll take the `@foreignFieldName` and add the "list 188 | suffix" ("List" by default, but "" if `pgOmitListSuffix` is set), but if you 189 | prefer you can override it entirely with `@foreignSimpleFieldName`. If you set 190 | `@foreignSimpleFieldName` and you're using `simpleCollections 'both'` then you 191 | should also set `@foreignFieldName` explicitly or unexpected things may occur. 192 | 193 | Applies to: 194 | 195 | - foreign key constraints 196 | 197 | ### `@listSuffix` 198 | 199 | `@listSuffix` allows you to override the default naming on a per-entity basis, 200 | overriding `pgOmitListSuffix`. For example, with `pgOmitListSuffix: true`, you 201 | can apply `@listSuffix include` to have the `-List` suffix appended to the 202 | simple collection generated for that table, and remove the `-Connection` suffix 203 | from the Relay connection. When `pgOmitListSuffix` is not `true`, you can use 204 | `@listSuffix omit` to selectively omit the `-List` suffix on simple collections 205 | and append `-Connection` to the Relay connection instead. 206 | 207 | If `@listSuffix` is set, the only valid values are `"omit"` and `"include"`. Any 208 | other value will cause an error. 209 | 210 | | | @listSuffix omit | @listSuffix include | 211 | | ----------------: | :------------------ | :------------------ | 212 | | Relay Connection | companiesConnection | companies | 213 | | Simple Collection | companies | companiesList | 214 | 215 | > NOTE: `@listSuffix` will have no effect when using `@foreignSimpleFieldName`. 216 | 217 | Applies to: 218 | 219 | - tables 220 | - foreign key constraints 221 | - computed column functions returning `SETOF ` 222 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from "graphile-build"; 2 | declare const PgSimplifyInflectorPlugin: Plugin; 3 | export default PgSimplifyInflectorPlugin; 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | function fixCapitalisedPlural(fn) { 4 | return function (str) { 5 | const original = fn.call(this, str); 6 | return original.replace(/[0-9]S(?=[A-Z]|$)/g, (match) => 7 | match.toLowerCase() 8 | ); 9 | }; 10 | } 11 | function fixChangePlural(fn) { 12 | return function (str) { 13 | const matches = str.match(/([A-Z]|_[a-z0-9])[a-z0-9]*_*$/); 14 | const index = matches ? matches.index + matches[1].length - 1 : 0; 15 | const suffixMatches = str.match(/_*$/); 16 | const suffixIndex = suffixMatches.index; 17 | const prefix = str.substr(0, index); 18 | const word = str.substr(index, suffixIndex - index); 19 | const suffix = str.substr(suffixIndex); 20 | return `${prefix}${fn.call(this, word)}${suffix}`; 21 | }; 22 | } 23 | function isPrimaryKey(detailedKeys, table) { 24 | if (!table.primaryKeyConstraint) { 25 | return false; 26 | } 27 | const { keyAttributes } = table.primaryKeyConstraint; 28 | return ( 29 | detailedKeys.length === keyAttributes.length && 30 | detailedKeys.every((key, i) => key === keyAttributes[i]) 31 | ); 32 | } 33 | function PgSimplifyInflectorPlugin( 34 | builder, 35 | { 36 | pgSimpleCollections, 37 | pgOmitListSuffix, 38 | pgSimplifyPatch = true, 39 | pgSimplifyAllRows = true, 40 | pgShortPk = true, 41 | pgSimplifyMultikeyRelations = true, 42 | nodeIdFieldName = "nodeId", 43 | } 44 | ) { 45 | const hasConnections = pgSimpleCollections !== "only"; 46 | const hasSimpleCollections = 47 | pgSimpleCollections === "only" || pgSimpleCollections === "both"; 48 | if ( 49 | hasSimpleCollections && 50 | !hasConnections && 51 | pgOmitListSuffix !== true && 52 | pgOmitListSuffix !== false 53 | ) { 54 | // eslint-disable-next-line no-console 55 | console.warn( 56 | "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." 57 | ); 58 | } 59 | function omitListSuffix(entity) { 60 | const tag = entity.tags.listSuffix; 61 | if (tag == null) return !!pgOmitListSuffix; 62 | if (tag !== "include" && tag !== "omit") 63 | throw new Error( 64 | `Unrecognized @listSuffix value "${tag}" on ${entity.kind} "${entity.name}". If @listSuffix is set, it must be "omit" or "include".` 65 | ); 66 | return tag === "omit"; 67 | } 68 | function connectionSuffix(entity) { 69 | return omitListSuffix(entity) ? "-connection" : ""; 70 | } 71 | function ConnectionSuffix(entity) { 72 | return omitListSuffix(entity) ? "Connection" : ""; 73 | } 74 | function listSuffix(entity) { 75 | return omitListSuffix(entity) ? "" : "-list"; 76 | } 77 | function ListSuffix(entity) { 78 | return omitListSuffix(entity) ? "" : "List"; 79 | } 80 | builder.hook("inflection", (oldInflection) => { 81 | return { 82 | ...oldInflection, 83 | /* 84 | * This solves the issue with `blah-table1s` becoming `blahTable1S` 85 | * (i.e. the capital S at the end) or `table1-connection becoming `Table1SConnection` 86 | */ 87 | camelCase: fixCapitalisedPlural(oldInflection.camelCase), 88 | upperCamelCase: fixCapitalisedPlural(oldInflection.upperCamelCase), 89 | /* 90 | * Pluralize/singularize only supports single words, so only run 91 | * on the final segment of a name. 92 | */ 93 | pluralize: fixChangePlural(oldInflection.pluralize), 94 | singularize: fixChangePlural(oldInflection.singularize), 95 | distinctPluralize(str) { 96 | const singular = this.singularize(str); 97 | const plural = this.pluralize(singular); 98 | if (singular !== plural) { 99 | return plural; 100 | } 101 | if ( 102 | plural.endsWith("ch") || 103 | plural.endsWith("s") || 104 | plural.endsWith("sh") || 105 | plural.endsWith("x") || 106 | plural.endsWith("z") 107 | ) { 108 | return plural + "es"; 109 | } else if (plural.endsWith("y")) { 110 | return plural.slice(0, -1) + "ies"; 111 | } else { 112 | return plural + "s"; 113 | } 114 | }, 115 | // Fix a naming bug 116 | deletedNodeId(table) { 117 | return this.camelCase( 118 | `deleted-${this.singularize(table.name)}-${nodeIdFieldName}` 119 | ); 120 | }, 121 | getBaseName(columnName) { 122 | const matches = columnName.match( 123 | /^(.+?)(_row_id|_id|_uuid|_fk|_pk|RowId|Id|Uuid|UUID|Fk|Pk)$/ 124 | ); 125 | if (matches) { 126 | return matches[1]; 127 | } 128 | return null; 129 | }, 130 | baseNameMatches(baseName, otherName) { 131 | const singularizedName = this.singularize(otherName); 132 | return baseName === singularizedName; 133 | }, 134 | /* This is a good method to override. */ 135 | getOppositeBaseName(baseName) { 136 | return ( 137 | { 138 | /* 139 | * Changes to this list are breaking changes and will require a 140 | * major version update, so we need to group as many together as 141 | * possible! Rather than sending a PR, please look for an open 142 | * issue called something like "Add more opposites" (if there isn't 143 | * one then please open it) and add your suggestions to the GitHub 144 | * comments. 145 | */ 146 | parent: "child", 147 | child: "parent", 148 | author: "authored", 149 | editor: "edited", 150 | reviewer: "reviewed", 151 | }[baseName] || null 152 | ); 153 | }, 154 | getBaseNameFromKeys(detailedKeys) { 155 | if (detailedKeys.length === 1) { 156 | const key = detailedKeys[0]; 157 | const columnName = this._columnName(key); 158 | return this.getBaseName(columnName); 159 | } 160 | if (pgSimplifyMultikeyRelations) { 161 | const columnNames = detailedKeys.map((key) => this._columnName(key)); 162 | const baseNames = columnNames.map((columnName) => 163 | this.getBaseName(columnName) 164 | ); 165 | // Check none are null 166 | if (baseNames.every((n) => n)) { 167 | return baseNames.join("-"); 168 | } 169 | } 170 | return null; 171 | }, 172 | ...(pgSimplifyPatch 173 | ? { 174 | patchField() { 175 | return "patch"; 176 | }, 177 | } 178 | : null), 179 | ...(pgSimplifyAllRows 180 | ? { 181 | allRows(table) { 182 | return this.camelCase( 183 | this.distinctPluralize(this._singularizedTableName(table)) + 184 | connectionSuffix(table) 185 | ); 186 | }, 187 | allRowsSimple(table) { 188 | return this.camelCase( 189 | this.distinctPluralize(this._singularizedTableName(table)) + 190 | listSuffix(table) 191 | ); 192 | }, 193 | } 194 | : null), 195 | computedColumn(pseudoColumnName, proc, _table) { 196 | return proc.tags.fieldName 197 | ? proc.tags.fieldName + 198 | (proc.returnsSet ? ConnectionSuffix(proc) : "") 199 | : this.camelCase( 200 | pseudoColumnName + (proc.returnsSet ? connectionSuffix(proc) : "") 201 | ); 202 | }, 203 | computedColumnList(pseudoColumnName, proc, _table) { 204 | return proc.tags.fieldName 205 | ? proc.tags.fieldName + ListSuffix(proc) 206 | : this.camelCase(pseudoColumnName + listSuffix(proc)); 207 | }, 208 | singleRelationByKeys(detailedKeys, table, foreignTable, constraint) { 209 | if (constraint.tags.fieldName) { 210 | return constraint.tags.fieldName; 211 | } 212 | const baseName = this.getBaseNameFromKeys(detailedKeys); 213 | if (baseName) { 214 | return this.camelCase(baseName); 215 | } 216 | if (isPrimaryKey(detailedKeys, foreignTable)) { 217 | return this.camelCase(`${this._singularizedTableName(table)}`); 218 | } 219 | return oldInflection.singleRelationByKeys( 220 | detailedKeys, 221 | table, 222 | foreignTable, 223 | constraint 224 | ); 225 | }, 226 | singleRelationByKeysBackwards( 227 | detailedKeys, 228 | table, 229 | foreignTable, 230 | constraint 231 | ) { 232 | if (constraint.tags.foreignSingleFieldName) { 233 | return constraint.tags.foreignSingleFieldName; 234 | } 235 | if (constraint.tags.foreignFieldName) { 236 | return constraint.tags.foreignFieldName; 237 | } 238 | const baseName = this.getBaseNameFromKeys(detailedKeys); 239 | if (baseName) { 240 | const oppositeBaseName = this.getOppositeBaseName(baseName); 241 | if (oppositeBaseName) { 242 | return this.camelCase( 243 | `${oppositeBaseName}-${this._singularizedTableName(table)}` 244 | ); 245 | } 246 | if (this.baseNameMatches(baseName, foreignTable.name)) { 247 | return this.camelCase(`${this._singularizedTableName(table)}`); 248 | } 249 | } 250 | if (isPrimaryKey(detailedKeys, table)) { 251 | return this.camelCase(`${this._singularizedTableName(table)}`); 252 | } 253 | return oldInflection.singleRelationByKeysBackwards( 254 | detailedKeys, 255 | table, 256 | foreignTable, 257 | constraint 258 | ); 259 | }, 260 | _manyRelationByKeysBase(detailedKeys, table, _foreignTable, _constraint) { 261 | const baseName = this.getBaseNameFromKeys(detailedKeys); 262 | if (baseName) { 263 | const oppositeBaseName = this.getOppositeBaseName(baseName); 264 | if (oppositeBaseName) { 265 | return this.camelCase( 266 | `${oppositeBaseName}-${this.distinctPluralize( 267 | this._singularizedTableName(table) 268 | )}` 269 | ); 270 | } 271 | if (this.baseNameMatches(baseName, _foreignTable.name)) { 272 | return this.camelCase( 273 | `${this.distinctPluralize(this._singularizedTableName(table))}` 274 | ); 275 | } 276 | } 277 | return null; 278 | }, 279 | manyRelationByKeys(detailedKeys, table, foreignTable, constraint) { 280 | if (constraint.tags.foreignFieldName) { 281 | if (constraint.tags.foreignSimpleFieldName) { 282 | return constraint.tags.foreignFieldName; 283 | } else { 284 | return ( 285 | constraint.tags.foreignFieldName + ConnectionSuffix(constraint) 286 | ); 287 | } 288 | } 289 | const base = this._manyRelationByKeysBase( 290 | detailedKeys, 291 | table, 292 | foreignTable, 293 | constraint 294 | ); 295 | if (base) { 296 | return base + ConnectionSuffix(constraint); 297 | } 298 | if (isPrimaryKey(detailedKeys, table)) { 299 | return ( 300 | this.camelCase( 301 | `${this.distinctPluralize(this._singularizedTableName(table))}` 302 | ) + ConnectionSuffix(constraint) 303 | ); 304 | } 305 | return ( 306 | oldInflection.manyRelationByKeys( 307 | detailedKeys, 308 | table, 309 | foreignTable, 310 | constraint 311 | ) + ConnectionSuffix(constraint) 312 | ); 313 | }, 314 | manyRelationByKeysSimple(detailedKeys, table, foreignTable, constraint) { 315 | if (constraint.tags.foreignSimpleFieldName) { 316 | return constraint.tags.foreignSimpleFieldName; 317 | } 318 | if (constraint.tags.foreignFieldName) { 319 | return constraint.tags.foreignFieldName + ListSuffix(constraint); 320 | } 321 | const base = this._manyRelationByKeysBase( 322 | detailedKeys, 323 | table, 324 | foreignTable, 325 | constraint 326 | ); 327 | if (base) { 328 | return base + ListSuffix(constraint); 329 | } 330 | if (isPrimaryKey(detailedKeys, table)) { 331 | return ( 332 | this.camelCase( 333 | `${this.distinctPluralize(this._singularizedTableName(table))}` 334 | ) + ListSuffix(constraint) 335 | ); 336 | } 337 | return ( 338 | oldInflection.manyRelationByKeys( 339 | detailedKeys, 340 | table, 341 | foreignTable, 342 | constraint 343 | ) + ListSuffix(constraint) 344 | ); 345 | }, 346 | functionQueryName(proc) { 347 | return this.camelCase( 348 | this._functionName(proc) + 349 | (proc.returnsSet ? connectionSuffix(proc) : "") 350 | ); 351 | }, 352 | functionQueryNameList(proc) { 353 | return this.camelCase(this._functionName(proc) + listSuffix(proc)); 354 | }, 355 | ...(pgShortPk 356 | ? { 357 | tableNode(table) { 358 | return this.camelCase( 359 | `${this._singularizedTableName(table)}-by-${nodeIdFieldName}` 360 | ); 361 | }, 362 | rowByUniqueKeys(detailedKeys, table, constraint) { 363 | if (constraint.tags.fieldName) { 364 | return constraint.tags.fieldName; 365 | } 366 | if (constraint.type === "p") { 367 | // Primary key, shorten! 368 | return this.camelCase(this._singularizedTableName(table)); 369 | } else { 370 | return this.camelCase( 371 | `${this._singularizedTableName(table)}-by-${detailedKeys 372 | .map((key) => this.column(key)) 373 | .join("-and-")}` 374 | ); 375 | } 376 | }, 377 | updateByKeys(detailedKeys, table, constraint) { 378 | if (constraint.tags.updateFieldName) { 379 | return constraint.tags.updateFieldName; 380 | } 381 | if (constraint.type === "p") { 382 | return this.camelCase( 383 | `update-${this._singularizedTableName(table)}` 384 | ); 385 | } else { 386 | return this.camelCase( 387 | `update-${this._singularizedTableName( 388 | table 389 | )}-by-${detailedKeys 390 | .map((key) => this.column(key)) 391 | .join("-and-")}` 392 | ); 393 | } 394 | }, 395 | deleteByKeys(detailedKeys, table, constraint) { 396 | if (constraint.tags.deleteFieldName) { 397 | return constraint.tags.deleteFieldName; 398 | } 399 | if (constraint.type === "p") { 400 | // Primary key, shorten! 401 | return this.camelCase( 402 | `delete-${this._singularizedTableName(table)}` 403 | ); 404 | } else { 405 | return this.camelCase( 406 | `delete-${this._singularizedTableName( 407 | table 408 | )}-by-${detailedKeys 409 | .map((key) => this.column(key)) 410 | .join("-and-")}` 411 | ); 412 | } 413 | }, 414 | updateByKeysInputType(detailedKeys, table, constraint) { 415 | if (constraint.tags.updateFieldName) { 416 | return this.upperCamelCase( 417 | `${constraint.tags.updateFieldName}-input` 418 | ); 419 | } 420 | if (constraint.type === "p") { 421 | // Primary key, shorten! 422 | return this.upperCamelCase( 423 | `update-${this._singularizedTableName(table)}-input` 424 | ); 425 | } else { 426 | return this.upperCamelCase( 427 | `update-${this._singularizedTableName( 428 | table 429 | )}-by-${detailedKeys 430 | .map((key) => this.column(key)) 431 | .join("-and-")}-input` 432 | ); 433 | } 434 | }, 435 | deleteByKeysInputType(detailedKeys, table, constraint) { 436 | if (constraint.tags.deleteFieldName) { 437 | return this.upperCamelCase( 438 | `${constraint.tags.deleteFieldName}-input` 439 | ); 440 | } 441 | if (constraint.type === "p") { 442 | // Primary key, shorten! 443 | return this.upperCamelCase( 444 | `delete-${this._singularizedTableName(table)}-input` 445 | ); 446 | } else { 447 | return this.upperCamelCase( 448 | `delete-${this._singularizedTableName( 449 | table 450 | )}-by-${detailedKeys 451 | .map((key) => this.column(key)) 452 | .join("-and-")}-input` 453 | ); 454 | } 455 | }, 456 | updateNode(table) { 457 | return this.camelCase( 458 | `update-${this._singularizedTableName( 459 | table 460 | )}-by-${nodeIdFieldName}` 461 | ); 462 | }, 463 | deleteNode(table) { 464 | return this.camelCase( 465 | `delete-${this._singularizedTableName( 466 | table 467 | )}-by-${nodeIdFieldName}` 468 | ); 469 | }, 470 | updateNodeInputType(table) { 471 | return this.upperCamelCase( 472 | `update-${this._singularizedTableName( 473 | table 474 | )}-by-${nodeIdFieldName}-input` 475 | ); 476 | }, 477 | deleteNodeInputType(table) { 478 | return this.upperCamelCase( 479 | `delete-${this._singularizedTableName( 480 | table 481 | )}-by-${nodeIdFieldName}-input` 482 | ); 483 | }, 484 | } 485 | : null), 486 | }; 487 | }); 488 | } 489 | module.exports = PgSimplifyInflectorPlugin; 490 | // Hacks for TypeScript/Babel import 491 | module.exports.default = PgSimplifyInflectorPlugin; 492 | Object.defineProperty(module.exports, "__esModule", { value: true }); 493 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import type { Inflection, Options, SchemaBuilder } from "graphile-build"; 2 | import type { 3 | PgClass, 4 | PgConstraint, 5 | PgEntity, 6 | PgProc, 7 | PgAttribute, 8 | } from "graphile-build-pg"; 9 | 10 | function fixCapitalisedPlural(fn: (this: Inflection, str: string) => string) { 11 | return function (this: Inflection, str: string) { 12 | const original = fn.call(this, str); 13 | return original.replace(/[0-9]S(?=[A-Z]|$)/g, (match) => 14 | match.toLowerCase() 15 | ); 16 | }; 17 | } 18 | 19 | function fixChangePlural(fn: (this: Inflection, str: string) => string) { 20 | return function (this: Inflection, str: string) { 21 | const matches = str.match(/([A-Z]|_[a-z0-9])[a-z0-9]*_*$/); 22 | const index = matches ? matches.index! + matches[1].length - 1 : 0; 23 | const suffixMatches = str.match(/_*$/); 24 | const suffixIndex = suffixMatches!.index!; 25 | const prefix = str.substr(0, index); 26 | const word = str.substr(index, suffixIndex - index); 27 | const suffix = str.substr(suffixIndex); 28 | return `${prefix}${fn.call(this, word)}${suffix}`; 29 | }; 30 | } 31 | 32 | function isPrimaryKey(detailedKeys: PgAttribute[], table: PgClass): boolean { 33 | if (!table.primaryKeyConstraint) { 34 | return false; 35 | } 36 | const { keyAttributes } = table.primaryKeyConstraint; 37 | return ( 38 | detailedKeys.length === keyAttributes.length && 39 | detailedKeys.every((key, i) => key === keyAttributes[i]) 40 | ); 41 | } 42 | 43 | function PgSimplifyInflectorPlugin( 44 | builder: SchemaBuilder, 45 | { 46 | pgSimpleCollections, 47 | pgOmitListSuffix, 48 | pgSimplifyPatch = true, 49 | pgSimplifyAllRows = true, 50 | pgShortPk = true, 51 | pgSimplifyMultikeyRelations = true, 52 | nodeIdFieldName = "nodeId", 53 | }: Options 54 | ) { 55 | const hasConnections = pgSimpleCollections !== "only"; 56 | const hasSimpleCollections = 57 | pgSimpleCollections === "only" || pgSimpleCollections === "both"; 58 | 59 | if ( 60 | hasSimpleCollections && 61 | !hasConnections && 62 | pgOmitListSuffix !== true && 63 | pgOmitListSuffix !== false 64 | ) { 65 | // eslint-disable-next-line no-console 66 | console.warn( 67 | "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." 68 | ); 69 | } 70 | 71 | function omitListSuffix(entity: PgEntity) { 72 | const tag = entity.tags.listSuffix; 73 | if (tag == null) return !!pgOmitListSuffix; 74 | if (tag !== "include" && tag !== "omit") 75 | throw new Error( 76 | `Unrecognized @listSuffix value "${tag}" on ${entity.kind} "${entity.name}". If @listSuffix is set, it must be "omit" or "include".` 77 | ); 78 | return tag === "omit"; 79 | } 80 | 81 | function connectionSuffix(entity: PgEntity) { 82 | return omitListSuffix(entity) ? "-connection" : ""; 83 | } 84 | 85 | function ConnectionSuffix(entity: PgEntity) { 86 | return omitListSuffix(entity) ? "Connection" : ""; 87 | } 88 | 89 | function listSuffix(entity: PgEntity) { 90 | return omitListSuffix(entity) ? "" : "-list"; 91 | } 92 | 93 | function ListSuffix(entity: PgEntity) { 94 | return omitListSuffix(entity) ? "" : "List"; 95 | } 96 | 97 | builder.hook("inflection", (oldInflection) => { 98 | return { 99 | ...oldInflection, 100 | 101 | /* 102 | * This solves the issue with `blah-table1s` becoming `blahTable1S` 103 | * (i.e. the capital S at the end) or `table1-connection becoming `Table1SConnection` 104 | */ 105 | camelCase: fixCapitalisedPlural(oldInflection.camelCase), 106 | upperCamelCase: fixCapitalisedPlural(oldInflection.upperCamelCase), 107 | 108 | /* 109 | * Pluralize/singularize only supports single words, so only run 110 | * on the final segment of a name. 111 | */ 112 | pluralize: fixChangePlural(oldInflection.pluralize), 113 | singularize: fixChangePlural(oldInflection.singularize), 114 | 115 | distinctPluralize(str: string) { 116 | const singular = this.singularize(str); 117 | const plural = this.pluralize(singular); 118 | if (singular !== plural) { 119 | return plural; 120 | } 121 | if ( 122 | plural.endsWith("ch") || 123 | plural.endsWith("s") || 124 | plural.endsWith("sh") || 125 | plural.endsWith("x") || 126 | plural.endsWith("z") 127 | ) { 128 | return plural + "es"; 129 | } else if (plural.endsWith("y")) { 130 | return plural.slice(0, -1) + "ies"; 131 | } else { 132 | return plural + "s"; 133 | } 134 | }, 135 | 136 | // Fix a naming bug 137 | deletedNodeId(table: PgClass) { 138 | return this.camelCase( 139 | `deleted-${this.singularize(table.name)}-${nodeIdFieldName}` 140 | ); 141 | }, 142 | 143 | getBaseName(columnName: string) { 144 | const matches = columnName.match( 145 | /^(.+?)(_row_id|_id|_uuid|_fk|_pk|RowId|Id|Uuid|UUID|Fk|Pk)$/ 146 | ); 147 | if (matches) { 148 | return matches[1]; 149 | } 150 | return null; 151 | }, 152 | 153 | baseNameMatches(baseName: string, otherName: string) { 154 | const singularizedName = this.singularize(otherName); 155 | return baseName === singularizedName; 156 | }, 157 | 158 | /* This is a good method to override. */ 159 | getOppositeBaseName(baseName: string) { 160 | return ( 161 | ( 162 | { 163 | /* 164 | * Changes to this list are breaking changes and will require a 165 | * major version update, so we need to group as many together as 166 | * possible! Rather than sending a PR, please look for an open 167 | * issue called something like "Add more opposites" (if there isn't 168 | * one then please open it) and add your suggestions to the GitHub 169 | * comments. 170 | */ 171 | parent: "child", 172 | child: "parent", 173 | author: "authored", 174 | editor: "edited", 175 | reviewer: "reviewed", 176 | } as { [key: string]: string } 177 | )[baseName] || null 178 | ); 179 | }, 180 | 181 | getBaseNameFromKeys(detailedKeys: string[]) { 182 | if (detailedKeys.length === 1) { 183 | const key = detailedKeys[0]; 184 | const columnName = this._columnName(key); 185 | return this.getBaseName(columnName); 186 | } 187 | if (pgSimplifyMultikeyRelations) { 188 | const columnNames = detailedKeys.map((key) => this._columnName(key)); 189 | const baseNames = columnNames.map((columnName) => 190 | this.getBaseName(columnName) 191 | ); 192 | // Check none are null 193 | if (baseNames.every((n) => n)) { 194 | return baseNames.join("-"); 195 | } 196 | } 197 | return null; 198 | }, 199 | 200 | ...(pgSimplifyPatch 201 | ? { 202 | patchField() { 203 | return "patch"; 204 | }, 205 | } 206 | : null), 207 | 208 | ...(pgSimplifyAllRows 209 | ? { 210 | allRows(table: PgClass) { 211 | return this.camelCase( 212 | this.distinctPluralize(this._singularizedTableName(table)) + 213 | connectionSuffix(table) 214 | ); 215 | }, 216 | allRowsSimple(table: PgClass) { 217 | return this.camelCase( 218 | this.distinctPluralize(this._singularizedTableName(table)) + 219 | listSuffix(table) 220 | ); 221 | }, 222 | } 223 | : null), 224 | 225 | computedColumn(pseudoColumnName: string, proc: PgProc, _table: PgClass) { 226 | return proc.tags.fieldName 227 | ? proc.tags.fieldName + 228 | (proc.returnsSet ? ConnectionSuffix(proc) : "") 229 | : this.camelCase( 230 | pseudoColumnName + (proc.returnsSet ? connectionSuffix(proc) : "") 231 | ); 232 | }, 233 | 234 | computedColumnList( 235 | pseudoColumnName: string, 236 | proc: PgProc, 237 | _table: PgClass 238 | ) { 239 | return proc.tags.fieldName 240 | ? proc.tags.fieldName + ListSuffix(proc) 241 | : this.camelCase(pseudoColumnName + listSuffix(proc)); 242 | }, 243 | 244 | singleRelationByKeys( 245 | detailedKeys: PgAttribute[], 246 | table: PgClass, 247 | foreignTable: PgClass, 248 | constraint: PgConstraint 249 | ) { 250 | if (constraint.tags.fieldName) { 251 | return constraint.tags.fieldName; 252 | } 253 | const baseName = this.getBaseNameFromKeys(detailedKeys); 254 | if (baseName) { 255 | return this.camelCase(baseName); 256 | } 257 | if (isPrimaryKey(detailedKeys, foreignTable)) { 258 | return this.camelCase(`${this._singularizedTableName(table)}`); 259 | } 260 | return oldInflection.singleRelationByKeys( 261 | detailedKeys, 262 | table, 263 | foreignTable, 264 | constraint 265 | ); 266 | }, 267 | 268 | singleRelationByKeysBackwards( 269 | detailedKeys: PgAttribute[], 270 | table: PgClass, 271 | foreignTable: PgClass, 272 | constraint: PgConstraint 273 | ) { 274 | if (constraint.tags.foreignSingleFieldName) { 275 | return constraint.tags.foreignSingleFieldName; 276 | } 277 | if (constraint.tags.foreignFieldName) { 278 | return constraint.tags.foreignFieldName; 279 | } 280 | const baseName = this.getBaseNameFromKeys(detailedKeys); 281 | if (baseName) { 282 | const oppositeBaseName = this.getOppositeBaseName(baseName); 283 | if (oppositeBaseName) { 284 | return this.camelCase( 285 | `${oppositeBaseName}-${this._singularizedTableName(table)}` 286 | ); 287 | } 288 | if (this.baseNameMatches(baseName, foreignTable.name)) { 289 | return this.camelCase(`${this._singularizedTableName(table)}`); 290 | } 291 | } 292 | if (isPrimaryKey(detailedKeys, table)) { 293 | return this.camelCase(`${this._singularizedTableName(table)}`); 294 | } 295 | return oldInflection.singleRelationByKeysBackwards( 296 | detailedKeys, 297 | table, 298 | foreignTable, 299 | constraint 300 | ); 301 | }, 302 | 303 | _manyRelationByKeysBase( 304 | detailedKeys: PgAttribute[], 305 | table: PgClass, 306 | _foreignTable: PgClass, 307 | _constraint: PgConstraint 308 | ) { 309 | const baseName = this.getBaseNameFromKeys(detailedKeys); 310 | if (baseName) { 311 | const oppositeBaseName = this.getOppositeBaseName(baseName); 312 | if (oppositeBaseName) { 313 | return this.camelCase( 314 | `${oppositeBaseName}-${this.distinctPluralize( 315 | this._singularizedTableName(table) 316 | )}` 317 | ); 318 | } 319 | if (this.baseNameMatches(baseName, _foreignTable.name)) { 320 | return this.camelCase( 321 | `${this.distinctPluralize(this._singularizedTableName(table))}` 322 | ); 323 | } 324 | } 325 | return null; 326 | }, 327 | 328 | manyRelationByKeys( 329 | detailedKeys: PgAttribute[], 330 | table: PgClass, 331 | foreignTable: PgClass, 332 | constraint: PgConstraint 333 | ) { 334 | if (constraint.tags.foreignFieldName) { 335 | if (constraint.tags.foreignSimpleFieldName) { 336 | return constraint.tags.foreignFieldName; 337 | } else { 338 | return ( 339 | constraint.tags.foreignFieldName + ConnectionSuffix(constraint) 340 | ); 341 | } 342 | } 343 | const base = this._manyRelationByKeysBase( 344 | detailedKeys, 345 | table, 346 | foreignTable, 347 | constraint 348 | ); 349 | if (base) { 350 | return base + ConnectionSuffix(constraint); 351 | } 352 | if (isPrimaryKey(detailedKeys, table)) { 353 | return ( 354 | this.camelCase( 355 | `${this.distinctPluralize(this._singularizedTableName(table))}` 356 | ) + ConnectionSuffix(constraint) 357 | ); 358 | } 359 | return ( 360 | oldInflection.manyRelationByKeys( 361 | detailedKeys, 362 | table, 363 | foreignTable, 364 | constraint 365 | ) + ConnectionSuffix(constraint) 366 | ); 367 | }, 368 | 369 | manyRelationByKeysSimple( 370 | detailedKeys: PgAttribute[], 371 | table: PgClass, 372 | foreignTable: PgClass, 373 | constraint: PgConstraint 374 | ) { 375 | if (constraint.tags.foreignSimpleFieldName) { 376 | return constraint.tags.foreignSimpleFieldName; 377 | } 378 | if (constraint.tags.foreignFieldName) { 379 | return constraint.tags.foreignFieldName + ListSuffix(constraint); 380 | } 381 | const base = this._manyRelationByKeysBase( 382 | detailedKeys, 383 | table, 384 | foreignTable, 385 | constraint 386 | ); 387 | if (base) { 388 | return base + ListSuffix(constraint); 389 | } 390 | if (isPrimaryKey(detailedKeys, table)) { 391 | return ( 392 | this.camelCase( 393 | `${this.distinctPluralize(this._singularizedTableName(table))}` 394 | ) + ListSuffix(constraint) 395 | ); 396 | } 397 | return ( 398 | oldInflection.manyRelationByKeys( 399 | detailedKeys, 400 | table, 401 | foreignTable, 402 | constraint 403 | ) + ListSuffix(constraint) 404 | ); 405 | }, 406 | 407 | functionQueryName(proc: PgProc) { 408 | return this.camelCase( 409 | this._functionName(proc) + 410 | (proc.returnsSet ? connectionSuffix(proc) : "") 411 | ); 412 | }, 413 | functionQueryNameList(proc: PgProc) { 414 | return this.camelCase(this._functionName(proc) + listSuffix(proc)); 415 | }, 416 | 417 | ...(pgShortPk 418 | ? { 419 | tableNode(table: PgClass) { 420 | return this.camelCase( 421 | `${this._singularizedTableName(table)}-by-${nodeIdFieldName}` 422 | ); 423 | }, 424 | rowByUniqueKeys( 425 | detailedKeys: string[], 426 | table: PgClass, 427 | constraint: PgConstraint 428 | ) { 429 | if (constraint.tags.fieldName) { 430 | return constraint.tags.fieldName; 431 | } 432 | if (constraint.type === "p") { 433 | // Primary key, shorten! 434 | return this.camelCase(this._singularizedTableName(table)); 435 | } else { 436 | return this.camelCase( 437 | `${this._singularizedTableName(table)}-by-${detailedKeys 438 | .map((key) => this.column(key)) 439 | .join("-and-")}` 440 | ); 441 | } 442 | }, 443 | 444 | updateByKeys( 445 | detailedKeys: string[], 446 | table: PgClass, 447 | constraint: PgConstraint 448 | ) { 449 | if (constraint.tags.updateFieldName) { 450 | return constraint.tags.updateFieldName; 451 | } 452 | if (constraint.type === "p") { 453 | return this.camelCase( 454 | `update-${this._singularizedTableName(table)}` 455 | ); 456 | } else { 457 | return this.camelCase( 458 | `update-${this._singularizedTableName( 459 | table 460 | )}-by-${detailedKeys 461 | .map((key) => this.column(key)) 462 | .join("-and-")}` 463 | ); 464 | } 465 | }, 466 | deleteByKeys( 467 | detailedKeys: string[], 468 | table: PgClass, 469 | constraint: PgConstraint 470 | ) { 471 | if (constraint.tags.deleteFieldName) { 472 | return constraint.tags.deleteFieldName; 473 | } 474 | if (constraint.type === "p") { 475 | // Primary key, shorten! 476 | return this.camelCase( 477 | `delete-${this._singularizedTableName(table)}` 478 | ); 479 | } else { 480 | return this.camelCase( 481 | `delete-${this._singularizedTableName( 482 | table 483 | )}-by-${detailedKeys 484 | .map((key) => this.column(key)) 485 | .join("-and-")}` 486 | ); 487 | } 488 | }, 489 | updateByKeysInputType( 490 | detailedKeys: PgAttribute[], 491 | table: PgClass, 492 | constraint: PgConstraint 493 | ) { 494 | if (constraint.tags.updateFieldName) { 495 | return this.upperCamelCase( 496 | `${constraint.tags.updateFieldName}-input` 497 | ); 498 | } 499 | if (constraint.type === "p") { 500 | // Primary key, shorten! 501 | return this.upperCamelCase( 502 | `update-${this._singularizedTableName(table)}-input` 503 | ); 504 | } else { 505 | return this.upperCamelCase( 506 | `update-${this._singularizedTableName( 507 | table 508 | )}-by-${detailedKeys 509 | .map((key) => this.column(key)) 510 | .join("-and-")}-input` 511 | ); 512 | } 513 | }, 514 | deleteByKeysInputType( 515 | detailedKeys: PgAttribute[], 516 | table: PgClass, 517 | constraint: PgConstraint 518 | ) { 519 | if (constraint.tags.deleteFieldName) { 520 | return this.upperCamelCase( 521 | `${constraint.tags.deleteFieldName}-input` 522 | ); 523 | } 524 | if (constraint.type === "p") { 525 | // Primary key, shorten! 526 | return this.upperCamelCase( 527 | `delete-${this._singularizedTableName(table)}-input` 528 | ); 529 | } else { 530 | return this.upperCamelCase( 531 | `delete-${this._singularizedTableName( 532 | table 533 | )}-by-${detailedKeys 534 | .map((key) => this.column(key)) 535 | .join("-and-")}-input` 536 | ); 537 | } 538 | }, 539 | updateNode(table: PgClass) { 540 | return this.camelCase( 541 | `update-${this._singularizedTableName( 542 | table 543 | )}-by-${nodeIdFieldName}` 544 | ); 545 | }, 546 | deleteNode(table: PgClass) { 547 | return this.camelCase( 548 | `delete-${this._singularizedTableName( 549 | table 550 | )}-by-${nodeIdFieldName}` 551 | ); 552 | }, 553 | updateNodeInputType(table: PgClass) { 554 | return this.upperCamelCase( 555 | `update-${this._singularizedTableName( 556 | table 557 | )}-by-${nodeIdFieldName}-input` 558 | ); 559 | }, 560 | deleteNodeInputType(table: PgClass) { 561 | return this.upperCamelCase( 562 | `delete-${this._singularizedTableName( 563 | table 564 | )}-by-${nodeIdFieldName}-input` 565 | ); 566 | }, 567 | } 568 | : null), 569 | }; 570 | }); 571 | } 572 | 573 | module.exports = PgSimplifyInflectorPlugin; 574 | // Hacks for TypeScript/Babel import 575 | module.exports.default = PgSimplifyInflectorPlugin; 576 | Object.defineProperty(module.exports, "__esModule", { value: true }); 577 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@graphile-contrib/pg-simplify-inflector", 3 | "version": "7.0.0-alpha.1", 4 | "description": "Simplifies the graphile-build-pg inflector to trim the `ByFooIdAndBarId` from relations", 5 | "main": "index.js", 6 | "scripts": { 7 | "prepack": "tsc && prettier --write index.js", 8 | "postgraphile": "postgraphile --append-plugins \"$(pwd)/index.js\"", 9 | "lint:fix": "yarn prettier --write", 10 | "prettier": "prettier --ignore-path ./.eslintignore '**/*.{js,jsx,ts,tsx,graphql,md,json,yml}'", 11 | "test": "node test.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/graphile-contrib/pg-simplify-inflector.git" 16 | }, 17 | "keywords": [ 18 | "graphile", 19 | "postgraphile", 20 | "plugin", 21 | "simple", 22 | "simplify", 23 | "inflection", 24 | "inflector", 25 | "pg" 26 | ], 27 | "author": "Benjie Gillam ", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/graphile-contrib/pg-simplify-inflector/issues" 31 | }, 32 | "homepage": "https://github.com/graphile-contrib/pg-simplify-inflector#readme", 33 | "files": [ 34 | "index.js", 35 | "index.d.ts" 36 | ], 37 | "devDependencies": { 38 | "@types/node": "^15.12.5", 39 | "postgraphile": "^4.12.3", 40 | "prettier": "^2.3.2", 41 | "typescript": "^4.3.5" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const pg = require("pg"); 3 | const fsp = require("fs").promises; 4 | const child_process = require("child_process"); 5 | const SimplifyPlugin = require("./index.js"); 6 | const { createPostGraphileSchema } = require("postgraphile"); 7 | const { printSchema } = require("graphql"); 8 | 9 | const ROOT_CONNECTION_STRING = "postgres"; 10 | const DATABASE_NAME = "pg_simplify_inflectors"; 11 | const ROOT = `${__dirname}/tests`; 12 | 13 | const BASE_SETTINGS = { 14 | simpleCollections: "both", 15 | }; 16 | 17 | const withPool = async (connectionString, cb) => { 18 | const pool = new pg.Pool({ 19 | connectionString, 20 | }); 21 | try { 22 | return await cb(pool); 23 | } finally { 24 | pool.end(); 25 | } 26 | }; 27 | 28 | const withClient = async (pool, cb) => { 29 | const client = await pool.connect(); 30 | try { 31 | return await cb(client); 32 | } finally { 33 | try { 34 | // Just in case there's any shenanigans 35 | await client.query("ROLLBACK"); 36 | await client.query("RESET ALL"); 37 | } catch (e) { 38 | /* IGNORE */ 39 | } 40 | 41 | await client.release(); 42 | } 43 | }; 44 | 45 | async function withCleanDb(cb) { 46 | await withPool(ROOT_CONNECTION_STRING, async (pool) => { 47 | await pool.query(`DROP DATABASE IF EXISTS ${DATABASE_NAME};`); 48 | await pool.query(`CREATE DATABASE ${DATABASE_NAME};`); 49 | }); 50 | await withPool(DATABASE_NAME, async (pool) => cb(pool)); 51 | } 52 | 53 | async function getSettings(dir) { 54 | try { 55 | const json = await fsp.readFile(`${ROOT}/${dir}/settings.json`, "utf8"); 56 | return JSON.parse(json); 57 | } catch { 58 | return {}; 59 | } 60 | } 61 | 62 | async function getSchema(client, withSimplify, settings) { 63 | return await createPostGraphileSchema(DATABASE_NAME, "app_public", { 64 | ...BASE_SETTINGS, 65 | ...settings, 66 | appendPlugins: withSimplify ? [SimplifyPlugin] : [], 67 | }); 68 | } 69 | 70 | async function runTests(pool, dir) { 71 | const schema = await fsp.readFile(`${ROOT}/${dir}/schema.sql`, "utf8"); 72 | const settings = await getSettings(dir); 73 | await withClient(pool, async (client) => { 74 | await client.query(` 75 | set search_path to public; 76 | create extension if not exists pgcrypto; 77 | 78 | drop schema if exists app_public cascade; 79 | create schema app_public; 80 | set search_path to app_public, public; 81 | `); 82 | await client.query(schema); 83 | 84 | const before = await getSchema(client, false, settings); 85 | const after = await getSchema(client, true, settings); 86 | const beforePath = `${ROOT}/${dir}/schema.unsimplified.graphql`; 87 | const afterPath = `${ROOT}/${dir}/schema.simplified.graphql`; 88 | const diffPath = `${ROOT}/${dir}/schema.graphql.diff`; 89 | await fsp.writeFile(beforePath, printSchema(before)); 90 | await fsp.writeFile(afterPath, printSchema(after)); 91 | 92 | const diff = await new Promise((resolve, reject) => { 93 | const child = child_process.spawn( 94 | "diff", 95 | [ 96 | "-u", 97 | "--label", 98 | "unsimplified", 99 | beforePath, 100 | "--label", 101 | "simplified", 102 | afterPath, 103 | ], 104 | { 105 | stdio: "pipe", 106 | } 107 | ); 108 | const buffers = []; 109 | 110 | child.stdout.on("data", (data) => { 111 | buffers.push(data); 112 | }); 113 | 114 | child.stderr.on("data", (data) => { 115 | console.error(`stderr: ${data}`); 116 | }); 117 | 118 | child.on("close", (code) => { 119 | const data = Buffer.concat(buffers).toString("utf8"); 120 | if (data.length) { 121 | resolve(data); 122 | } else { 123 | reject(new Error(`child process exited with code ${code}`)); 124 | } 125 | }); 126 | }); 127 | 128 | await fsp.writeFile(diffPath, diff); 129 | }); 130 | } 131 | 132 | async function main() { 133 | const dirs = await fsp.readdir(ROOT); 134 | for (const dir of dirs) { 135 | const stat = await fsp.stat(`${ROOT}/${dir}`); 136 | if (stat.isDirectory()) { 137 | if (/^[a-z0-9_]+$/.test(dir)) { 138 | console.log(dir); 139 | await withCleanDb((pool) => runTests(pool, dir)); 140 | } else { 141 | console.warn( 142 | `Skipping '${dir}' because it does not adhere to the naming convention` 143 | ); 144 | } 145 | } 146 | } 147 | } 148 | 149 | withPool("postgres", main).catch((e) => { 150 | console.error(e); 151 | process.exit(1); 152 | }); 153 | -------------------------------------------------------------------------------- /tests/animal/schema.sql: -------------------------------------------------------------------------------- 1 | create table animal ( 2 | id serial primary key 3 | ); 4 | 5 | create table dog ( 6 | id integer primary key references animal 7 | ); 8 | 9 | create table cat ( 10 | id integer primary key references animal 11 | ); 12 | 13 | create table gerbil ( 14 | animal_id integer primary key references animal 15 | ); 16 | -------------------------------------------------------------------------------- /tests/companies/schema.graphql.diff: -------------------------------------------------------------------------------- 1 | --- unsimplified 2 | +++ simplified 3 | @@ -18,7 +18,7 @@ 4 | ): Node 5 | 6 | """Reads and enables pagination through a set of `Beverage`.""" 7 | - allBeverages( 8 | + beverages( 9 | """Only read the first `n` values of the set.""" 10 | first: Int 11 | 12 | @@ -47,7 +47,7 @@ 13 | ): BeveragesConnection 14 | 15 | """Reads a set of `Beverage`.""" 16 | - allBeveragesList( 17 | + beveragesList( 18 | """Only read the first `n` values of the set.""" 19 | first: Int 20 | 21 | @@ -64,7 +64,7 @@ 22 | ): [Beverage!] 23 | 24 | """Reads and enables pagination through a set of `Company`.""" 25 | - allCompanies( 26 | + companies( 27 | """Only read the first `n` values of the set.""" 28 | first: Int 29 | 30 | @@ -93,7 +93,7 @@ 31 | ): CompaniesConnection 32 | 33 | """Reads a set of `Company`.""" 34 | - allCompaniesList( 35 | + companiesList( 36 | """Only read the first `n` values of the set.""" 37 | first: Int 38 | 39 | @@ -110,7 +110,7 @@ 40 | ): [Company!] 41 | 42 | """Reads and enables pagination through a set of `Mascot`.""" 43 | - allMascots( 44 | + mascots( 45 | """Only read the first `n` values of the set.""" 46 | first: Int 47 | 48 | @@ -139,7 +139,7 @@ 49 | ): MascotsConnection 50 | 51 | """Reads a set of `Mascot`.""" 52 | - allMascotsList( 53 | + mascotsList( 54 | """Only read the first `n` values of the set.""" 55 | first: Int 56 | 57 | @@ -154,25 +154,25 @@ 58 | """ 59 | condition: MascotCondition 60 | ): [Mascot!] 61 | - beverageById(id: Int!): Beverage 62 | - companyById(id: Int!): Company 63 | - mascotById(id: Int!): Mascot 64 | + beverage(id: Int!): Beverage 65 | + company(id: Int!): Company 66 | + mascot(id: Int!): Mascot 67 | mascotByCompanyId(companyId: Int!): Mascot 68 | 69 | """Reads a single `Beverage` using its globally unique `ID`.""" 70 | - beverage( 71 | + beverageByNodeId( 72 | """The globally unique `ID` to be used in selecting a single `Beverage`.""" 73 | nodeId: ID! 74 | ): Beverage 75 | 76 | """Reads a single `Company` using its globally unique `ID`.""" 77 | - company( 78 | + companyByNodeId( 79 | """The globally unique `ID` to be used in selecting a single `Company`.""" 80 | nodeId: ID! 81 | ): Company 82 | 83 | """Reads a single `Mascot` using its globally unique `ID`.""" 84 | - mascot( 85 | + mascotByNodeId( 86 | """The globally unique `ID` to be used in selecting a single `Mascot`.""" 87 | nodeId: ID! 88 | ): Mascot 89 | @@ -214,10 +214,10 @@ 90 | name: String! 91 | 92 | """Reads a single `Company` that is related to this `Beverage`.""" 93 | - companyByCompanyId: Company 94 | + company: Company 95 | 96 | """Reads a single `Company` that is related to this `Beverage`.""" 97 | - companyByDistributorId: Company 98 | + distributor: Company 99 | } 100 | 101 | type Company implements Node { 102 | @@ -229,7 +229,7 @@ 103 | name: String! 104 | 105 | """Reads and enables pagination through a set of `Beverage`.""" 106 | - beveragesByCompanyId( 107 | + beverages( 108 | """Only read the first `n` values of the set.""" 109 | first: Int 110 | 111 | @@ -258,7 +258,7 @@ 112 | ): BeveragesConnection! 113 | 114 | """Reads and enables pagination through a set of `Beverage`.""" 115 | - beveragesByCompanyIdList( 116 | + beveragesList( 117 | """Only read the first `n` values of the set.""" 118 | first: Int 119 | 120 | @@ -321,10 +321,10 @@ 121 | ): [Beverage!]! 122 | 123 | """Reads a single `Mascot` that is related to this `Company`.""" 124 | - mascotByCompanyId: Mascot 125 | + mascot: Mascot 126 | 127 | """Reads and enables pagination through a set of `Mascot`.""" 128 | - mascotsByCompanyId( 129 | + mascots( 130 | """Only read the first `n` values of the set.""" 131 | first: Int 132 | 133 | @@ -350,7 +350,7 @@ 134 | A condition to be used in determining which values should be returned by the collection. 135 | """ 136 | condition: MascotCondition 137 | - ): MascotsConnection! @deprecated(reason: "Please use mascotByCompanyId instead") 138 | + ): MascotsConnection! @deprecated(reason: "Please use mascot instead") 139 | } 140 | 141 | """A location in a connection that can be used for resuming pagination.""" 142 | @@ -399,7 +399,7 @@ 143 | name: String! 144 | 145 | """Reads a single `Company` that is related to this `Mascot`.""" 146 | - companyByCompanyId: Company 147 | + company: Company 148 | } 149 | 150 | """A connection to a list of `Mascot` values.""" 151 | @@ -556,51 +556,51 @@ 152 | ): CreateMascotPayload 153 | 154 | """Updates a single `Beverage` using its globally unique id and a patch.""" 155 | - updateBeverage( 156 | + updateBeverageByNodeId( 157 | """ 158 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 159 | """ 160 | - input: UpdateBeverageInput! 161 | + input: UpdateBeverageByNodeIdInput! 162 | ): UpdateBeveragePayload 163 | 164 | """Updates a single `Beverage` using a unique key and a patch.""" 165 | - updateBeverageById( 166 | + updateBeverage( 167 | """ 168 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 169 | """ 170 | - input: UpdateBeverageByIdInput! 171 | + input: UpdateBeverageInput! 172 | ): UpdateBeveragePayload 173 | 174 | """Updates a single `Company` using its globally unique id and a patch.""" 175 | - updateCompany( 176 | + updateCompanyByNodeId( 177 | """ 178 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 179 | """ 180 | - input: UpdateCompanyInput! 181 | + input: UpdateCompanyByNodeIdInput! 182 | ): UpdateCompanyPayload 183 | 184 | """Updates a single `Company` using a unique key and a patch.""" 185 | - updateCompanyById( 186 | + updateCompany( 187 | """ 188 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 189 | """ 190 | - input: UpdateCompanyByIdInput! 191 | + input: UpdateCompanyInput! 192 | ): UpdateCompanyPayload 193 | 194 | """Updates a single `Mascot` using its globally unique id and a patch.""" 195 | - updateMascot( 196 | + updateMascotByNodeId( 197 | """ 198 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 199 | """ 200 | - input: UpdateMascotInput! 201 | + input: UpdateMascotByNodeIdInput! 202 | ): UpdateMascotPayload 203 | 204 | """Updates a single `Mascot` using a unique key and a patch.""" 205 | - updateMascotById( 206 | + updateMascot( 207 | """ 208 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 209 | """ 210 | - input: UpdateMascotByIdInput! 211 | + input: UpdateMascotInput! 212 | ): UpdateMascotPayload 213 | 214 | """Updates a single `Mascot` using a unique key and a patch.""" 215 | @@ -612,51 +612,51 @@ 216 | ): UpdateMascotPayload 217 | 218 | """Deletes a single `Beverage` using its globally unique id.""" 219 | - deleteBeverage( 220 | + deleteBeverageByNodeId( 221 | """ 222 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 223 | """ 224 | - input: DeleteBeverageInput! 225 | + input: DeleteBeverageByNodeIdInput! 226 | ): DeleteBeveragePayload 227 | 228 | """Deletes a single `Beverage` using a unique key.""" 229 | - deleteBeverageById( 230 | + deleteBeverage( 231 | """ 232 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 233 | """ 234 | - input: DeleteBeverageByIdInput! 235 | + input: DeleteBeverageInput! 236 | ): DeleteBeveragePayload 237 | 238 | """Deletes a single `Company` using its globally unique id.""" 239 | - deleteCompany( 240 | + deleteCompanyByNodeId( 241 | """ 242 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 243 | """ 244 | - input: DeleteCompanyInput! 245 | + input: DeleteCompanyByNodeIdInput! 246 | ): DeleteCompanyPayload 247 | 248 | """Deletes a single `Company` using a unique key.""" 249 | - deleteCompanyById( 250 | + deleteCompany( 251 | """ 252 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 253 | """ 254 | - input: DeleteCompanyByIdInput! 255 | + input: DeleteCompanyInput! 256 | ): DeleteCompanyPayload 257 | 258 | """Deletes a single `Mascot` using its globally unique id.""" 259 | - deleteMascot( 260 | + deleteMascotByNodeId( 261 | """ 262 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 263 | """ 264 | - input: DeleteMascotInput! 265 | + input: DeleteMascotByNodeIdInput! 266 | ): DeleteMascotPayload 267 | 268 | """Deletes a single `Mascot` using a unique key.""" 269 | - deleteMascotById( 270 | + deleteMascot( 271 | """ 272 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 273 | """ 274 | - input: DeleteMascotByIdInput! 275 | + input: DeleteMascotInput! 276 | ): DeleteMascotPayload 277 | 278 | """Deletes a single `Mascot` using a unique key.""" 279 | @@ -685,10 +685,10 @@ 280 | query: Query 281 | 282 | """Reads a single `Company` that is related to this `Beverage`.""" 283 | - companyByCompanyId: Company 284 | + company: Company 285 | 286 | """Reads a single `Company` that is related to this `Beverage`.""" 287 | - companyByDistributorId: Company 288 | + distributor: Company 289 | 290 | """An edge for our `Beverage`. May be used by Relay 1.""" 291 | beverageEdge( 292 | @@ -775,7 +775,7 @@ 293 | query: Query 294 | 295 | """Reads a single `Company` that is related to this `Mascot`.""" 296 | - companyByCompanyId: Company 297 | + company: Company 298 | 299 | """An edge for our `Mascot`. May be used by Relay 1.""" 300 | mascotEdge( 301 | @@ -820,10 +820,10 @@ 302 | query: Query 303 | 304 | """Reads a single `Company` that is related to this `Beverage`.""" 305 | - companyByCompanyId: Company 306 | + company: Company 307 | 308 | """Reads a single `Company` that is related to this `Beverage`.""" 309 | - companyByDistributorId: Company 310 | + distributor: Company 311 | 312 | """An edge for our `Beverage`. May be used by Relay 1.""" 313 | beverageEdge( 314 | @@ -832,8 +832,8 @@ 315 | ): BeveragesEdge 316 | } 317 | 318 | -"""All input for the `updateBeverage` mutation.""" 319 | -input UpdateBeverageInput { 320 | +"""All input for the `updateBeverageByNodeId` mutation.""" 321 | +input UpdateBeverageByNodeIdInput { 322 | """ 323 | An arbitrary string value with no semantic meaning. Will be included in the 324 | payload verbatim. May be used to track mutations by the client. 325 | @@ -848,7 +848,7 @@ 326 | """ 327 | An object where the defined keys will be set on the `Beverage` being updated. 328 | """ 329 | - beveragePatch: BeveragePatch! 330 | + patch: BeveragePatch! 331 | } 332 | 333 | """ 334 | @@ -861,8 +861,8 @@ 335 | name: String 336 | } 337 | 338 | -"""All input for the `updateBeverageById` mutation.""" 339 | -input UpdateBeverageByIdInput { 340 | +"""All input for the `updateBeverage` mutation.""" 341 | +input UpdateBeverageInput { 342 | """ 343 | An arbitrary string value with no semantic meaning. Will be included in the 344 | payload verbatim. May be used to track mutations by the client. 345 | @@ -872,7 +872,7 @@ 346 | """ 347 | An object where the defined keys will be set on the `Beverage` being updated. 348 | """ 349 | - beveragePatch: BeveragePatch! 350 | + patch: BeveragePatch! 351 | id: Int! 352 | } 353 | 354 | @@ -899,8 +899,8 @@ 355 | ): CompaniesEdge 356 | } 357 | 358 | -"""All input for the `updateCompany` mutation.""" 359 | -input UpdateCompanyInput { 360 | +"""All input for the `updateCompanyByNodeId` mutation.""" 361 | +input UpdateCompanyByNodeIdInput { 362 | """ 363 | An arbitrary string value with no semantic meaning. Will be included in the 364 | payload verbatim. May be used to track mutations by the client. 365 | @@ -915,7 +915,7 @@ 366 | """ 367 | An object where the defined keys will be set on the `Company` being updated. 368 | """ 369 | - companyPatch: CompanyPatch! 370 | + patch: CompanyPatch! 371 | } 372 | 373 | """ 374 | @@ -926,8 +926,8 @@ 375 | name: String 376 | } 377 | 378 | -"""All input for the `updateCompanyById` mutation.""" 379 | -input UpdateCompanyByIdInput { 380 | +"""All input for the `updateCompany` mutation.""" 381 | +input UpdateCompanyInput { 382 | """ 383 | An arbitrary string value with no semantic meaning. Will be included in the 384 | payload verbatim. May be used to track mutations by the client. 385 | @@ -937,7 +937,7 @@ 386 | """ 387 | An object where the defined keys will be set on the `Company` being updated. 388 | """ 389 | - companyPatch: CompanyPatch! 390 | + patch: CompanyPatch! 391 | id: Int! 392 | } 393 | 394 | @@ -958,7 +958,7 @@ 395 | query: Query 396 | 397 | """Reads a single `Company` that is related to this `Mascot`.""" 398 | - companyByCompanyId: Company 399 | + company: Company 400 | 401 | """An edge for our `Mascot`. May be used by Relay 1.""" 402 | mascotEdge( 403 | @@ -967,8 +967,8 @@ 404 | ): MascotsEdge 405 | } 406 | 407 | -"""All input for the `updateMascot` mutation.""" 408 | -input UpdateMascotInput { 409 | +"""All input for the `updateMascotByNodeId` mutation.""" 410 | +input UpdateMascotByNodeIdInput { 411 | """ 412 | An arbitrary string value with no semantic meaning. Will be included in the 413 | payload verbatim. May be used to track mutations by the client. 414 | @@ -983,7 +983,7 @@ 415 | """ 416 | An object where the defined keys will be set on the `Mascot` being updated. 417 | """ 418 | - mascotPatch: MascotPatch! 419 | + patch: MascotPatch! 420 | } 421 | 422 | """ 423 | @@ -995,8 +995,8 @@ 424 | name: String 425 | } 426 | 427 | -"""All input for the `updateMascotById` mutation.""" 428 | -input UpdateMascotByIdInput { 429 | +"""All input for the `updateMascot` mutation.""" 430 | +input UpdateMascotInput { 431 | """ 432 | An arbitrary string value with no semantic meaning. Will be included in the 433 | payload verbatim. May be used to track mutations by the client. 434 | @@ -1006,7 +1006,7 @@ 435 | """ 436 | An object where the defined keys will be set on the `Mascot` being updated. 437 | """ 438 | - mascotPatch: MascotPatch! 439 | + patch: MascotPatch! 440 | id: Int! 441 | } 442 | 443 | @@ -1021,7 +1021,7 @@ 444 | """ 445 | An object where the defined keys will be set on the `Mascot` being updated. 446 | """ 447 | - mascotPatch: MascotPatch! 448 | + patch: MascotPatch! 449 | companyId: Int! 450 | } 451 | 452 | @@ -1035,7 +1035,7 @@ 453 | 454 | """The `Beverage` that was deleted by this mutation.""" 455 | beverage: Beverage 456 | - deletedBeverageId: ID 457 | + deletedBeverageNodeId: ID 458 | 459 | """ 460 | Our root query field type. Allows us to run any query from our mutation payload. 461 | @@ -1043,10 +1043,10 @@ 462 | query: Query 463 | 464 | """Reads a single `Company` that is related to this `Beverage`.""" 465 | - companyByCompanyId: Company 466 | + company: Company 467 | 468 | """Reads a single `Company` that is related to this `Beverage`.""" 469 | - companyByDistributorId: Company 470 | + distributor: Company 471 | 472 | """An edge for our `Beverage`. May be used by Relay 1.""" 473 | beverageEdge( 474 | @@ -1055,8 +1055,8 @@ 475 | ): BeveragesEdge 476 | } 477 | 478 | -"""All input for the `deleteBeverage` mutation.""" 479 | -input DeleteBeverageInput { 480 | +"""All input for the `deleteBeverageByNodeId` mutation.""" 481 | +input DeleteBeverageByNodeIdInput { 482 | """ 483 | An arbitrary string value with no semantic meaning. Will be included in the 484 | payload verbatim. May be used to track mutations by the client. 485 | @@ -1069,8 +1069,8 @@ 486 | nodeId: ID! 487 | } 488 | 489 | -"""All input for the `deleteBeverageById` mutation.""" 490 | -input DeleteBeverageByIdInput { 491 | +"""All input for the `deleteBeverage` mutation.""" 492 | +input DeleteBeverageInput { 493 | """ 494 | An arbitrary string value with no semantic meaning. Will be included in the 495 | payload verbatim. May be used to track mutations by the client. 496 | @@ -1089,7 +1089,7 @@ 497 | 498 | """The `Company` that was deleted by this mutation.""" 499 | company: Company 500 | - deletedCompanyId: ID 501 | + deletedCompanyNodeId: ID 502 | 503 | """ 504 | Our root query field type. Allows us to run any query from our mutation payload. 505 | @@ -1103,8 +1103,8 @@ 506 | ): CompaniesEdge 507 | } 508 | 509 | -"""All input for the `deleteCompany` mutation.""" 510 | -input DeleteCompanyInput { 511 | +"""All input for the `deleteCompanyByNodeId` mutation.""" 512 | +input DeleteCompanyByNodeIdInput { 513 | """ 514 | An arbitrary string value with no semantic meaning. Will be included in the 515 | payload verbatim. May be used to track mutations by the client. 516 | @@ -1117,8 +1117,8 @@ 517 | nodeId: ID! 518 | } 519 | 520 | -"""All input for the `deleteCompanyById` mutation.""" 521 | -input DeleteCompanyByIdInput { 522 | +"""All input for the `deleteCompany` mutation.""" 523 | +input DeleteCompanyInput { 524 | """ 525 | An arbitrary string value with no semantic meaning. Will be included in the 526 | payload verbatim. May be used to track mutations by the client. 527 | @@ -1137,7 +1137,7 @@ 528 | 529 | """The `Mascot` that was deleted by this mutation.""" 530 | mascot: Mascot 531 | - deletedMascotId: ID 532 | + deletedMascotNodeId: ID 533 | 534 | """ 535 | Our root query field type. Allows us to run any query from our mutation payload. 536 | @@ -1145,7 +1145,7 @@ 537 | query: Query 538 | 539 | """Reads a single `Company` that is related to this `Mascot`.""" 540 | - companyByCompanyId: Company 541 | + company: Company 542 | 543 | """An edge for our `Mascot`. May be used by Relay 1.""" 544 | mascotEdge( 545 | @@ -1154,8 +1154,8 @@ 546 | ): MascotsEdge 547 | } 548 | 549 | -"""All input for the `deleteMascot` mutation.""" 550 | -input DeleteMascotInput { 551 | +"""All input for the `deleteMascotByNodeId` mutation.""" 552 | +input DeleteMascotByNodeIdInput { 553 | """ 554 | An arbitrary string value with no semantic meaning. Will be included in the 555 | payload verbatim. May be used to track mutations by the client. 556 | @@ -1168,8 +1168,8 @@ 557 | nodeId: ID! 558 | } 559 | 560 | -"""All input for the `deleteMascotById` mutation.""" 561 | -input DeleteMascotByIdInput { 562 | +"""All input for the `deleteMascot` mutation.""" 563 | +input DeleteMascotInput { 564 | """ 565 | An arbitrary string value with no semantic meaning. Will be included in the 566 | payload verbatim. May be used to track mutations by the client. 567 | -------------------------------------------------------------------------------- /tests/companies/schema.sql: -------------------------------------------------------------------------------- 1 | create table companies ( 2 | id serial primary key, 3 | name text not null 4 | ); 5 | 6 | create table beverages ( 7 | id serial primary key, 8 | company_id int not null references companies, 9 | distributor_id int references companies, 10 | name text not null 11 | ); 12 | comment on constraint "beverages_distributor_id_fkey" on "beverages" is 13 | E'@foreignFieldName distributedBeverages\n@foreignSimpleFieldName distributedBeveragesList'; 14 | 15 | create table mascots ( 16 | id serial primary key, 17 | company_id int unique not null references companies, 18 | name text not null 19 | ); 20 | 21 | -------------------------------------------------------------------------------- /tests/fish/schema.graphql.diff: -------------------------------------------------------------------------------- 1 | --- unsimplified 2 | +++ simplified 3 | @@ -18,7 +18,7 @@ 4 | ): Node 5 | 6 | """Reads and enables pagination through a set of `Fish`.""" 7 | - allFish( 8 | + fishes( 9 | """Only read the first `n` values of the set.""" 10 | first: Int 11 | 12 | @@ -47,7 +47,7 @@ 13 | ): FishConnection 14 | 15 | """Reads a set of `Fish`.""" 16 | - allFishList( 17 | + fishesList( 18 | """Only read the first `n` values of the set.""" 19 | first: Int 20 | 21 | @@ -64,7 +64,7 @@ 22 | ): [Fish!] 23 | 24 | """Reads and enables pagination through a set of `Pond`.""" 25 | - allPonds( 26 | + ponds( 27 | """Only read the first `n` values of the set.""" 28 | first: Int 29 | 30 | @@ -93,7 +93,7 @@ 31 | ): PondsConnection 32 | 33 | """Reads a set of `Pond`.""" 34 | - allPondsList( 35 | + pondsList( 36 | """Only read the first `n` values of the set.""" 37 | first: Int 38 | 39 | @@ -108,17 +108,17 @@ 40 | """ 41 | condition: PondCondition 42 | ): [Pond!] 43 | - fishById(id: Int!): Fish 44 | - pondById(id: Int!): Pond 45 | + fish(id: Int!): Fish 46 | + pond(id: Int!): Pond 47 | 48 | """Reads a single `Fish` using its globally unique `ID`.""" 49 | - fish( 50 | + fishByNodeId( 51 | """The globally unique `ID` to be used in selecting a single `Fish`.""" 52 | nodeId: ID! 53 | ): Fish 54 | 55 | """Reads a single `Pond` using its globally unique `ID`.""" 56 | - pond( 57 | + pondByNodeId( 58 | """The globally unique `ID` to be used in selecting a single `Pond`.""" 59 | nodeId: ID! 60 | ): Pond 61 | @@ -159,7 +159,7 @@ 62 | name: String! 63 | 64 | """Reads a single `Pond` that is related to this `Fish`.""" 65 | - pondByPondId: Pond 66 | + pond: Pond 67 | } 68 | 69 | type Pond implements Node { 70 | @@ -171,7 +171,7 @@ 71 | name: String! 72 | 73 | """Reads and enables pagination through a set of `Fish`.""" 74 | - fishByPondId( 75 | + fishes( 76 | """Only read the first `n` values of the set.""" 77 | first: Int 78 | 79 | @@ -200,7 +200,7 @@ 80 | ): FishConnection! 81 | 82 | """Reads and enables pagination through a set of `Fish`.""" 83 | - fishByPondIdList( 84 | + fishesList( 85 | """Only read the first `n` values of the set.""" 86 | first: Int 87 | 88 | @@ -340,67 +340,67 @@ 89 | ): CreatePondPayload 90 | 91 | """Updates a single `Fish` using its globally unique id and a patch.""" 92 | - updateFish( 93 | + updateFishByNodeId( 94 | """ 95 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 96 | """ 97 | - input: UpdateFishInput! 98 | + input: UpdateFishByNodeIdInput! 99 | ): UpdateFishPayload 100 | 101 | """Updates a single `Fish` using a unique key and a patch.""" 102 | - updateFishById( 103 | + updateFish( 104 | """ 105 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 106 | """ 107 | - input: UpdateFishByIdInput! 108 | + input: UpdateFishInput! 109 | ): UpdateFishPayload 110 | 111 | """Updates a single `Pond` using its globally unique id and a patch.""" 112 | - updatePond( 113 | + updatePondByNodeId( 114 | """ 115 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 116 | """ 117 | - input: UpdatePondInput! 118 | + input: UpdatePondByNodeIdInput! 119 | ): UpdatePondPayload 120 | 121 | """Updates a single `Pond` using a unique key and a patch.""" 122 | - updatePondById( 123 | + updatePond( 124 | """ 125 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 126 | """ 127 | - input: UpdatePondByIdInput! 128 | + input: UpdatePondInput! 129 | ): UpdatePondPayload 130 | 131 | """Deletes a single `Fish` using its globally unique id.""" 132 | - deleteFish( 133 | + deleteFishByNodeId( 134 | """ 135 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 136 | """ 137 | - input: DeleteFishInput! 138 | + input: DeleteFishByNodeIdInput! 139 | ): DeleteFishPayload 140 | 141 | """Deletes a single `Fish` using a unique key.""" 142 | - deleteFishById( 143 | + deleteFish( 144 | """ 145 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 146 | """ 147 | - input: DeleteFishByIdInput! 148 | + input: DeleteFishInput! 149 | ): DeleteFishPayload 150 | 151 | """Deletes a single `Pond` using its globally unique id.""" 152 | - deletePond( 153 | + deletePondByNodeId( 154 | """ 155 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 156 | """ 157 | - input: DeletePondInput! 158 | + input: DeletePondByNodeIdInput! 159 | ): DeletePondPayload 160 | 161 | """Deletes a single `Pond` using a unique key.""" 162 | - deletePondById( 163 | + deletePond( 164 | """ 165 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 166 | """ 167 | - input: DeletePondByIdInput! 168 | + input: DeletePondInput! 169 | ): DeletePondPayload 170 | } 171 | 172 | @@ -421,7 +421,7 @@ 173 | query: Query 174 | 175 | """Reads a single `Pond` that is related to this `Fish`.""" 176 | - pondByPondId: Pond 177 | + pond: Pond 178 | 179 | """An edge for our `Fish`. May be used by Relay 1.""" 180 | fishEdge( 181 | @@ -507,7 +507,7 @@ 182 | query: Query 183 | 184 | """Reads a single `Pond` that is related to this `Fish`.""" 185 | - pondByPondId: Pond 186 | + pond: Pond 187 | 188 | """An edge for our `Fish`. May be used by Relay 1.""" 189 | fishEdge( 190 | @@ -516,8 +516,8 @@ 191 | ): FishEdge 192 | } 193 | 194 | -"""All input for the `updateFish` mutation.""" 195 | -input UpdateFishInput { 196 | +"""All input for the `updateFishByNodeId` mutation.""" 197 | +input UpdateFishByNodeIdInput { 198 | """ 199 | An arbitrary string value with no semantic meaning. Will be included in the 200 | payload verbatim. May be used to track mutations by the client. 201 | @@ -532,7 +532,7 @@ 202 | """ 203 | An object where the defined keys will be set on the `Fish` being updated. 204 | """ 205 | - fishPatch: FishPatch! 206 | + patch: FishPatch! 207 | } 208 | 209 | """Represents an update to a `Fish`. Fields that are set will be updated.""" 210 | @@ -542,8 +542,8 @@ 211 | name: String 212 | } 213 | 214 | -"""All input for the `updateFishById` mutation.""" 215 | -input UpdateFishByIdInput { 216 | +"""All input for the `updateFish` mutation.""" 217 | +input UpdateFishInput { 218 | """ 219 | An arbitrary string value with no semantic meaning. Will be included in the 220 | payload verbatim. May be used to track mutations by the client. 221 | @@ -553,7 +553,7 @@ 222 | """ 223 | An object where the defined keys will be set on the `Fish` being updated. 224 | """ 225 | - fishPatch: FishPatch! 226 | + patch: FishPatch! 227 | id: Int! 228 | } 229 | 230 | @@ -580,8 +580,8 @@ 231 | ): PondsEdge 232 | } 233 | 234 | -"""All input for the `updatePond` mutation.""" 235 | -input UpdatePondInput { 236 | +"""All input for the `updatePondByNodeId` mutation.""" 237 | +input UpdatePondByNodeIdInput { 238 | """ 239 | An arbitrary string value with no semantic meaning. Will be included in the 240 | payload verbatim. May be used to track mutations by the client. 241 | @@ -596,7 +596,7 @@ 242 | """ 243 | An object where the defined keys will be set on the `Pond` being updated. 244 | """ 245 | - pondPatch: PondPatch! 246 | + patch: PondPatch! 247 | } 248 | 249 | """Represents an update to a `Pond`. Fields that are set will be updated.""" 250 | @@ -605,8 +605,8 @@ 251 | name: String 252 | } 253 | 254 | -"""All input for the `updatePondById` mutation.""" 255 | -input UpdatePondByIdInput { 256 | +"""All input for the `updatePond` mutation.""" 257 | +input UpdatePondInput { 258 | """ 259 | An arbitrary string value with no semantic meaning. Will be included in the 260 | payload verbatim. May be used to track mutations by the client. 261 | @@ -616,7 +616,7 @@ 262 | """ 263 | An object where the defined keys will be set on the `Pond` being updated. 264 | """ 265 | - pondPatch: PondPatch! 266 | + patch: PondPatch! 267 | id: Int! 268 | } 269 | 270 | @@ -630,7 +630,7 @@ 271 | 272 | """The `Fish` that was deleted by this mutation.""" 273 | fish: Fish 274 | - deletedFishId: ID 275 | + deletedFishNodeId: ID 276 | 277 | """ 278 | Our root query field type. Allows us to run any query from our mutation payload. 279 | @@ -638,7 +638,7 @@ 280 | query: Query 281 | 282 | """Reads a single `Pond` that is related to this `Fish`.""" 283 | - pondByPondId: Pond 284 | + pond: Pond 285 | 286 | """An edge for our `Fish`. May be used by Relay 1.""" 287 | fishEdge( 288 | @@ -647,8 +647,8 @@ 289 | ): FishEdge 290 | } 291 | 292 | -"""All input for the `deleteFish` mutation.""" 293 | -input DeleteFishInput { 294 | +"""All input for the `deleteFishByNodeId` mutation.""" 295 | +input DeleteFishByNodeIdInput { 296 | """ 297 | An arbitrary string value with no semantic meaning. Will be included in the 298 | payload verbatim. May be used to track mutations by the client. 299 | @@ -661,8 +661,8 @@ 300 | nodeId: ID! 301 | } 302 | 303 | -"""All input for the `deleteFishById` mutation.""" 304 | -input DeleteFishByIdInput { 305 | +"""All input for the `deleteFish` mutation.""" 306 | +input DeleteFishInput { 307 | """ 308 | An arbitrary string value with no semantic meaning. Will be included in the 309 | payload verbatim. May be used to track mutations by the client. 310 | @@ -681,7 +681,7 @@ 311 | 312 | """The `Pond` that was deleted by this mutation.""" 313 | pond: Pond 314 | - deletedPondId: ID 315 | + deletedPondNodeId: ID 316 | 317 | """ 318 | Our root query field type. Allows us to run any query from our mutation payload. 319 | @@ -695,8 +695,8 @@ 320 | ): PondsEdge 321 | } 322 | 323 | -"""All input for the `deletePond` mutation.""" 324 | -input DeletePondInput { 325 | +"""All input for the `deletePondByNodeId` mutation.""" 326 | +input DeletePondByNodeIdInput { 327 | """ 328 | An arbitrary string value with no semantic meaning. Will be included in the 329 | payload verbatim. May be used to track mutations by the client. 330 | @@ -709,8 +709,8 @@ 331 | nodeId: ID! 332 | } 333 | 334 | -"""All input for the `deletePondById` mutation.""" 335 | -input DeletePondByIdInput { 336 | +"""All input for the `deletePond` mutation.""" 337 | +input DeletePondInput { 338 | """ 339 | An arbitrary string value with no semantic meaning. Will be included in the 340 | payload verbatim. May be used to track mutations by the client. 341 | -------------------------------------------------------------------------------- /tests/fish/schema.simplified.graphql: -------------------------------------------------------------------------------- 1 | """The root query type which gives access points into the data universe.""" 2 | type Query implements Node { 3 | """ 4 | Exposes the root query type nested one level down. This is helpful for Relay 1 5 | which can only query top level fields if they are in a particular form. 6 | """ 7 | query: Query! 8 | 9 | """ 10 | The root query type must be a `Node` to work well with Relay 1 mutations. This just resolves to `query`. 11 | """ 12 | nodeId: ID! 13 | 14 | """Fetches an object given its globally unique `ID`.""" 15 | node( 16 | """The globally unique `ID`.""" 17 | nodeId: ID! 18 | ): Node 19 | 20 | """Reads and enables pagination through a set of `Fish`.""" 21 | fishes( 22 | """Only read the first `n` values of the set.""" 23 | first: Int 24 | 25 | """Only read the last `n` values of the set.""" 26 | last: Int 27 | 28 | """ 29 | Skip the first `n` values from our `after` cursor, an alternative to cursor 30 | based pagination. May not be used with `last`. 31 | """ 32 | offset: Int 33 | 34 | """Read all values in the set before (above) this cursor.""" 35 | before: Cursor 36 | 37 | """Read all values in the set after (below) this cursor.""" 38 | after: Cursor 39 | 40 | """The method to use when ordering `Fish`.""" 41 | orderBy: [FishOrderBy!] = [PRIMARY_KEY_ASC] 42 | 43 | """ 44 | A condition to be used in determining which values should be returned by the collection. 45 | """ 46 | condition: FishCondition 47 | ): FishConnection 48 | 49 | """Reads a set of `Fish`.""" 50 | fishesList( 51 | """Only read the first `n` values of the set.""" 52 | first: Int 53 | 54 | """Skip the first `n` values.""" 55 | offset: Int 56 | 57 | """The method to use when ordering `Fish`.""" 58 | orderBy: [FishOrderBy!] 59 | 60 | """ 61 | A condition to be used in determining which values should be returned by the collection. 62 | """ 63 | condition: FishCondition 64 | ): [Fish!] 65 | 66 | """Reads and enables pagination through a set of `Pond`.""" 67 | ponds( 68 | """Only read the first `n` values of the set.""" 69 | first: Int 70 | 71 | """Only read the last `n` values of the set.""" 72 | last: Int 73 | 74 | """ 75 | Skip the first `n` values from our `after` cursor, an alternative to cursor 76 | based pagination. May not be used with `last`. 77 | """ 78 | offset: Int 79 | 80 | """Read all values in the set before (above) this cursor.""" 81 | before: Cursor 82 | 83 | """Read all values in the set after (below) this cursor.""" 84 | after: Cursor 85 | 86 | """The method to use when ordering `Pond`.""" 87 | orderBy: [PondsOrderBy!] = [PRIMARY_KEY_ASC] 88 | 89 | """ 90 | A condition to be used in determining which values should be returned by the collection. 91 | """ 92 | condition: PondCondition 93 | ): PondsConnection 94 | 95 | """Reads a set of `Pond`.""" 96 | pondsList( 97 | """Only read the first `n` values of the set.""" 98 | first: Int 99 | 100 | """Skip the first `n` values.""" 101 | offset: Int 102 | 103 | """The method to use when ordering `Pond`.""" 104 | orderBy: [PondsOrderBy!] 105 | 106 | """ 107 | A condition to be used in determining which values should be returned by the collection. 108 | """ 109 | condition: PondCondition 110 | ): [Pond!] 111 | fish(id: Int!): Fish 112 | pond(id: Int!): Pond 113 | 114 | """Reads a single `Fish` using its globally unique `ID`.""" 115 | fishByNodeId( 116 | """The globally unique `ID` to be used in selecting a single `Fish`.""" 117 | nodeId: ID! 118 | ): Fish 119 | 120 | """Reads a single `Pond` using its globally unique `ID`.""" 121 | pondByNodeId( 122 | """The globally unique `ID` to be used in selecting a single `Pond`.""" 123 | nodeId: ID! 124 | ): Pond 125 | } 126 | 127 | """An object with a globally unique `ID`.""" 128 | interface Node { 129 | """ 130 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 131 | """ 132 | nodeId: ID! 133 | } 134 | 135 | """A connection to a list of `Fish` values.""" 136 | type FishConnection { 137 | """A list of `Fish` objects.""" 138 | nodes: [Fish]! 139 | 140 | """ 141 | A list of edges which contains the `Fish` and cursor to aid in pagination. 142 | """ 143 | edges: [FishEdge!]! 144 | 145 | """Information to aid in pagination.""" 146 | pageInfo: PageInfo! 147 | 148 | """The count of *all* `Fish` you could get from the connection.""" 149 | totalCount: Int! 150 | } 151 | 152 | type Fish implements Node { 153 | """ 154 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 155 | """ 156 | nodeId: ID! 157 | id: Int! 158 | pondId: Int! 159 | name: String! 160 | 161 | """Reads a single `Pond` that is related to this `Fish`.""" 162 | pond: Pond 163 | } 164 | 165 | type Pond implements Node { 166 | """ 167 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 168 | """ 169 | nodeId: ID! 170 | id: Int! 171 | name: String! 172 | 173 | """Reads and enables pagination through a set of `Fish`.""" 174 | fishes( 175 | """Only read the first `n` values of the set.""" 176 | first: Int 177 | 178 | """Only read the last `n` values of the set.""" 179 | last: Int 180 | 181 | """ 182 | Skip the first `n` values from our `after` cursor, an alternative to cursor 183 | based pagination. May not be used with `last`. 184 | """ 185 | offset: Int 186 | 187 | """Read all values in the set before (above) this cursor.""" 188 | before: Cursor 189 | 190 | """Read all values in the set after (below) this cursor.""" 191 | after: Cursor 192 | 193 | """The method to use when ordering `Fish`.""" 194 | orderBy: [FishOrderBy!] = [PRIMARY_KEY_ASC] 195 | 196 | """ 197 | A condition to be used in determining which values should be returned by the collection. 198 | """ 199 | condition: FishCondition 200 | ): FishConnection! 201 | 202 | """Reads and enables pagination through a set of `Fish`.""" 203 | fishesList( 204 | """Only read the first `n` values of the set.""" 205 | first: Int 206 | 207 | """Skip the first `n` values.""" 208 | offset: Int 209 | 210 | """The method to use when ordering `Fish`.""" 211 | orderBy: [FishOrderBy!] 212 | 213 | """ 214 | A condition to be used in determining which values should be returned by the collection. 215 | """ 216 | condition: FishCondition 217 | ): [Fish!]! 218 | } 219 | 220 | """A location in a connection that can be used for resuming pagination.""" 221 | scalar Cursor 222 | 223 | """Methods to use when ordering `Fish`.""" 224 | enum FishOrderBy { 225 | NATURAL 226 | ID_ASC 227 | ID_DESC 228 | POND_ID_ASC 229 | POND_ID_DESC 230 | NAME_ASC 231 | NAME_DESC 232 | PRIMARY_KEY_ASC 233 | PRIMARY_KEY_DESC 234 | } 235 | 236 | """ 237 | A condition to be used against `Fish` object types. All fields are tested for equality and combined with a logical ‘and.’ 238 | """ 239 | input FishCondition { 240 | """Checks for equality with the object’s `id` field.""" 241 | id: Int 242 | 243 | """Checks for equality with the object’s `pondId` field.""" 244 | pondId: Int 245 | 246 | """Checks for equality with the object’s `name` field.""" 247 | name: String 248 | } 249 | 250 | """A `Fish` edge in the connection.""" 251 | type FishEdge { 252 | """A cursor for use in pagination.""" 253 | cursor: Cursor 254 | 255 | """The `Fish` at the end of the edge.""" 256 | node: Fish 257 | } 258 | 259 | """Information about pagination in a connection.""" 260 | type PageInfo { 261 | """When paginating forwards, are there more items?""" 262 | hasNextPage: Boolean! 263 | 264 | """When paginating backwards, are there more items?""" 265 | hasPreviousPage: Boolean! 266 | 267 | """When paginating backwards, the cursor to continue.""" 268 | startCursor: Cursor 269 | 270 | """When paginating forwards, the cursor to continue.""" 271 | endCursor: Cursor 272 | } 273 | 274 | """A connection to a list of `Pond` values.""" 275 | type PondsConnection { 276 | """A list of `Pond` objects.""" 277 | nodes: [Pond]! 278 | 279 | """ 280 | A list of edges which contains the `Pond` and cursor to aid in pagination. 281 | """ 282 | edges: [PondsEdge!]! 283 | 284 | """Information to aid in pagination.""" 285 | pageInfo: PageInfo! 286 | 287 | """The count of *all* `Pond` you could get from the connection.""" 288 | totalCount: Int! 289 | } 290 | 291 | """A `Pond` edge in the connection.""" 292 | type PondsEdge { 293 | """A cursor for use in pagination.""" 294 | cursor: Cursor 295 | 296 | """The `Pond` at the end of the edge.""" 297 | node: Pond 298 | } 299 | 300 | """Methods to use when ordering `Pond`.""" 301 | enum PondsOrderBy { 302 | NATURAL 303 | ID_ASC 304 | ID_DESC 305 | NAME_ASC 306 | NAME_DESC 307 | PRIMARY_KEY_ASC 308 | PRIMARY_KEY_DESC 309 | } 310 | 311 | """ 312 | A condition to be used against `Pond` object types. All fields are tested for equality and combined with a logical ‘and.’ 313 | """ 314 | input PondCondition { 315 | """Checks for equality with the object’s `id` field.""" 316 | id: Int 317 | 318 | """Checks for equality with the object’s `name` field.""" 319 | name: String 320 | } 321 | 322 | """ 323 | The root mutation type which contains root level fields which mutate data. 324 | """ 325 | type Mutation { 326 | """Creates a single `Fish`.""" 327 | createFish( 328 | """ 329 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 330 | """ 331 | input: CreateFishInput! 332 | ): CreateFishPayload 333 | 334 | """Creates a single `Pond`.""" 335 | createPond( 336 | """ 337 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 338 | """ 339 | input: CreatePondInput! 340 | ): CreatePondPayload 341 | 342 | """Updates a single `Fish` using its globally unique id and a patch.""" 343 | updateFishByNodeId( 344 | """ 345 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 346 | """ 347 | input: UpdateFishByNodeIdInput! 348 | ): UpdateFishPayload 349 | 350 | """Updates a single `Fish` using a unique key and a patch.""" 351 | updateFish( 352 | """ 353 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 354 | """ 355 | input: UpdateFishInput! 356 | ): UpdateFishPayload 357 | 358 | """Updates a single `Pond` using its globally unique id and a patch.""" 359 | updatePondByNodeId( 360 | """ 361 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 362 | """ 363 | input: UpdatePondByNodeIdInput! 364 | ): UpdatePondPayload 365 | 366 | """Updates a single `Pond` using a unique key and a patch.""" 367 | updatePond( 368 | """ 369 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 370 | """ 371 | input: UpdatePondInput! 372 | ): UpdatePondPayload 373 | 374 | """Deletes a single `Fish` using its globally unique id.""" 375 | deleteFishByNodeId( 376 | """ 377 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 378 | """ 379 | input: DeleteFishByNodeIdInput! 380 | ): DeleteFishPayload 381 | 382 | """Deletes a single `Fish` using a unique key.""" 383 | deleteFish( 384 | """ 385 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 386 | """ 387 | input: DeleteFishInput! 388 | ): DeleteFishPayload 389 | 390 | """Deletes a single `Pond` using its globally unique id.""" 391 | deletePondByNodeId( 392 | """ 393 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 394 | """ 395 | input: DeletePondByNodeIdInput! 396 | ): DeletePondPayload 397 | 398 | """Deletes a single `Pond` using a unique key.""" 399 | deletePond( 400 | """ 401 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 402 | """ 403 | input: DeletePondInput! 404 | ): DeletePondPayload 405 | } 406 | 407 | """The output of our create `Fish` mutation.""" 408 | type CreateFishPayload { 409 | """ 410 | The exact same `clientMutationId` that was provided in the mutation input, 411 | unchanged and unused. May be used by a client to track mutations. 412 | """ 413 | clientMutationId: String 414 | 415 | """The `Fish` that was created by this mutation.""" 416 | fish: Fish 417 | 418 | """ 419 | Our root query field type. Allows us to run any query from our mutation payload. 420 | """ 421 | query: Query 422 | 423 | """Reads a single `Pond` that is related to this `Fish`.""" 424 | pond: Pond 425 | 426 | """An edge for our `Fish`. May be used by Relay 1.""" 427 | fishEdge( 428 | """The method to use when ordering `Fish`.""" 429 | orderBy: [FishOrderBy!] = [PRIMARY_KEY_ASC] 430 | ): FishEdge 431 | } 432 | 433 | """All input for the create `Fish` mutation.""" 434 | input CreateFishInput { 435 | """ 436 | An arbitrary string value with no semantic meaning. Will be included in the 437 | payload verbatim. May be used to track mutations by the client. 438 | """ 439 | clientMutationId: String 440 | 441 | """The `Fish` to be created by this mutation.""" 442 | fish: FishInput! 443 | } 444 | 445 | """An input for mutations affecting `Fish`""" 446 | input FishInput { 447 | id: Int 448 | pondId: Int! 449 | name: String! 450 | } 451 | 452 | """The output of our create `Pond` mutation.""" 453 | type CreatePondPayload { 454 | """ 455 | The exact same `clientMutationId` that was provided in the mutation input, 456 | unchanged and unused. May be used by a client to track mutations. 457 | """ 458 | clientMutationId: String 459 | 460 | """The `Pond` that was created by this mutation.""" 461 | pond: Pond 462 | 463 | """ 464 | Our root query field type. Allows us to run any query from our mutation payload. 465 | """ 466 | query: Query 467 | 468 | """An edge for our `Pond`. May be used by Relay 1.""" 469 | pondEdge( 470 | """The method to use when ordering `Pond`.""" 471 | orderBy: [PondsOrderBy!] = [PRIMARY_KEY_ASC] 472 | ): PondsEdge 473 | } 474 | 475 | """All input for the create `Pond` mutation.""" 476 | input CreatePondInput { 477 | """ 478 | An arbitrary string value with no semantic meaning. Will be included in the 479 | payload verbatim. May be used to track mutations by the client. 480 | """ 481 | clientMutationId: String 482 | 483 | """The `Pond` to be created by this mutation.""" 484 | pond: PondInput! 485 | } 486 | 487 | """An input for mutations affecting `Pond`""" 488 | input PondInput { 489 | id: Int 490 | name: String! 491 | } 492 | 493 | """The output of our update `Fish` mutation.""" 494 | type UpdateFishPayload { 495 | """ 496 | The exact same `clientMutationId` that was provided in the mutation input, 497 | unchanged and unused. May be used by a client to track mutations. 498 | """ 499 | clientMutationId: String 500 | 501 | """The `Fish` that was updated by this mutation.""" 502 | fish: Fish 503 | 504 | """ 505 | Our root query field type. Allows us to run any query from our mutation payload. 506 | """ 507 | query: Query 508 | 509 | """Reads a single `Pond` that is related to this `Fish`.""" 510 | pond: Pond 511 | 512 | """An edge for our `Fish`. May be used by Relay 1.""" 513 | fishEdge( 514 | """The method to use when ordering `Fish`.""" 515 | orderBy: [FishOrderBy!] = [PRIMARY_KEY_ASC] 516 | ): FishEdge 517 | } 518 | 519 | """All input for the `updateFishByNodeId` mutation.""" 520 | input UpdateFishByNodeIdInput { 521 | """ 522 | An arbitrary string value with no semantic meaning. Will be included in the 523 | payload verbatim. May be used to track mutations by the client. 524 | """ 525 | clientMutationId: String 526 | 527 | """ 528 | The globally unique `ID` which will identify a single `Fish` to be updated. 529 | """ 530 | nodeId: ID! 531 | 532 | """ 533 | An object where the defined keys will be set on the `Fish` being updated. 534 | """ 535 | patch: FishPatch! 536 | } 537 | 538 | """Represents an update to a `Fish`. Fields that are set will be updated.""" 539 | input FishPatch { 540 | id: Int 541 | pondId: Int 542 | name: String 543 | } 544 | 545 | """All input for the `updateFish` mutation.""" 546 | input UpdateFishInput { 547 | """ 548 | An arbitrary string value with no semantic meaning. Will be included in the 549 | payload verbatim. May be used to track mutations by the client. 550 | """ 551 | clientMutationId: String 552 | 553 | """ 554 | An object where the defined keys will be set on the `Fish` being updated. 555 | """ 556 | patch: FishPatch! 557 | id: Int! 558 | } 559 | 560 | """The output of our update `Pond` mutation.""" 561 | type UpdatePondPayload { 562 | """ 563 | The exact same `clientMutationId` that was provided in the mutation input, 564 | unchanged and unused. May be used by a client to track mutations. 565 | """ 566 | clientMutationId: String 567 | 568 | """The `Pond` that was updated by this mutation.""" 569 | pond: Pond 570 | 571 | """ 572 | Our root query field type. Allows us to run any query from our mutation payload. 573 | """ 574 | query: Query 575 | 576 | """An edge for our `Pond`. May be used by Relay 1.""" 577 | pondEdge( 578 | """The method to use when ordering `Pond`.""" 579 | orderBy: [PondsOrderBy!] = [PRIMARY_KEY_ASC] 580 | ): PondsEdge 581 | } 582 | 583 | """All input for the `updatePondByNodeId` mutation.""" 584 | input UpdatePondByNodeIdInput { 585 | """ 586 | An arbitrary string value with no semantic meaning. Will be included in the 587 | payload verbatim. May be used to track mutations by the client. 588 | """ 589 | clientMutationId: String 590 | 591 | """ 592 | The globally unique `ID` which will identify a single `Pond` to be updated. 593 | """ 594 | nodeId: ID! 595 | 596 | """ 597 | An object where the defined keys will be set on the `Pond` being updated. 598 | """ 599 | patch: PondPatch! 600 | } 601 | 602 | """Represents an update to a `Pond`. Fields that are set will be updated.""" 603 | input PondPatch { 604 | id: Int 605 | name: String 606 | } 607 | 608 | """All input for the `updatePond` mutation.""" 609 | input UpdatePondInput { 610 | """ 611 | An arbitrary string value with no semantic meaning. Will be included in the 612 | payload verbatim. May be used to track mutations by the client. 613 | """ 614 | clientMutationId: String 615 | 616 | """ 617 | An object where the defined keys will be set on the `Pond` being updated. 618 | """ 619 | patch: PondPatch! 620 | id: Int! 621 | } 622 | 623 | """The output of our delete `Fish` mutation.""" 624 | type DeleteFishPayload { 625 | """ 626 | The exact same `clientMutationId` that was provided in the mutation input, 627 | unchanged and unused. May be used by a client to track mutations. 628 | """ 629 | clientMutationId: String 630 | 631 | """The `Fish` that was deleted by this mutation.""" 632 | fish: Fish 633 | deletedFishNodeId: ID 634 | 635 | """ 636 | Our root query field type. Allows us to run any query from our mutation payload. 637 | """ 638 | query: Query 639 | 640 | """Reads a single `Pond` that is related to this `Fish`.""" 641 | pond: Pond 642 | 643 | """An edge for our `Fish`. May be used by Relay 1.""" 644 | fishEdge( 645 | """The method to use when ordering `Fish`.""" 646 | orderBy: [FishOrderBy!] = [PRIMARY_KEY_ASC] 647 | ): FishEdge 648 | } 649 | 650 | """All input for the `deleteFishByNodeId` mutation.""" 651 | input DeleteFishByNodeIdInput { 652 | """ 653 | An arbitrary string value with no semantic meaning. Will be included in the 654 | payload verbatim. May be used to track mutations by the client. 655 | """ 656 | clientMutationId: String 657 | 658 | """ 659 | The globally unique `ID` which will identify a single `Fish` to be deleted. 660 | """ 661 | nodeId: ID! 662 | } 663 | 664 | """All input for the `deleteFish` mutation.""" 665 | input DeleteFishInput { 666 | """ 667 | An arbitrary string value with no semantic meaning. Will be included in the 668 | payload verbatim. May be used to track mutations by the client. 669 | """ 670 | clientMutationId: String 671 | id: Int! 672 | } 673 | 674 | """The output of our delete `Pond` mutation.""" 675 | type DeletePondPayload { 676 | """ 677 | The exact same `clientMutationId` that was provided in the mutation input, 678 | unchanged and unused. May be used by a client to track mutations. 679 | """ 680 | clientMutationId: String 681 | 682 | """The `Pond` that was deleted by this mutation.""" 683 | pond: Pond 684 | deletedPondNodeId: ID 685 | 686 | """ 687 | Our root query field type. Allows us to run any query from our mutation payload. 688 | """ 689 | query: Query 690 | 691 | """An edge for our `Pond`. May be used by Relay 1.""" 692 | pondEdge( 693 | """The method to use when ordering `Pond`.""" 694 | orderBy: [PondsOrderBy!] = [PRIMARY_KEY_ASC] 695 | ): PondsEdge 696 | } 697 | 698 | """All input for the `deletePondByNodeId` mutation.""" 699 | input DeletePondByNodeIdInput { 700 | """ 701 | An arbitrary string value with no semantic meaning. Will be included in the 702 | payload verbatim. May be used to track mutations by the client. 703 | """ 704 | clientMutationId: String 705 | 706 | """ 707 | The globally unique `ID` which will identify a single `Pond` to be deleted. 708 | """ 709 | nodeId: ID! 710 | } 711 | 712 | """All input for the `deletePond` mutation.""" 713 | input DeletePondInput { 714 | """ 715 | An arbitrary string value with no semantic meaning. Will be included in the 716 | payload verbatim. May be used to track mutations by the client. 717 | """ 718 | clientMutationId: String 719 | id: Int! 720 | } 721 | -------------------------------------------------------------------------------- /tests/fish/schema.sql: -------------------------------------------------------------------------------- 1 | create table ponds( 2 | id serial primary key, 3 | name text not null 4 | ); 5 | 6 | create table fish( 7 | id serial primary key, 8 | pond_id int not null references ponds, 9 | name text not null 10 | ); 11 | insert into ponds (name) values ('Amy'), ('James'), ('Duck'); 12 | insert into fish (pond_id, name) values (1, 'Blub'), (2, 'Bubble'), (3, 'Guber'); 13 | 14 | -------------------------------------------------------------------------------- /tests/fish/schema.unsimplified.graphql: -------------------------------------------------------------------------------- 1 | """The root query type which gives access points into the data universe.""" 2 | type Query implements Node { 3 | """ 4 | Exposes the root query type nested one level down. This is helpful for Relay 1 5 | which can only query top level fields if they are in a particular form. 6 | """ 7 | query: Query! 8 | 9 | """ 10 | The root query type must be a `Node` to work well with Relay 1 mutations. This just resolves to `query`. 11 | """ 12 | nodeId: ID! 13 | 14 | """Fetches an object given its globally unique `ID`.""" 15 | node( 16 | """The globally unique `ID`.""" 17 | nodeId: ID! 18 | ): Node 19 | 20 | """Reads and enables pagination through a set of `Fish`.""" 21 | allFish( 22 | """Only read the first `n` values of the set.""" 23 | first: Int 24 | 25 | """Only read the last `n` values of the set.""" 26 | last: Int 27 | 28 | """ 29 | Skip the first `n` values from our `after` cursor, an alternative to cursor 30 | based pagination. May not be used with `last`. 31 | """ 32 | offset: Int 33 | 34 | """Read all values in the set before (above) this cursor.""" 35 | before: Cursor 36 | 37 | """Read all values in the set after (below) this cursor.""" 38 | after: Cursor 39 | 40 | """The method to use when ordering `Fish`.""" 41 | orderBy: [FishOrderBy!] = [PRIMARY_KEY_ASC] 42 | 43 | """ 44 | A condition to be used in determining which values should be returned by the collection. 45 | """ 46 | condition: FishCondition 47 | ): FishConnection 48 | 49 | """Reads a set of `Fish`.""" 50 | allFishList( 51 | """Only read the first `n` values of the set.""" 52 | first: Int 53 | 54 | """Skip the first `n` values.""" 55 | offset: Int 56 | 57 | """The method to use when ordering `Fish`.""" 58 | orderBy: [FishOrderBy!] 59 | 60 | """ 61 | A condition to be used in determining which values should be returned by the collection. 62 | """ 63 | condition: FishCondition 64 | ): [Fish!] 65 | 66 | """Reads and enables pagination through a set of `Pond`.""" 67 | allPonds( 68 | """Only read the first `n` values of the set.""" 69 | first: Int 70 | 71 | """Only read the last `n` values of the set.""" 72 | last: Int 73 | 74 | """ 75 | Skip the first `n` values from our `after` cursor, an alternative to cursor 76 | based pagination. May not be used with `last`. 77 | """ 78 | offset: Int 79 | 80 | """Read all values in the set before (above) this cursor.""" 81 | before: Cursor 82 | 83 | """Read all values in the set after (below) this cursor.""" 84 | after: Cursor 85 | 86 | """The method to use when ordering `Pond`.""" 87 | orderBy: [PondsOrderBy!] = [PRIMARY_KEY_ASC] 88 | 89 | """ 90 | A condition to be used in determining which values should be returned by the collection. 91 | """ 92 | condition: PondCondition 93 | ): PondsConnection 94 | 95 | """Reads a set of `Pond`.""" 96 | allPondsList( 97 | """Only read the first `n` values of the set.""" 98 | first: Int 99 | 100 | """Skip the first `n` values.""" 101 | offset: Int 102 | 103 | """The method to use when ordering `Pond`.""" 104 | orderBy: [PondsOrderBy!] 105 | 106 | """ 107 | A condition to be used in determining which values should be returned by the collection. 108 | """ 109 | condition: PondCondition 110 | ): [Pond!] 111 | fishById(id: Int!): Fish 112 | pondById(id: Int!): Pond 113 | 114 | """Reads a single `Fish` using its globally unique `ID`.""" 115 | fish( 116 | """The globally unique `ID` to be used in selecting a single `Fish`.""" 117 | nodeId: ID! 118 | ): Fish 119 | 120 | """Reads a single `Pond` using its globally unique `ID`.""" 121 | pond( 122 | """The globally unique `ID` to be used in selecting a single `Pond`.""" 123 | nodeId: ID! 124 | ): Pond 125 | } 126 | 127 | """An object with a globally unique `ID`.""" 128 | interface Node { 129 | """ 130 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 131 | """ 132 | nodeId: ID! 133 | } 134 | 135 | """A connection to a list of `Fish` values.""" 136 | type FishConnection { 137 | """A list of `Fish` objects.""" 138 | nodes: [Fish]! 139 | 140 | """ 141 | A list of edges which contains the `Fish` and cursor to aid in pagination. 142 | """ 143 | edges: [FishEdge!]! 144 | 145 | """Information to aid in pagination.""" 146 | pageInfo: PageInfo! 147 | 148 | """The count of *all* `Fish` you could get from the connection.""" 149 | totalCount: Int! 150 | } 151 | 152 | type Fish implements Node { 153 | """ 154 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 155 | """ 156 | nodeId: ID! 157 | id: Int! 158 | pondId: Int! 159 | name: String! 160 | 161 | """Reads a single `Pond` that is related to this `Fish`.""" 162 | pondByPondId: Pond 163 | } 164 | 165 | type Pond implements Node { 166 | """ 167 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 168 | """ 169 | nodeId: ID! 170 | id: Int! 171 | name: String! 172 | 173 | """Reads and enables pagination through a set of `Fish`.""" 174 | fishByPondId( 175 | """Only read the first `n` values of the set.""" 176 | first: Int 177 | 178 | """Only read the last `n` values of the set.""" 179 | last: Int 180 | 181 | """ 182 | Skip the first `n` values from our `after` cursor, an alternative to cursor 183 | based pagination. May not be used with `last`. 184 | """ 185 | offset: Int 186 | 187 | """Read all values in the set before (above) this cursor.""" 188 | before: Cursor 189 | 190 | """Read all values in the set after (below) this cursor.""" 191 | after: Cursor 192 | 193 | """The method to use when ordering `Fish`.""" 194 | orderBy: [FishOrderBy!] = [PRIMARY_KEY_ASC] 195 | 196 | """ 197 | A condition to be used in determining which values should be returned by the collection. 198 | """ 199 | condition: FishCondition 200 | ): FishConnection! 201 | 202 | """Reads and enables pagination through a set of `Fish`.""" 203 | fishByPondIdList( 204 | """Only read the first `n` values of the set.""" 205 | first: Int 206 | 207 | """Skip the first `n` values.""" 208 | offset: Int 209 | 210 | """The method to use when ordering `Fish`.""" 211 | orderBy: [FishOrderBy!] 212 | 213 | """ 214 | A condition to be used in determining which values should be returned by the collection. 215 | """ 216 | condition: FishCondition 217 | ): [Fish!]! 218 | } 219 | 220 | """A location in a connection that can be used for resuming pagination.""" 221 | scalar Cursor 222 | 223 | """Methods to use when ordering `Fish`.""" 224 | enum FishOrderBy { 225 | NATURAL 226 | ID_ASC 227 | ID_DESC 228 | POND_ID_ASC 229 | POND_ID_DESC 230 | NAME_ASC 231 | NAME_DESC 232 | PRIMARY_KEY_ASC 233 | PRIMARY_KEY_DESC 234 | } 235 | 236 | """ 237 | A condition to be used against `Fish` object types. All fields are tested for equality and combined with a logical ‘and.’ 238 | """ 239 | input FishCondition { 240 | """Checks for equality with the object’s `id` field.""" 241 | id: Int 242 | 243 | """Checks for equality with the object’s `pondId` field.""" 244 | pondId: Int 245 | 246 | """Checks for equality with the object’s `name` field.""" 247 | name: String 248 | } 249 | 250 | """A `Fish` edge in the connection.""" 251 | type FishEdge { 252 | """A cursor for use in pagination.""" 253 | cursor: Cursor 254 | 255 | """The `Fish` at the end of the edge.""" 256 | node: Fish 257 | } 258 | 259 | """Information about pagination in a connection.""" 260 | type PageInfo { 261 | """When paginating forwards, are there more items?""" 262 | hasNextPage: Boolean! 263 | 264 | """When paginating backwards, are there more items?""" 265 | hasPreviousPage: Boolean! 266 | 267 | """When paginating backwards, the cursor to continue.""" 268 | startCursor: Cursor 269 | 270 | """When paginating forwards, the cursor to continue.""" 271 | endCursor: Cursor 272 | } 273 | 274 | """A connection to a list of `Pond` values.""" 275 | type PondsConnection { 276 | """A list of `Pond` objects.""" 277 | nodes: [Pond]! 278 | 279 | """ 280 | A list of edges which contains the `Pond` and cursor to aid in pagination. 281 | """ 282 | edges: [PondsEdge!]! 283 | 284 | """Information to aid in pagination.""" 285 | pageInfo: PageInfo! 286 | 287 | """The count of *all* `Pond` you could get from the connection.""" 288 | totalCount: Int! 289 | } 290 | 291 | """A `Pond` edge in the connection.""" 292 | type PondsEdge { 293 | """A cursor for use in pagination.""" 294 | cursor: Cursor 295 | 296 | """The `Pond` at the end of the edge.""" 297 | node: Pond 298 | } 299 | 300 | """Methods to use when ordering `Pond`.""" 301 | enum PondsOrderBy { 302 | NATURAL 303 | ID_ASC 304 | ID_DESC 305 | NAME_ASC 306 | NAME_DESC 307 | PRIMARY_KEY_ASC 308 | PRIMARY_KEY_DESC 309 | } 310 | 311 | """ 312 | A condition to be used against `Pond` object types. All fields are tested for equality and combined with a logical ‘and.’ 313 | """ 314 | input PondCondition { 315 | """Checks for equality with the object’s `id` field.""" 316 | id: Int 317 | 318 | """Checks for equality with the object’s `name` field.""" 319 | name: String 320 | } 321 | 322 | """ 323 | The root mutation type which contains root level fields which mutate data. 324 | """ 325 | type Mutation { 326 | """Creates a single `Fish`.""" 327 | createFish( 328 | """ 329 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 330 | """ 331 | input: CreateFishInput! 332 | ): CreateFishPayload 333 | 334 | """Creates a single `Pond`.""" 335 | createPond( 336 | """ 337 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 338 | """ 339 | input: CreatePondInput! 340 | ): CreatePondPayload 341 | 342 | """Updates a single `Fish` using its globally unique id and a patch.""" 343 | updateFish( 344 | """ 345 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 346 | """ 347 | input: UpdateFishInput! 348 | ): UpdateFishPayload 349 | 350 | """Updates a single `Fish` using a unique key and a patch.""" 351 | updateFishById( 352 | """ 353 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 354 | """ 355 | input: UpdateFishByIdInput! 356 | ): UpdateFishPayload 357 | 358 | """Updates a single `Pond` using its globally unique id and a patch.""" 359 | updatePond( 360 | """ 361 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 362 | """ 363 | input: UpdatePondInput! 364 | ): UpdatePondPayload 365 | 366 | """Updates a single `Pond` using a unique key and a patch.""" 367 | updatePondById( 368 | """ 369 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 370 | """ 371 | input: UpdatePondByIdInput! 372 | ): UpdatePondPayload 373 | 374 | """Deletes a single `Fish` using its globally unique id.""" 375 | deleteFish( 376 | """ 377 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 378 | """ 379 | input: DeleteFishInput! 380 | ): DeleteFishPayload 381 | 382 | """Deletes a single `Fish` using a unique key.""" 383 | deleteFishById( 384 | """ 385 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 386 | """ 387 | input: DeleteFishByIdInput! 388 | ): DeleteFishPayload 389 | 390 | """Deletes a single `Pond` using its globally unique id.""" 391 | deletePond( 392 | """ 393 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 394 | """ 395 | input: DeletePondInput! 396 | ): DeletePondPayload 397 | 398 | """Deletes a single `Pond` using a unique key.""" 399 | deletePondById( 400 | """ 401 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 402 | """ 403 | input: DeletePondByIdInput! 404 | ): DeletePondPayload 405 | } 406 | 407 | """The output of our create `Fish` mutation.""" 408 | type CreateFishPayload { 409 | """ 410 | The exact same `clientMutationId` that was provided in the mutation input, 411 | unchanged and unused. May be used by a client to track mutations. 412 | """ 413 | clientMutationId: String 414 | 415 | """The `Fish` that was created by this mutation.""" 416 | fish: Fish 417 | 418 | """ 419 | Our root query field type. Allows us to run any query from our mutation payload. 420 | """ 421 | query: Query 422 | 423 | """Reads a single `Pond` that is related to this `Fish`.""" 424 | pondByPondId: Pond 425 | 426 | """An edge for our `Fish`. May be used by Relay 1.""" 427 | fishEdge( 428 | """The method to use when ordering `Fish`.""" 429 | orderBy: [FishOrderBy!] = [PRIMARY_KEY_ASC] 430 | ): FishEdge 431 | } 432 | 433 | """All input for the create `Fish` mutation.""" 434 | input CreateFishInput { 435 | """ 436 | An arbitrary string value with no semantic meaning. Will be included in the 437 | payload verbatim. May be used to track mutations by the client. 438 | """ 439 | clientMutationId: String 440 | 441 | """The `Fish` to be created by this mutation.""" 442 | fish: FishInput! 443 | } 444 | 445 | """An input for mutations affecting `Fish`""" 446 | input FishInput { 447 | id: Int 448 | pondId: Int! 449 | name: String! 450 | } 451 | 452 | """The output of our create `Pond` mutation.""" 453 | type CreatePondPayload { 454 | """ 455 | The exact same `clientMutationId` that was provided in the mutation input, 456 | unchanged and unused. May be used by a client to track mutations. 457 | """ 458 | clientMutationId: String 459 | 460 | """The `Pond` that was created by this mutation.""" 461 | pond: Pond 462 | 463 | """ 464 | Our root query field type. Allows us to run any query from our mutation payload. 465 | """ 466 | query: Query 467 | 468 | """An edge for our `Pond`. May be used by Relay 1.""" 469 | pondEdge( 470 | """The method to use when ordering `Pond`.""" 471 | orderBy: [PondsOrderBy!] = [PRIMARY_KEY_ASC] 472 | ): PondsEdge 473 | } 474 | 475 | """All input for the create `Pond` mutation.""" 476 | input CreatePondInput { 477 | """ 478 | An arbitrary string value with no semantic meaning. Will be included in the 479 | payload verbatim. May be used to track mutations by the client. 480 | """ 481 | clientMutationId: String 482 | 483 | """The `Pond` to be created by this mutation.""" 484 | pond: PondInput! 485 | } 486 | 487 | """An input for mutations affecting `Pond`""" 488 | input PondInput { 489 | id: Int 490 | name: String! 491 | } 492 | 493 | """The output of our update `Fish` mutation.""" 494 | type UpdateFishPayload { 495 | """ 496 | The exact same `clientMutationId` that was provided in the mutation input, 497 | unchanged and unused. May be used by a client to track mutations. 498 | """ 499 | clientMutationId: String 500 | 501 | """The `Fish` that was updated by this mutation.""" 502 | fish: Fish 503 | 504 | """ 505 | Our root query field type. Allows us to run any query from our mutation payload. 506 | """ 507 | query: Query 508 | 509 | """Reads a single `Pond` that is related to this `Fish`.""" 510 | pondByPondId: Pond 511 | 512 | """An edge for our `Fish`. May be used by Relay 1.""" 513 | fishEdge( 514 | """The method to use when ordering `Fish`.""" 515 | orderBy: [FishOrderBy!] = [PRIMARY_KEY_ASC] 516 | ): FishEdge 517 | } 518 | 519 | """All input for the `updateFish` mutation.""" 520 | input UpdateFishInput { 521 | """ 522 | An arbitrary string value with no semantic meaning. Will be included in the 523 | payload verbatim. May be used to track mutations by the client. 524 | """ 525 | clientMutationId: String 526 | 527 | """ 528 | The globally unique `ID` which will identify a single `Fish` to be updated. 529 | """ 530 | nodeId: ID! 531 | 532 | """ 533 | An object where the defined keys will be set on the `Fish` being updated. 534 | """ 535 | fishPatch: FishPatch! 536 | } 537 | 538 | """Represents an update to a `Fish`. Fields that are set will be updated.""" 539 | input FishPatch { 540 | id: Int 541 | pondId: Int 542 | name: String 543 | } 544 | 545 | """All input for the `updateFishById` mutation.""" 546 | input UpdateFishByIdInput { 547 | """ 548 | An arbitrary string value with no semantic meaning. Will be included in the 549 | payload verbatim. May be used to track mutations by the client. 550 | """ 551 | clientMutationId: String 552 | 553 | """ 554 | An object where the defined keys will be set on the `Fish` being updated. 555 | """ 556 | fishPatch: FishPatch! 557 | id: Int! 558 | } 559 | 560 | """The output of our update `Pond` mutation.""" 561 | type UpdatePondPayload { 562 | """ 563 | The exact same `clientMutationId` that was provided in the mutation input, 564 | unchanged and unused. May be used by a client to track mutations. 565 | """ 566 | clientMutationId: String 567 | 568 | """The `Pond` that was updated by this mutation.""" 569 | pond: Pond 570 | 571 | """ 572 | Our root query field type. Allows us to run any query from our mutation payload. 573 | """ 574 | query: Query 575 | 576 | """An edge for our `Pond`. May be used by Relay 1.""" 577 | pondEdge( 578 | """The method to use when ordering `Pond`.""" 579 | orderBy: [PondsOrderBy!] = [PRIMARY_KEY_ASC] 580 | ): PondsEdge 581 | } 582 | 583 | """All input for the `updatePond` mutation.""" 584 | input UpdatePondInput { 585 | """ 586 | An arbitrary string value with no semantic meaning. Will be included in the 587 | payload verbatim. May be used to track mutations by the client. 588 | """ 589 | clientMutationId: String 590 | 591 | """ 592 | The globally unique `ID` which will identify a single `Pond` to be updated. 593 | """ 594 | nodeId: ID! 595 | 596 | """ 597 | An object where the defined keys will be set on the `Pond` being updated. 598 | """ 599 | pondPatch: PondPatch! 600 | } 601 | 602 | """Represents an update to a `Pond`. Fields that are set will be updated.""" 603 | input PondPatch { 604 | id: Int 605 | name: String 606 | } 607 | 608 | """All input for the `updatePondById` mutation.""" 609 | input UpdatePondByIdInput { 610 | """ 611 | An arbitrary string value with no semantic meaning. Will be included in the 612 | payload verbatim. May be used to track mutations by the client. 613 | """ 614 | clientMutationId: String 615 | 616 | """ 617 | An object where the defined keys will be set on the `Pond` being updated. 618 | """ 619 | pondPatch: PondPatch! 620 | id: Int! 621 | } 622 | 623 | """The output of our delete `Fish` mutation.""" 624 | type DeleteFishPayload { 625 | """ 626 | The exact same `clientMutationId` that was provided in the mutation input, 627 | unchanged and unused. May be used by a client to track mutations. 628 | """ 629 | clientMutationId: String 630 | 631 | """The `Fish` that was deleted by this mutation.""" 632 | fish: Fish 633 | deletedFishId: ID 634 | 635 | """ 636 | Our root query field type. Allows us to run any query from our mutation payload. 637 | """ 638 | query: Query 639 | 640 | """Reads a single `Pond` that is related to this `Fish`.""" 641 | pondByPondId: Pond 642 | 643 | """An edge for our `Fish`. May be used by Relay 1.""" 644 | fishEdge( 645 | """The method to use when ordering `Fish`.""" 646 | orderBy: [FishOrderBy!] = [PRIMARY_KEY_ASC] 647 | ): FishEdge 648 | } 649 | 650 | """All input for the `deleteFish` mutation.""" 651 | input DeleteFishInput { 652 | """ 653 | An arbitrary string value with no semantic meaning. Will be included in the 654 | payload verbatim. May be used to track mutations by the client. 655 | """ 656 | clientMutationId: String 657 | 658 | """ 659 | The globally unique `ID` which will identify a single `Fish` to be deleted. 660 | """ 661 | nodeId: ID! 662 | } 663 | 664 | """All input for the `deleteFishById` mutation.""" 665 | input DeleteFishByIdInput { 666 | """ 667 | An arbitrary string value with no semantic meaning. Will be included in the 668 | payload verbatim. May be used to track mutations by the client. 669 | """ 670 | clientMutationId: String 671 | id: Int! 672 | } 673 | 674 | """The output of our delete `Pond` mutation.""" 675 | type DeletePondPayload { 676 | """ 677 | The exact same `clientMutationId` that was provided in the mutation input, 678 | unchanged and unused. May be used by a client to track mutations. 679 | """ 680 | clientMutationId: String 681 | 682 | """The `Pond` that was deleted by this mutation.""" 683 | pond: Pond 684 | deletedPondId: ID 685 | 686 | """ 687 | Our root query field type. Allows us to run any query from our mutation payload. 688 | """ 689 | query: Query 690 | 691 | """An edge for our `Pond`. May be used by Relay 1.""" 692 | pondEdge( 693 | """The method to use when ordering `Pond`.""" 694 | orderBy: [PondsOrderBy!] = [PRIMARY_KEY_ASC] 695 | ): PondsEdge 696 | } 697 | 698 | """All input for the `deletePond` mutation.""" 699 | input DeletePondInput { 700 | """ 701 | An arbitrary string value with no semantic meaning. Will be included in the 702 | payload verbatim. May be used to track mutations by the client. 703 | """ 704 | clientMutationId: String 705 | 706 | """ 707 | The globally unique `ID` which will identify a single `Pond` to be deleted. 708 | """ 709 | nodeId: ID! 710 | } 711 | 712 | """All input for the `deletePondById` mutation.""" 713 | input DeletePondByIdInput { 714 | """ 715 | An arbitrary string value with no semantic meaning. Will be included in the 716 | payload verbatim. May be used to track mutations by the client. 717 | """ 718 | clientMutationId: String 719 | id: Int! 720 | } 721 | -------------------------------------------------------------------------------- /tests/issue_29/schema.graphql.diff: -------------------------------------------------------------------------------- 1 | --- unsimplified 2 | +++ simplified 3 | @@ -18,7 +18,7 @@ 4 | ): Node 5 | 6 | """Reads and enables pagination through a set of `Something`.""" 7 | - allSomethings( 8 | + somethings( 9 | """Only read the first `n` values of the set.""" 10 | first: Int 11 | 12 | @@ -47,7 +47,7 @@ 13 | ): SomethingsConnection 14 | 15 | """Reads a set of `Something`.""" 16 | - allSomethingsList( 17 | + somethingsList( 18 | """Only read the first `n` values of the set.""" 19 | first: Int 20 | 21 | @@ -64,7 +64,7 @@ 22 | ): [Something!] 23 | 24 | """Reads and enables pagination through a set of `SomethingDatum`.""" 25 | - allSomethingData( 26 | + somethingData( 27 | """Only read the first `n` values of the set.""" 28 | first: Int 29 | 30 | @@ -93,7 +93,7 @@ 31 | ): SomethingDataConnection 32 | 33 | """Reads a set of `SomethingDatum`.""" 34 | - allSomethingDataList( 35 | + somethingDataList( 36 | """Only read the first `n` values of the set.""" 37 | first: Int 38 | 39 | @@ -108,17 +108,17 @@ 40 | """ 41 | condition: SomethingDatumCondition 42 | ): [SomethingDatum!] 43 | - somethingBySomeId(someId: Int!): Something 44 | - somethingDatumBySomethingDataId(somethingDataId: Int!): SomethingDatum 45 | + something(someId: Int!): Something 46 | + somethingDatum(somethingDataId: Int!): SomethingDatum 47 | 48 | """Reads a single `Something` using its globally unique `ID`.""" 49 | - something( 50 | + somethingByNodeId( 51 | """The globally unique `ID` to be used in selecting a single `Something`.""" 52 | nodeId: ID! 53 | ): Something 54 | 55 | """Reads a single `SomethingDatum` using its globally unique `ID`.""" 56 | - somethingDatum( 57 | + somethingDatumByNodeId( 58 | """ 59 | The globally unique `ID` to be used in selecting a single `SomethingDatum`. 60 | """ 61 | @@ -233,7 +233,7 @@ 62 | data: String 63 | 64 | """Reads a single `Something` that is related to this `SomethingDatum`.""" 65 | - somethingBySomeId: Something 66 | + some: Something 67 | } 68 | 69 | """A `SomethingDatum` edge in the connection.""" 70 | @@ -344,69 +344,69 @@ 71 | ): CreateSomethingDatumPayload 72 | 73 | """Updates a single `Something` using its globally unique id and a patch.""" 74 | - updateSomething( 75 | + updateSomethingByNodeId( 76 | """ 77 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 78 | """ 79 | - input: UpdateSomethingInput! 80 | + input: UpdateSomethingByNodeIdInput! 81 | ): UpdateSomethingPayload 82 | 83 | """Updates a single `Something` using a unique key and a patch.""" 84 | - updateSomethingBySomeId( 85 | + updateSomething( 86 | """ 87 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 88 | """ 89 | - input: UpdateSomethingBySomeIdInput! 90 | + input: UpdateSomethingInput! 91 | ): UpdateSomethingPayload 92 | 93 | """ 94 | Updates a single `SomethingDatum` using its globally unique id and a patch. 95 | """ 96 | - updateSomethingDatum( 97 | + updateSomethingDatumByNodeId( 98 | """ 99 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 100 | """ 101 | - input: UpdateSomethingDatumInput! 102 | + input: UpdateSomethingDatumByNodeIdInput! 103 | ): UpdateSomethingDatumPayload 104 | 105 | """Updates a single `SomethingDatum` using a unique key and a patch.""" 106 | - updateSomethingDatumBySomethingDataId( 107 | + updateSomethingDatum( 108 | """ 109 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 110 | """ 111 | - input: UpdateSomethingDatumBySomethingDataIdInput! 112 | + input: UpdateSomethingDatumInput! 113 | ): UpdateSomethingDatumPayload 114 | 115 | """Deletes a single `Something` using its globally unique id.""" 116 | - deleteSomething( 117 | + deleteSomethingByNodeId( 118 | """ 119 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 120 | """ 121 | - input: DeleteSomethingInput! 122 | + input: DeleteSomethingByNodeIdInput! 123 | ): DeleteSomethingPayload 124 | 125 | """Deletes a single `Something` using a unique key.""" 126 | - deleteSomethingBySomeId( 127 | + deleteSomething( 128 | """ 129 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 130 | """ 131 | - input: DeleteSomethingBySomeIdInput! 132 | + input: DeleteSomethingInput! 133 | ): DeleteSomethingPayload 134 | 135 | """Deletes a single `SomethingDatum` using its globally unique id.""" 136 | - deleteSomethingDatum( 137 | + deleteSomethingDatumByNodeId( 138 | """ 139 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 140 | """ 141 | - input: DeleteSomethingDatumInput! 142 | + input: DeleteSomethingDatumByNodeIdInput! 143 | ): DeleteSomethingDatumPayload 144 | 145 | """Deletes a single `SomethingDatum` using a unique key.""" 146 | - deleteSomethingDatumBySomethingDataId( 147 | + deleteSomethingDatum( 148 | """ 149 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. 150 | """ 151 | - input: DeleteSomethingDatumBySomethingDataIdInput! 152 | + input: DeleteSomethingDatumInput! 153 | ): DeleteSomethingDatumPayload 154 | } 155 | 156 | @@ -468,7 +468,7 @@ 157 | query: Query 158 | 159 | """Reads a single `Something` that is related to this `SomethingDatum`.""" 160 | - somethingBySomeId: Something 161 | + some: Something 162 | 163 | """An edge for our `SomethingDatum`. May be used by Relay 1.""" 164 | somethingDatumEdge( 165 | @@ -519,8 +519,8 @@ 166 | ): SomethingsEdge 167 | } 168 | 169 | -"""All input for the `updateSomething` mutation.""" 170 | -input UpdateSomethingInput { 171 | +"""All input for the `updateSomethingByNodeId` mutation.""" 172 | +input UpdateSomethingByNodeIdInput { 173 | """ 174 | An arbitrary string value with no semantic meaning. Will be included in the 175 | payload verbatim. May be used to track mutations by the client. 176 | @@ -535,7 +535,7 @@ 177 | """ 178 | An object where the defined keys will be set on the `Something` being updated. 179 | """ 180 | - somethingPatch: SomethingPatch! 181 | + patch: SomethingPatch! 182 | } 183 | 184 | """ 185 | @@ -546,8 +546,8 @@ 186 | name: String 187 | } 188 | 189 | -"""All input for the `updateSomethingBySomeId` mutation.""" 190 | -input UpdateSomethingBySomeIdInput { 191 | +"""All input for the `updateSomething` mutation.""" 192 | +input UpdateSomethingInput { 193 | """ 194 | An arbitrary string value with no semantic meaning. Will be included in the 195 | payload verbatim. May be used to track mutations by the client. 196 | @@ -557,7 +557,7 @@ 197 | """ 198 | An object where the defined keys will be set on the `Something` being updated. 199 | """ 200 | - somethingPatch: SomethingPatch! 201 | + patch: SomethingPatch! 202 | someId: Int! 203 | } 204 | 205 | @@ -578,7 +578,7 @@ 206 | query: Query 207 | 208 | """Reads a single `Something` that is related to this `SomethingDatum`.""" 209 | - somethingBySomeId: Something 210 | + some: Something 211 | 212 | """An edge for our `SomethingDatum`. May be used by Relay 1.""" 213 | somethingDatumEdge( 214 | @@ -587,8 +587,8 @@ 215 | ): SomethingDataEdge 216 | } 217 | 218 | -"""All input for the `updateSomethingDatum` mutation.""" 219 | -input UpdateSomethingDatumInput { 220 | +"""All input for the `updateSomethingDatumByNodeId` mutation.""" 221 | +input UpdateSomethingDatumByNodeIdInput { 222 | """ 223 | An arbitrary string value with no semantic meaning. Will be included in the 224 | payload verbatim. May be used to track mutations by the client. 225 | @@ -603,7 +603,7 @@ 226 | """ 227 | An object where the defined keys will be set on the `SomethingDatum` being updated. 228 | """ 229 | - somethingDatumPatch: SomethingDatumPatch! 230 | + patch: SomethingDatumPatch! 231 | } 232 | 233 | """ 234 | @@ -615,8 +615,8 @@ 235 | data: String 236 | } 237 | 238 | -"""All input for the `updateSomethingDatumBySomethingDataId` mutation.""" 239 | -input UpdateSomethingDatumBySomethingDataIdInput { 240 | +"""All input for the `updateSomethingDatum` mutation.""" 241 | +input UpdateSomethingDatumInput { 242 | """ 243 | An arbitrary string value with no semantic meaning. Will be included in the 244 | payload verbatim. May be used to track mutations by the client. 245 | @@ -626,7 +626,7 @@ 246 | """ 247 | An object where the defined keys will be set on the `SomethingDatum` being updated. 248 | """ 249 | - somethingDatumPatch: SomethingDatumPatch! 250 | + patch: SomethingDatumPatch! 251 | somethingDataId: Int! 252 | } 253 | 254 | @@ -640,7 +640,7 @@ 255 | 256 | """The `Something` that was deleted by this mutation.""" 257 | something: Something 258 | - deletedSomethingId: ID 259 | + deletedSomethingNodeId: ID 260 | 261 | """ 262 | Our root query field type. Allows us to run any query from our mutation payload. 263 | @@ -654,8 +654,8 @@ 264 | ): SomethingsEdge 265 | } 266 | 267 | -"""All input for the `deleteSomething` mutation.""" 268 | -input DeleteSomethingInput { 269 | +"""All input for the `deleteSomethingByNodeId` mutation.""" 270 | +input DeleteSomethingByNodeIdInput { 271 | """ 272 | An arbitrary string value with no semantic meaning. Will be included in the 273 | payload verbatim. May be used to track mutations by the client. 274 | @@ -668,8 +668,8 @@ 275 | nodeId: ID! 276 | } 277 | 278 | -"""All input for the `deleteSomethingBySomeId` mutation.""" 279 | -input DeleteSomethingBySomeIdInput { 280 | +"""All input for the `deleteSomething` mutation.""" 281 | +input DeleteSomethingInput { 282 | """ 283 | An arbitrary string value with no semantic meaning. Will be included in the 284 | payload verbatim. May be used to track mutations by the client. 285 | @@ -688,7 +688,7 @@ 286 | 287 | """The `SomethingDatum` that was deleted by this mutation.""" 288 | somethingDatum: SomethingDatum 289 | - deletedSomethingDatumId: ID 290 | + deletedSomethingDatumNodeId: ID 291 | 292 | """ 293 | Our root query field type. Allows us to run any query from our mutation payload. 294 | @@ -696,7 +696,7 @@ 295 | query: Query 296 | 297 | """Reads a single `Something` that is related to this `SomethingDatum`.""" 298 | - somethingBySomeId: Something 299 | + some: Something 300 | 301 | """An edge for our `SomethingDatum`. May be used by Relay 1.""" 302 | somethingDatumEdge( 303 | @@ -705,8 +705,8 @@ 304 | ): SomethingDataEdge 305 | } 306 | 307 | -"""All input for the `deleteSomethingDatum` mutation.""" 308 | -input DeleteSomethingDatumInput { 309 | +"""All input for the `deleteSomethingDatumByNodeId` mutation.""" 310 | +input DeleteSomethingDatumByNodeIdInput { 311 | """ 312 | An arbitrary string value with no semantic meaning. Will be included in the 313 | payload verbatim. May be used to track mutations by the client. 314 | @@ -719,8 +719,8 @@ 315 | nodeId: ID! 316 | } 317 | 318 | -"""All input for the `deleteSomethingDatumBySomethingDataId` mutation.""" 319 | -input DeleteSomethingDatumBySomethingDataIdInput { 320 | +"""All input for the `deleteSomethingDatum` mutation.""" 321 | +input DeleteSomethingDatumInput { 322 | """ 323 | An arbitrary string value with no semantic meaning. Will be included in the 324 | payload verbatim. May be used to track mutations by the client. 325 | -------------------------------------------------------------------------------- /tests/issue_29/schema.sql: -------------------------------------------------------------------------------- 1 | create table something( 2 | something_id integer not null primary key, 3 | name text 4 | ); 5 | 6 | create table something_data( 7 | something_data_id integer not null primary key, 8 | something_id integer not null references something on delete cascade, 9 | data text 10 | ); 11 | 12 | comment on column something.something_id is E'@name some_id'; 13 | comment on column something_data.something_id is E'@name some_id'; 14 | -------------------------------------------------------------------------------- /tests/list_suffix_omit/schema.graphql.diff: -------------------------------------------------------------------------------- 1 | --- unsimplified 2 | +++ simplified 3 | @@ -18,7 +18,7 @@ 4 | ): Node 5 | 6 | """Reads and enables pagination through a set of `Beverage`.""" 7 | - allBeverages( 8 | + beverages( 9 | """Only read the first `n` values of the set.""" 10 | first: Int 11 | 12 | @@ -47,7 +47,7 @@ 13 | ): BeveragesConnection 14 | 15 | """Reads a set of `Beverage`.""" 16 | - allBeveragesList( 17 | + beveragesList( 18 | """Only read the first `n` values of the set.""" 19 | first: Int 20 | 21 | @@ -64,7 +64,7 @@ 22 | ): [Beverage!] 23 | 24 | """Reads and enables pagination through a set of `Company`.""" 25 | - allCompanies( 26 | + companiesConnection( 27 | """Only read the first `n` values of the set.""" 28 | first: Int 29 | 30 | @@ -93,7 +93,7 @@ 31 | ): CompaniesConnection 32 | 33 | """Reads a set of `Company`.""" 34 | - allCompaniesList( 35 | + companies( 36 | """Only read the first `n` values of the set.""" 37 | first: Int 38 | 39 | @@ -108,8 +108,8 @@ 40 | """ 41 | condition: CompanyCondition 42 | ): [Company!] 43 | - beverageById(id: Int!): Beverage 44 | - companyById(id: Int!): Company 45 | + beverage(id: Int!): Beverage 46 | + company(id: Int!): Company 47 | 48 | """Reads and enables pagination through a set of `Beverage`.""" 49 | listInclude( 50 | @@ -142,7 +142,7 @@ 51 | ): [Beverage] 52 | 53 | """Reads and enables pagination through a set of `Beverage`.""" 54 | - listOmit( 55 | + listOmitConnection( 56 | """Only read the first `n` values of the set.""" 57 | first: Int 58 | 59 | @@ -163,7 +163,7 @@ 60 | ): BeveragesConnection 61 | 62 | """Reads and enables pagination through a set of `Beverage`.""" 63 | - listOmitList( 64 | + listOmit( 65 | """Only read the first `n` values of the set.""" 66 | first: Int 67 | 68 | @@ -172,13 +172,13 @@ 69 | ): [Beverage] 70 | 71 | """Reads a single `Beverage` using its globally unique `ID`.""" 72 | - beverage( 73 | + beverageByNodeId( 74 | """The globally unique `ID` to be used in selecting a single `Beverage`.""" 75 | nodeId: ID! 76 | ): Beverage 77 | 78 | """Reads a single `Company` using its globally unique `ID`.""" 79 | - company( 80 | + companyByNodeId( 81 | """The globally unique `ID` to be used in selecting a single `Company`.""" 82 | nodeId: ID! 83 | ): Company 84 | @@ -220,10 +220,10 @@ 85 | name: String! 86 | 87 | """Reads a single `Company` that is related to this `Beverage`.""" 88 | - companyByCompanyId: Company 89 | + company: Company 90 | 91 | """Reads a single `Company` that is related to this `Beverage`.""" 92 | - companyByDistributorId: Company 93 | + distributor: Company 94 | } 95 | 96 | type Company implements Node { 97 | @@ -235,7 +235,7 @@ 98 | name: String! 99 | 100 | """Reads and enables pagination through a set of `Beverage`.""" 101 | - beveragesByCompanyId( 102 | + beveragesConnection( 103 | """Only read the first `n` values of the set.""" 104 | first: Int 105 | 106 | @@ -264,7 +264,7 @@ 107 | ): BeveragesConnection! 108 | 109 | """Reads and enables pagination through a set of `Beverage`.""" 110 | - beveragesByCompanyIdList( 111 | + beverages( 112 | """Only read the first `n` values of the set.""" 113 | first: Int 114 | 115 | @@ -357,7 +357,7 @@ 116 | ): [Beverage] 117 | 118 | """Reads and enables pagination through a set of `Beverage`.""" 119 | - computedListOmit( 120 | + computedListOmitConnection( 121 | """Only read the first `n` values of the set.""" 122 | first: Int 123 | 124 | @@ -378,7 +378,7 @@ 125 | ): BeveragesConnection! 126 | 127 | """Reads and enables pagination through a set of `Beverage`.""" 128 | - computedListOmitList( 129 | + computedListOmit( 130 | """Only read the first `n` values of the set.""" 131 | first: Int 132 | 133 | -------------------------------------------------------------------------------- /tests/list_suffix_omit/schema.simplified.graphql: -------------------------------------------------------------------------------- 1 | """The root query type which gives access points into the data universe.""" 2 | type Query implements Node { 3 | """ 4 | Exposes the root query type nested one level down. This is helpful for Relay 1 5 | which can only query top level fields if they are in a particular form. 6 | """ 7 | query: Query! 8 | 9 | """ 10 | The root query type must be a `Node` to work well with Relay 1 mutations. This just resolves to `query`. 11 | """ 12 | nodeId: ID! 13 | 14 | """Fetches an object given its globally unique `ID`.""" 15 | node( 16 | """The globally unique `ID`.""" 17 | nodeId: ID! 18 | ): Node 19 | 20 | """Reads and enables pagination through a set of `Beverage`.""" 21 | beverages( 22 | """Only read the first `n` values of the set.""" 23 | first: Int 24 | 25 | """Only read the last `n` values of the set.""" 26 | last: Int 27 | 28 | """ 29 | Skip the first `n` values from our `after` cursor, an alternative to cursor 30 | based pagination. May not be used with `last`. 31 | """ 32 | offset: Int 33 | 34 | """Read all values in the set before (above) this cursor.""" 35 | before: Cursor 36 | 37 | """Read all values in the set after (below) this cursor.""" 38 | after: Cursor 39 | 40 | """The method to use when ordering `Beverage`.""" 41 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 42 | 43 | """ 44 | A condition to be used in determining which values should be returned by the collection. 45 | """ 46 | condition: BeverageCondition 47 | ): BeveragesConnection 48 | 49 | """Reads a set of `Beverage`.""" 50 | beveragesList( 51 | """Only read the first `n` values of the set.""" 52 | first: Int 53 | 54 | """Skip the first `n` values.""" 55 | offset: Int 56 | 57 | """The method to use when ordering `Beverage`.""" 58 | orderBy: [BeveragesOrderBy!] 59 | 60 | """ 61 | A condition to be used in determining which values should be returned by the collection. 62 | """ 63 | condition: BeverageCondition 64 | ): [Beverage!] 65 | 66 | """Reads and enables pagination through a set of `Company`.""" 67 | companiesConnection( 68 | """Only read the first `n` values of the set.""" 69 | first: Int 70 | 71 | """Only read the last `n` values of the set.""" 72 | last: Int 73 | 74 | """ 75 | Skip the first `n` values from our `after` cursor, an alternative to cursor 76 | based pagination. May not be used with `last`. 77 | """ 78 | offset: Int 79 | 80 | """Read all values in the set before (above) this cursor.""" 81 | before: Cursor 82 | 83 | """Read all values in the set after (below) this cursor.""" 84 | after: Cursor 85 | 86 | """The method to use when ordering `Company`.""" 87 | orderBy: [CompaniesOrderBy!] = [PRIMARY_KEY_ASC] 88 | 89 | """ 90 | A condition to be used in determining which values should be returned by the collection. 91 | """ 92 | condition: CompanyCondition 93 | ): CompaniesConnection 94 | 95 | """Reads a set of `Company`.""" 96 | companies( 97 | """Only read the first `n` values of the set.""" 98 | first: Int 99 | 100 | """Skip the first `n` values.""" 101 | offset: Int 102 | 103 | """The method to use when ordering `Company`.""" 104 | orderBy: [CompaniesOrderBy!] 105 | 106 | """ 107 | A condition to be used in determining which values should be returned by the collection. 108 | """ 109 | condition: CompanyCondition 110 | ): [Company!] 111 | beverage(id: Int!): Beverage 112 | company(id: Int!): Company 113 | 114 | """Reads and enables pagination through a set of `Beverage`.""" 115 | listInclude( 116 | """Only read the first `n` values of the set.""" 117 | first: Int 118 | 119 | """Only read the last `n` values of the set.""" 120 | last: Int 121 | 122 | """ 123 | Skip the first `n` values from our `after` cursor, an alternative to cursor 124 | based pagination. May not be used with `last`. 125 | """ 126 | offset: Int 127 | 128 | """Read all values in the set before (above) this cursor.""" 129 | before: Cursor 130 | 131 | """Read all values in the set after (below) this cursor.""" 132 | after: Cursor 133 | ): BeveragesConnection 134 | 135 | """Reads and enables pagination through a set of `Beverage`.""" 136 | listIncludeList( 137 | """Only read the first `n` values of the set.""" 138 | first: Int 139 | 140 | """Skip the first `n` values.""" 141 | offset: Int 142 | ): [Beverage] 143 | 144 | """Reads and enables pagination through a set of `Beverage`.""" 145 | listOmitConnection( 146 | """Only read the first `n` values of the set.""" 147 | first: Int 148 | 149 | """Only read the last `n` values of the set.""" 150 | last: Int 151 | 152 | """ 153 | Skip the first `n` values from our `after` cursor, an alternative to cursor 154 | based pagination. May not be used with `last`. 155 | """ 156 | offset: Int 157 | 158 | """Read all values in the set before (above) this cursor.""" 159 | before: Cursor 160 | 161 | """Read all values in the set after (below) this cursor.""" 162 | after: Cursor 163 | ): BeveragesConnection 164 | 165 | """Reads and enables pagination through a set of `Beverage`.""" 166 | listOmit( 167 | """Only read the first `n` values of the set.""" 168 | first: Int 169 | 170 | """Skip the first `n` values.""" 171 | offset: Int 172 | ): [Beverage] 173 | 174 | """Reads a single `Beverage` using its globally unique `ID`.""" 175 | beverageByNodeId( 176 | """The globally unique `ID` to be used in selecting a single `Beverage`.""" 177 | nodeId: ID! 178 | ): Beverage 179 | 180 | """Reads a single `Company` using its globally unique `ID`.""" 181 | companyByNodeId( 182 | """The globally unique `ID` to be used in selecting a single `Company`.""" 183 | nodeId: ID! 184 | ): Company 185 | } 186 | 187 | """An object with a globally unique `ID`.""" 188 | interface Node { 189 | """ 190 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 191 | """ 192 | nodeId: ID! 193 | } 194 | 195 | """A connection to a list of `Beverage` values.""" 196 | type BeveragesConnection { 197 | """A list of `Beverage` objects.""" 198 | nodes: [Beverage]! 199 | 200 | """ 201 | A list of edges which contains the `Beverage` and cursor to aid in pagination. 202 | """ 203 | edges: [BeveragesEdge!]! 204 | 205 | """Information to aid in pagination.""" 206 | pageInfo: PageInfo! 207 | 208 | """The count of *all* `Beverage` you could get from the connection.""" 209 | totalCount: Int! 210 | } 211 | 212 | type Beverage implements Node { 213 | """ 214 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 215 | """ 216 | nodeId: ID! 217 | id: Int! 218 | companyId: Int! 219 | distributorId: Int 220 | name: String! 221 | 222 | """Reads a single `Company` that is related to this `Beverage`.""" 223 | company: Company 224 | 225 | """Reads a single `Company` that is related to this `Beverage`.""" 226 | distributor: Company 227 | } 228 | 229 | type Company implements Node { 230 | """ 231 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 232 | """ 233 | nodeId: ID! 234 | id: Int! 235 | name: String! 236 | 237 | """Reads and enables pagination through a set of `Beverage`.""" 238 | beveragesConnection( 239 | """Only read the first `n` values of the set.""" 240 | first: Int 241 | 242 | """Only read the last `n` values of the set.""" 243 | last: Int 244 | 245 | """ 246 | Skip the first `n` values from our `after` cursor, an alternative to cursor 247 | based pagination. May not be used with `last`. 248 | """ 249 | offset: Int 250 | 251 | """Read all values in the set before (above) this cursor.""" 252 | before: Cursor 253 | 254 | """Read all values in the set after (below) this cursor.""" 255 | after: Cursor 256 | 257 | """The method to use when ordering `Beverage`.""" 258 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 259 | 260 | """ 261 | A condition to be used in determining which values should be returned by the collection. 262 | """ 263 | condition: BeverageCondition 264 | ): BeveragesConnection! 265 | 266 | """Reads and enables pagination through a set of `Beverage`.""" 267 | beverages( 268 | """Only read the first `n` values of the set.""" 269 | first: Int 270 | 271 | """Skip the first `n` values.""" 272 | offset: Int 273 | 274 | """The method to use when ordering `Beverage`.""" 275 | orderBy: [BeveragesOrderBy!] 276 | 277 | """ 278 | A condition to be used in determining which values should be returned by the collection. 279 | """ 280 | condition: BeverageCondition 281 | ): [Beverage!]! 282 | 283 | """Reads and enables pagination through a set of `Beverage`.""" 284 | distributedBeverages( 285 | """Only read the first `n` values of the set.""" 286 | first: Int 287 | 288 | """Only read the last `n` values of the set.""" 289 | last: Int 290 | 291 | """ 292 | Skip the first `n` values from our `after` cursor, an alternative to cursor 293 | based pagination. May not be used with `last`. 294 | """ 295 | offset: Int 296 | 297 | """Read all values in the set before (above) this cursor.""" 298 | before: Cursor 299 | 300 | """Read all values in the set after (below) this cursor.""" 301 | after: Cursor 302 | 303 | """The method to use when ordering `Beverage`.""" 304 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 305 | 306 | """ 307 | A condition to be used in determining which values should be returned by the collection. 308 | """ 309 | condition: BeverageCondition 310 | ): BeveragesConnection! 311 | 312 | """Reads and enables pagination through a set of `Beverage`.""" 313 | distributedBeveragesListing( 314 | """Only read the first `n` values of the set.""" 315 | first: Int 316 | 317 | """Skip the first `n` values.""" 318 | offset: Int 319 | 320 | """The method to use when ordering `Beverage`.""" 321 | orderBy: [BeveragesOrderBy!] 322 | 323 | """ 324 | A condition to be used in determining which values should be returned by the collection. 325 | """ 326 | condition: BeverageCondition 327 | ): [Beverage!]! 328 | 329 | """Reads and enables pagination through a set of `Beverage`.""" 330 | computedListInclude( 331 | """Only read the first `n` values of the set.""" 332 | first: Int 333 | 334 | """Only read the last `n` values of the set.""" 335 | last: Int 336 | 337 | """ 338 | Skip the first `n` values from our `after` cursor, an alternative to cursor 339 | based pagination. May not be used with `last`. 340 | """ 341 | offset: Int 342 | 343 | """Read all values in the set before (above) this cursor.""" 344 | before: Cursor 345 | 346 | """Read all values in the set after (below) this cursor.""" 347 | after: Cursor 348 | ): BeveragesConnection! 349 | 350 | """Reads and enables pagination through a set of `Beverage`.""" 351 | computedListIncludeList( 352 | """Only read the first `n` values of the set.""" 353 | first: Int 354 | 355 | """Skip the first `n` values.""" 356 | offset: Int 357 | ): [Beverage] 358 | 359 | """Reads and enables pagination through a set of `Beverage`.""" 360 | computedListOmitConnection( 361 | """Only read the first `n` values of the set.""" 362 | first: Int 363 | 364 | """Only read the last `n` values of the set.""" 365 | last: Int 366 | 367 | """ 368 | Skip the first `n` values from our `after` cursor, an alternative to cursor 369 | based pagination. May not be used with `last`. 370 | """ 371 | offset: Int 372 | 373 | """Read all values in the set before (above) this cursor.""" 374 | before: Cursor 375 | 376 | """Read all values in the set after (below) this cursor.""" 377 | after: Cursor 378 | ): BeveragesConnection! 379 | 380 | """Reads and enables pagination through a set of `Beverage`.""" 381 | computedListOmit( 382 | """Only read the first `n` values of the set.""" 383 | first: Int 384 | 385 | """Skip the first `n` values.""" 386 | offset: Int 387 | ): [Beverage] 388 | } 389 | 390 | """A location in a connection that can be used for resuming pagination.""" 391 | scalar Cursor 392 | 393 | """Methods to use when ordering `Beverage`.""" 394 | enum BeveragesOrderBy { 395 | NATURAL 396 | ID_ASC 397 | ID_DESC 398 | COMPANY_ID_ASC 399 | COMPANY_ID_DESC 400 | DISTRIBUTOR_ID_ASC 401 | DISTRIBUTOR_ID_DESC 402 | NAME_ASC 403 | NAME_DESC 404 | PRIMARY_KEY_ASC 405 | PRIMARY_KEY_DESC 406 | } 407 | 408 | """ 409 | A condition to be used against `Beverage` object types. All fields are tested 410 | for equality and combined with a logical ‘and.’ 411 | """ 412 | input BeverageCondition { 413 | """Checks for equality with the object’s `id` field.""" 414 | id: Int 415 | 416 | """Checks for equality with the object’s `companyId` field.""" 417 | companyId: Int 418 | 419 | """Checks for equality with the object’s `distributorId` field.""" 420 | distributorId: Int 421 | 422 | """Checks for equality with the object’s `name` field.""" 423 | name: String 424 | } 425 | 426 | """A `Beverage` edge in the connection.""" 427 | type BeveragesEdge { 428 | """A cursor for use in pagination.""" 429 | cursor: Cursor 430 | 431 | """The `Beverage` at the end of the edge.""" 432 | node: Beverage 433 | } 434 | 435 | """Information about pagination in a connection.""" 436 | type PageInfo { 437 | """When paginating forwards, are there more items?""" 438 | hasNextPage: Boolean! 439 | 440 | """When paginating backwards, are there more items?""" 441 | hasPreviousPage: Boolean! 442 | 443 | """When paginating backwards, the cursor to continue.""" 444 | startCursor: Cursor 445 | 446 | """When paginating forwards, the cursor to continue.""" 447 | endCursor: Cursor 448 | } 449 | 450 | """A connection to a list of `Company` values.""" 451 | type CompaniesConnection { 452 | """A list of `Company` objects.""" 453 | nodes: [Company]! 454 | 455 | """ 456 | A list of edges which contains the `Company` and cursor to aid in pagination. 457 | """ 458 | edges: [CompaniesEdge!]! 459 | 460 | """Information to aid in pagination.""" 461 | pageInfo: PageInfo! 462 | 463 | """The count of *all* `Company` you could get from the connection.""" 464 | totalCount: Int! 465 | } 466 | 467 | """A `Company` edge in the connection.""" 468 | type CompaniesEdge { 469 | """A cursor for use in pagination.""" 470 | cursor: Cursor 471 | 472 | """The `Company` at the end of the edge.""" 473 | node: Company 474 | } 475 | 476 | """Methods to use when ordering `Company`.""" 477 | enum CompaniesOrderBy { 478 | NATURAL 479 | ID_ASC 480 | ID_DESC 481 | NAME_ASC 482 | NAME_DESC 483 | PRIMARY_KEY_ASC 484 | PRIMARY_KEY_DESC 485 | } 486 | 487 | """ 488 | A condition to be used against `Company` object types. All fields are tested for equality and combined with a logical ‘and.’ 489 | """ 490 | input CompanyCondition { 491 | """Checks for equality with the object’s `id` field.""" 492 | id: Int 493 | 494 | """Checks for equality with the object’s `name` field.""" 495 | name: String 496 | } 497 | -------------------------------------------------------------------------------- /tests/list_suffix_omit/schema.sql: -------------------------------------------------------------------------------- 1 | create table companies ( 2 | id serial primary key, 3 | name text not null 4 | ); 5 | comment on table companies is E'@listSuffix omit'; 6 | 7 | create table beverages ( 8 | id serial primary key, 9 | company_id int not null references companies, 10 | distributor_id int references companies, 11 | name text not null 12 | ); 13 | comment on constraint "beverages_company_id_fkey" on "beverages" is E'@listSuffix omit'; 14 | -- @omitListSuffix has no effect when @foreignSimpleFieldName is set 15 | comment on constraint "beverages_distributor_id_fkey" on "beverages" is 16 | E'@foreignFieldName distributedBeverages\n@foreignSimpleFieldName distributedBeveragesListing\n@listSuffix omit'; 17 | 18 | create function list_omit() returns setof beverages as $$ 19 | select * from beverages; 20 | $$ language sql stable; 21 | comment on function list_omit() is E'@listSuffix omit'; 22 | 23 | create function list_include() returns setof beverages as $$ 24 | select * from beverages; 25 | $$ language sql stable; 26 | 27 | create function companies_computed_list_omit(company companies) returns setof beverages as $$ 28 | select * from beverages; 29 | $$ language sql stable; 30 | comment on function companies_computed_list_omit(companies) is E'@listSuffix omit'; 31 | 32 | create function companies_computed_list_include(company companies) returns setof beverages as $$ 33 | select * from beverages; 34 | $$ language sql stable; 35 | -------------------------------------------------------------------------------- /tests/list_suffix_omit/schema.unsimplified.graphql: -------------------------------------------------------------------------------- 1 | """The root query type which gives access points into the data universe.""" 2 | type Query implements Node { 3 | """ 4 | Exposes the root query type nested one level down. This is helpful for Relay 1 5 | which can only query top level fields if they are in a particular form. 6 | """ 7 | query: Query! 8 | 9 | """ 10 | The root query type must be a `Node` to work well with Relay 1 mutations. This just resolves to `query`. 11 | """ 12 | nodeId: ID! 13 | 14 | """Fetches an object given its globally unique `ID`.""" 15 | node( 16 | """The globally unique `ID`.""" 17 | nodeId: ID! 18 | ): Node 19 | 20 | """Reads and enables pagination through a set of `Beverage`.""" 21 | allBeverages( 22 | """Only read the first `n` values of the set.""" 23 | first: Int 24 | 25 | """Only read the last `n` values of the set.""" 26 | last: Int 27 | 28 | """ 29 | Skip the first `n` values from our `after` cursor, an alternative to cursor 30 | based pagination. May not be used with `last`. 31 | """ 32 | offset: Int 33 | 34 | """Read all values in the set before (above) this cursor.""" 35 | before: Cursor 36 | 37 | """Read all values in the set after (below) this cursor.""" 38 | after: Cursor 39 | 40 | """The method to use when ordering `Beverage`.""" 41 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 42 | 43 | """ 44 | A condition to be used in determining which values should be returned by the collection. 45 | """ 46 | condition: BeverageCondition 47 | ): BeveragesConnection 48 | 49 | """Reads a set of `Beverage`.""" 50 | allBeveragesList( 51 | """Only read the first `n` values of the set.""" 52 | first: Int 53 | 54 | """Skip the first `n` values.""" 55 | offset: Int 56 | 57 | """The method to use when ordering `Beverage`.""" 58 | orderBy: [BeveragesOrderBy!] 59 | 60 | """ 61 | A condition to be used in determining which values should be returned by the collection. 62 | """ 63 | condition: BeverageCondition 64 | ): [Beverage!] 65 | 66 | """Reads and enables pagination through a set of `Company`.""" 67 | allCompanies( 68 | """Only read the first `n` values of the set.""" 69 | first: Int 70 | 71 | """Only read the last `n` values of the set.""" 72 | last: Int 73 | 74 | """ 75 | Skip the first `n` values from our `after` cursor, an alternative to cursor 76 | based pagination. May not be used with `last`. 77 | """ 78 | offset: Int 79 | 80 | """Read all values in the set before (above) this cursor.""" 81 | before: Cursor 82 | 83 | """Read all values in the set after (below) this cursor.""" 84 | after: Cursor 85 | 86 | """The method to use when ordering `Company`.""" 87 | orderBy: [CompaniesOrderBy!] = [PRIMARY_KEY_ASC] 88 | 89 | """ 90 | A condition to be used in determining which values should be returned by the collection. 91 | """ 92 | condition: CompanyCondition 93 | ): CompaniesConnection 94 | 95 | """Reads a set of `Company`.""" 96 | allCompaniesList( 97 | """Only read the first `n` values of the set.""" 98 | first: Int 99 | 100 | """Skip the first `n` values.""" 101 | offset: Int 102 | 103 | """The method to use when ordering `Company`.""" 104 | orderBy: [CompaniesOrderBy!] 105 | 106 | """ 107 | A condition to be used in determining which values should be returned by the collection. 108 | """ 109 | condition: CompanyCondition 110 | ): [Company!] 111 | beverageById(id: Int!): Beverage 112 | companyById(id: Int!): Company 113 | 114 | """Reads and enables pagination through a set of `Beverage`.""" 115 | listInclude( 116 | """Only read the first `n` values of the set.""" 117 | first: Int 118 | 119 | """Only read the last `n` values of the set.""" 120 | last: Int 121 | 122 | """ 123 | Skip the first `n` values from our `after` cursor, an alternative to cursor 124 | based pagination. May not be used with `last`. 125 | """ 126 | offset: Int 127 | 128 | """Read all values in the set before (above) this cursor.""" 129 | before: Cursor 130 | 131 | """Read all values in the set after (below) this cursor.""" 132 | after: Cursor 133 | ): BeveragesConnection 134 | 135 | """Reads and enables pagination through a set of `Beverage`.""" 136 | listIncludeList( 137 | """Only read the first `n` values of the set.""" 138 | first: Int 139 | 140 | """Skip the first `n` values.""" 141 | offset: Int 142 | ): [Beverage] 143 | 144 | """Reads and enables pagination through a set of `Beverage`.""" 145 | listOmit( 146 | """Only read the first `n` values of the set.""" 147 | first: Int 148 | 149 | """Only read the last `n` values of the set.""" 150 | last: Int 151 | 152 | """ 153 | Skip the first `n` values from our `after` cursor, an alternative to cursor 154 | based pagination. May not be used with `last`. 155 | """ 156 | offset: Int 157 | 158 | """Read all values in the set before (above) this cursor.""" 159 | before: Cursor 160 | 161 | """Read all values in the set after (below) this cursor.""" 162 | after: Cursor 163 | ): BeveragesConnection 164 | 165 | """Reads and enables pagination through a set of `Beverage`.""" 166 | listOmitList( 167 | """Only read the first `n` values of the set.""" 168 | first: Int 169 | 170 | """Skip the first `n` values.""" 171 | offset: Int 172 | ): [Beverage] 173 | 174 | """Reads a single `Beverage` using its globally unique `ID`.""" 175 | beverage( 176 | """The globally unique `ID` to be used in selecting a single `Beverage`.""" 177 | nodeId: ID! 178 | ): Beverage 179 | 180 | """Reads a single `Company` using its globally unique `ID`.""" 181 | company( 182 | """The globally unique `ID` to be used in selecting a single `Company`.""" 183 | nodeId: ID! 184 | ): Company 185 | } 186 | 187 | """An object with a globally unique `ID`.""" 188 | interface Node { 189 | """ 190 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 191 | """ 192 | nodeId: ID! 193 | } 194 | 195 | """A connection to a list of `Beverage` values.""" 196 | type BeveragesConnection { 197 | """A list of `Beverage` objects.""" 198 | nodes: [Beverage]! 199 | 200 | """ 201 | A list of edges which contains the `Beverage` and cursor to aid in pagination. 202 | """ 203 | edges: [BeveragesEdge!]! 204 | 205 | """Information to aid in pagination.""" 206 | pageInfo: PageInfo! 207 | 208 | """The count of *all* `Beverage` you could get from the connection.""" 209 | totalCount: Int! 210 | } 211 | 212 | type Beverage implements Node { 213 | """ 214 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 215 | """ 216 | nodeId: ID! 217 | id: Int! 218 | companyId: Int! 219 | distributorId: Int 220 | name: String! 221 | 222 | """Reads a single `Company` that is related to this `Beverage`.""" 223 | companyByCompanyId: Company 224 | 225 | """Reads a single `Company` that is related to this `Beverage`.""" 226 | companyByDistributorId: Company 227 | } 228 | 229 | type Company implements Node { 230 | """ 231 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 232 | """ 233 | nodeId: ID! 234 | id: Int! 235 | name: String! 236 | 237 | """Reads and enables pagination through a set of `Beverage`.""" 238 | beveragesByCompanyId( 239 | """Only read the first `n` values of the set.""" 240 | first: Int 241 | 242 | """Only read the last `n` values of the set.""" 243 | last: Int 244 | 245 | """ 246 | Skip the first `n` values from our `after` cursor, an alternative to cursor 247 | based pagination. May not be used with `last`. 248 | """ 249 | offset: Int 250 | 251 | """Read all values in the set before (above) this cursor.""" 252 | before: Cursor 253 | 254 | """Read all values in the set after (below) this cursor.""" 255 | after: Cursor 256 | 257 | """The method to use when ordering `Beverage`.""" 258 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 259 | 260 | """ 261 | A condition to be used in determining which values should be returned by the collection. 262 | """ 263 | condition: BeverageCondition 264 | ): BeveragesConnection! 265 | 266 | """Reads and enables pagination through a set of `Beverage`.""" 267 | beveragesByCompanyIdList( 268 | """Only read the first `n` values of the set.""" 269 | first: Int 270 | 271 | """Skip the first `n` values.""" 272 | offset: Int 273 | 274 | """The method to use when ordering `Beverage`.""" 275 | orderBy: [BeveragesOrderBy!] 276 | 277 | """ 278 | A condition to be used in determining which values should be returned by the collection. 279 | """ 280 | condition: BeverageCondition 281 | ): [Beverage!]! 282 | 283 | """Reads and enables pagination through a set of `Beverage`.""" 284 | distributedBeverages( 285 | """Only read the first `n` values of the set.""" 286 | first: Int 287 | 288 | """Only read the last `n` values of the set.""" 289 | last: Int 290 | 291 | """ 292 | Skip the first `n` values from our `after` cursor, an alternative to cursor 293 | based pagination. May not be used with `last`. 294 | """ 295 | offset: Int 296 | 297 | """Read all values in the set before (above) this cursor.""" 298 | before: Cursor 299 | 300 | """Read all values in the set after (below) this cursor.""" 301 | after: Cursor 302 | 303 | """The method to use when ordering `Beverage`.""" 304 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 305 | 306 | """ 307 | A condition to be used in determining which values should be returned by the collection. 308 | """ 309 | condition: BeverageCondition 310 | ): BeveragesConnection! 311 | 312 | """Reads and enables pagination through a set of `Beverage`.""" 313 | distributedBeveragesListing( 314 | """Only read the first `n` values of the set.""" 315 | first: Int 316 | 317 | """Skip the first `n` values.""" 318 | offset: Int 319 | 320 | """The method to use when ordering `Beverage`.""" 321 | orderBy: [BeveragesOrderBy!] 322 | 323 | """ 324 | A condition to be used in determining which values should be returned by the collection. 325 | """ 326 | condition: BeverageCondition 327 | ): [Beverage!]! 328 | 329 | """Reads and enables pagination through a set of `Beverage`.""" 330 | computedListInclude( 331 | """Only read the first `n` values of the set.""" 332 | first: Int 333 | 334 | """Only read the last `n` values of the set.""" 335 | last: Int 336 | 337 | """ 338 | Skip the first `n` values from our `after` cursor, an alternative to cursor 339 | based pagination. May not be used with `last`. 340 | """ 341 | offset: Int 342 | 343 | """Read all values in the set before (above) this cursor.""" 344 | before: Cursor 345 | 346 | """Read all values in the set after (below) this cursor.""" 347 | after: Cursor 348 | ): BeveragesConnection! 349 | 350 | """Reads and enables pagination through a set of `Beverage`.""" 351 | computedListIncludeList( 352 | """Only read the first `n` values of the set.""" 353 | first: Int 354 | 355 | """Skip the first `n` values.""" 356 | offset: Int 357 | ): [Beverage] 358 | 359 | """Reads and enables pagination through a set of `Beverage`.""" 360 | computedListOmit( 361 | """Only read the first `n` values of the set.""" 362 | first: Int 363 | 364 | """Only read the last `n` values of the set.""" 365 | last: Int 366 | 367 | """ 368 | Skip the first `n` values from our `after` cursor, an alternative to cursor 369 | based pagination. May not be used with `last`. 370 | """ 371 | offset: Int 372 | 373 | """Read all values in the set before (above) this cursor.""" 374 | before: Cursor 375 | 376 | """Read all values in the set after (below) this cursor.""" 377 | after: Cursor 378 | ): BeveragesConnection! 379 | 380 | """Reads and enables pagination through a set of `Beverage`.""" 381 | computedListOmitList( 382 | """Only read the first `n` values of the set.""" 383 | first: Int 384 | 385 | """Skip the first `n` values.""" 386 | offset: Int 387 | ): [Beverage] 388 | } 389 | 390 | """A location in a connection that can be used for resuming pagination.""" 391 | scalar Cursor 392 | 393 | """Methods to use when ordering `Beverage`.""" 394 | enum BeveragesOrderBy { 395 | NATURAL 396 | ID_ASC 397 | ID_DESC 398 | COMPANY_ID_ASC 399 | COMPANY_ID_DESC 400 | DISTRIBUTOR_ID_ASC 401 | DISTRIBUTOR_ID_DESC 402 | NAME_ASC 403 | NAME_DESC 404 | PRIMARY_KEY_ASC 405 | PRIMARY_KEY_DESC 406 | } 407 | 408 | """ 409 | A condition to be used against `Beverage` object types. All fields are tested 410 | for equality and combined with a logical ‘and.’ 411 | """ 412 | input BeverageCondition { 413 | """Checks for equality with the object’s `id` field.""" 414 | id: Int 415 | 416 | """Checks for equality with the object’s `companyId` field.""" 417 | companyId: Int 418 | 419 | """Checks for equality with the object’s `distributorId` field.""" 420 | distributorId: Int 421 | 422 | """Checks for equality with the object’s `name` field.""" 423 | name: String 424 | } 425 | 426 | """A `Beverage` edge in the connection.""" 427 | type BeveragesEdge { 428 | """A cursor for use in pagination.""" 429 | cursor: Cursor 430 | 431 | """The `Beverage` at the end of the edge.""" 432 | node: Beverage 433 | } 434 | 435 | """Information about pagination in a connection.""" 436 | type PageInfo { 437 | """When paginating forwards, are there more items?""" 438 | hasNextPage: Boolean! 439 | 440 | """When paginating backwards, are there more items?""" 441 | hasPreviousPage: Boolean! 442 | 443 | """When paginating backwards, the cursor to continue.""" 444 | startCursor: Cursor 445 | 446 | """When paginating forwards, the cursor to continue.""" 447 | endCursor: Cursor 448 | } 449 | 450 | """A connection to a list of `Company` values.""" 451 | type CompaniesConnection { 452 | """A list of `Company` objects.""" 453 | nodes: [Company]! 454 | 455 | """ 456 | A list of edges which contains the `Company` and cursor to aid in pagination. 457 | """ 458 | edges: [CompaniesEdge!]! 459 | 460 | """Information to aid in pagination.""" 461 | pageInfo: PageInfo! 462 | 463 | """The count of *all* `Company` you could get from the connection.""" 464 | totalCount: Int! 465 | } 466 | 467 | """A `Company` edge in the connection.""" 468 | type CompaniesEdge { 469 | """A cursor for use in pagination.""" 470 | cursor: Cursor 471 | 472 | """The `Company` at the end of the edge.""" 473 | node: Company 474 | } 475 | 476 | """Methods to use when ordering `Company`.""" 477 | enum CompaniesOrderBy { 478 | NATURAL 479 | ID_ASC 480 | ID_DESC 481 | NAME_ASC 482 | NAME_DESC 483 | PRIMARY_KEY_ASC 484 | PRIMARY_KEY_DESC 485 | } 486 | 487 | """ 488 | A condition to be used against `Company` object types. All fields are tested for equality and combined with a logical ‘and.’ 489 | """ 490 | input CompanyCondition { 491 | """Checks for equality with the object’s `id` field.""" 492 | id: Int 493 | 494 | """Checks for equality with the object’s `name` field.""" 495 | name: String 496 | } 497 | -------------------------------------------------------------------------------- /tests/list_suffix_omit/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "disableDefaultMutations": true 3 | } 4 | -------------------------------------------------------------------------------- /tests/multikey_refs/schema.sql: -------------------------------------------------------------------------------- 1 | create table users ( 2 | id serial primary key, 3 | username text 4 | ); 5 | 6 | create table organizations ( 7 | id serial primary key, 8 | name text 9 | ); 10 | 11 | create table teams ( 12 | id serial primary key, 13 | name text 14 | ); 15 | 16 | create table goals ( 17 | organization_id int not null references organizations, 18 | team_id int not null references teams, 19 | goal_uuid uuid not null default gen_random_uuid(), 20 | aim text, 21 | primary key(organization_id, team_id, goal_uuid) 22 | ); 23 | 24 | create table goal_contributors ( 25 | id serial primary key, 26 | organization_id int not null references organizations, 27 | team_id int not null references teams, 28 | goal_uuid uuid not null, 29 | contributor_id int not null references users, 30 | contribution text, 31 | 32 | foreign key (organization_id, team_id, goal_uuid) references goals 33 | ); 34 | -------------------------------------------------------------------------------- /tests/pg_omit_list_suffix/schema.graphql.diff: -------------------------------------------------------------------------------- 1 | --- unsimplified 2 | +++ simplified 3 | @@ -18,7 +18,7 @@ 4 | ): Node 5 | 6 | """Reads and enables pagination through a set of `Beverage`.""" 7 | - allBeverages( 8 | + beveragesConnection( 9 | """Only read the first `n` values of the set.""" 10 | first: Int 11 | 12 | @@ -47,7 +47,7 @@ 13 | ): BeveragesConnection 14 | 15 | """Reads a set of `Beverage`.""" 16 | - allBeveragesList( 17 | + beverages( 18 | """Only read the first `n` values of the set.""" 19 | first: Int 20 | 21 | @@ -64,7 +64,7 @@ 22 | ): [Beverage!] 23 | 24 | """Reads and enables pagination through a set of `Company`.""" 25 | - allCompanies( 26 | + companies( 27 | """Only read the first `n` values of the set.""" 28 | first: Int 29 | 30 | @@ -93,7 +93,7 @@ 31 | ): CompaniesConnection 32 | 33 | """Reads a set of `Company`.""" 34 | - allCompaniesList( 35 | + companiesList( 36 | """Only read the first `n` values of the set.""" 37 | first: Int 38 | 39 | @@ -108,8 +108,8 @@ 40 | """ 41 | condition: CompanyCondition 42 | ): [Company!] 43 | - beverageById(id: Int!): Beverage 44 | - companyById(id: Int!): Company 45 | + beverage(id: Int!): Beverage 46 | + company(id: Int!): Company 47 | 48 | """Reads and enables pagination through a set of `Beverage`.""" 49 | listInclude( 50 | @@ -142,7 +142,7 @@ 51 | ): [Beverage] 52 | 53 | """Reads and enables pagination through a set of `Beverage`.""" 54 | - listOmit( 55 | + listOmitConnection( 56 | """Only read the first `n` values of the set.""" 57 | first: Int 58 | 59 | @@ -163,7 +163,7 @@ 60 | ): BeveragesConnection 61 | 62 | """Reads and enables pagination through a set of `Beverage`.""" 63 | - listOmitList( 64 | + listOmit( 65 | """Only read the first `n` values of the set.""" 66 | first: Int 67 | 68 | @@ -172,13 +172,13 @@ 69 | ): [Beverage] 70 | 71 | """Reads a single `Beverage` using its globally unique `ID`.""" 72 | - beverage( 73 | + beverageByNodeId( 74 | """The globally unique `ID` to be used in selecting a single `Beverage`.""" 75 | nodeId: ID! 76 | ): Beverage 77 | 78 | """Reads a single `Company` using its globally unique `ID`.""" 79 | - company( 80 | + companyByNodeId( 81 | """The globally unique `ID` to be used in selecting a single `Company`.""" 82 | nodeId: ID! 83 | ): Company 84 | @@ -220,10 +220,10 @@ 85 | name: String! 86 | 87 | """Reads a single `Company` that is related to this `Beverage`.""" 88 | - companyByCompanyId: Company 89 | + company: Company 90 | 91 | """Reads a single `Company` that is related to this `Beverage`.""" 92 | - companyByDistributorId: Company 93 | + distributor: Company 94 | } 95 | 96 | type Company implements Node { 97 | @@ -235,7 +235,7 @@ 98 | name: String! 99 | 100 | """Reads and enables pagination through a set of `Beverage`.""" 101 | - beveragesByCompanyId( 102 | + beverages( 103 | """Only read the first `n` values of the set.""" 104 | first: Int 105 | 106 | @@ -264,7 +264,7 @@ 107 | ): BeveragesConnection! 108 | 109 | """Reads and enables pagination through a set of `Beverage`.""" 110 | - beveragesByCompanyIdList( 111 | + beveragesList( 112 | """Only read the first `n` values of the set.""" 113 | first: Int 114 | 115 | @@ -357,7 +357,7 @@ 116 | ): [Beverage] 117 | 118 | """Reads and enables pagination through a set of `Beverage`.""" 119 | - computedListOmit( 120 | + computedListOmitConnection( 121 | """Only read the first `n` values of the set.""" 122 | first: Int 123 | 124 | @@ -378,7 +378,7 @@ 125 | ): BeveragesConnection! 126 | 127 | """Reads and enables pagination through a set of `Beverage`.""" 128 | - computedListOmitList( 129 | + computedListOmit( 130 | """Only read the first `n` values of the set.""" 131 | first: Int 132 | 133 | -------------------------------------------------------------------------------- /tests/pg_omit_list_suffix/schema.simplified.graphql: -------------------------------------------------------------------------------- 1 | """The root query type which gives access points into the data universe.""" 2 | type Query implements Node { 3 | """ 4 | Exposes the root query type nested one level down. This is helpful for Relay 1 5 | which can only query top level fields if they are in a particular form. 6 | """ 7 | query: Query! 8 | 9 | """ 10 | The root query type must be a `Node` to work well with Relay 1 mutations. This just resolves to `query`. 11 | """ 12 | nodeId: ID! 13 | 14 | """Fetches an object given its globally unique `ID`.""" 15 | node( 16 | """The globally unique `ID`.""" 17 | nodeId: ID! 18 | ): Node 19 | 20 | """Reads and enables pagination through a set of `Beverage`.""" 21 | beveragesConnection( 22 | """Only read the first `n` values of the set.""" 23 | first: Int 24 | 25 | """Only read the last `n` values of the set.""" 26 | last: Int 27 | 28 | """ 29 | Skip the first `n` values from our `after` cursor, an alternative to cursor 30 | based pagination. May not be used with `last`. 31 | """ 32 | offset: Int 33 | 34 | """Read all values in the set before (above) this cursor.""" 35 | before: Cursor 36 | 37 | """Read all values in the set after (below) this cursor.""" 38 | after: Cursor 39 | 40 | """The method to use when ordering `Beverage`.""" 41 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 42 | 43 | """ 44 | A condition to be used in determining which values should be returned by the collection. 45 | """ 46 | condition: BeverageCondition 47 | ): BeveragesConnection 48 | 49 | """Reads a set of `Beverage`.""" 50 | beverages( 51 | """Only read the first `n` values of the set.""" 52 | first: Int 53 | 54 | """Skip the first `n` values.""" 55 | offset: Int 56 | 57 | """The method to use when ordering `Beverage`.""" 58 | orderBy: [BeveragesOrderBy!] 59 | 60 | """ 61 | A condition to be used in determining which values should be returned by the collection. 62 | """ 63 | condition: BeverageCondition 64 | ): [Beverage!] 65 | 66 | """Reads and enables pagination through a set of `Company`.""" 67 | companies( 68 | """Only read the first `n` values of the set.""" 69 | first: Int 70 | 71 | """Only read the last `n` values of the set.""" 72 | last: Int 73 | 74 | """ 75 | Skip the first `n` values from our `after` cursor, an alternative to cursor 76 | based pagination. May not be used with `last`. 77 | """ 78 | offset: Int 79 | 80 | """Read all values in the set before (above) this cursor.""" 81 | before: Cursor 82 | 83 | """Read all values in the set after (below) this cursor.""" 84 | after: Cursor 85 | 86 | """The method to use when ordering `Company`.""" 87 | orderBy: [CompaniesOrderBy!] = [PRIMARY_KEY_ASC] 88 | 89 | """ 90 | A condition to be used in determining which values should be returned by the collection. 91 | """ 92 | condition: CompanyCondition 93 | ): CompaniesConnection 94 | 95 | """Reads a set of `Company`.""" 96 | companiesList( 97 | """Only read the first `n` values of the set.""" 98 | first: Int 99 | 100 | """Skip the first `n` values.""" 101 | offset: Int 102 | 103 | """The method to use when ordering `Company`.""" 104 | orderBy: [CompaniesOrderBy!] 105 | 106 | """ 107 | A condition to be used in determining which values should be returned by the collection. 108 | """ 109 | condition: CompanyCondition 110 | ): [Company!] 111 | beverage(id: Int!): Beverage 112 | company(id: Int!): Company 113 | 114 | """Reads and enables pagination through a set of `Beverage`.""" 115 | listInclude( 116 | """Only read the first `n` values of the set.""" 117 | first: Int 118 | 119 | """Only read the last `n` values of the set.""" 120 | last: Int 121 | 122 | """ 123 | Skip the first `n` values from our `after` cursor, an alternative to cursor 124 | based pagination. May not be used with `last`. 125 | """ 126 | offset: Int 127 | 128 | """Read all values in the set before (above) this cursor.""" 129 | before: Cursor 130 | 131 | """Read all values in the set after (below) this cursor.""" 132 | after: Cursor 133 | ): BeveragesConnection 134 | 135 | """Reads and enables pagination through a set of `Beverage`.""" 136 | listIncludeList( 137 | """Only read the first `n` values of the set.""" 138 | first: Int 139 | 140 | """Skip the first `n` values.""" 141 | offset: Int 142 | ): [Beverage] 143 | 144 | """Reads and enables pagination through a set of `Beverage`.""" 145 | listOmitConnection( 146 | """Only read the first `n` values of the set.""" 147 | first: Int 148 | 149 | """Only read the last `n` values of the set.""" 150 | last: Int 151 | 152 | """ 153 | Skip the first `n` values from our `after` cursor, an alternative to cursor 154 | based pagination. May not be used with `last`. 155 | """ 156 | offset: Int 157 | 158 | """Read all values in the set before (above) this cursor.""" 159 | before: Cursor 160 | 161 | """Read all values in the set after (below) this cursor.""" 162 | after: Cursor 163 | ): BeveragesConnection 164 | 165 | """Reads and enables pagination through a set of `Beverage`.""" 166 | listOmit( 167 | """Only read the first `n` values of the set.""" 168 | first: Int 169 | 170 | """Skip the first `n` values.""" 171 | offset: Int 172 | ): [Beverage] 173 | 174 | """Reads a single `Beverage` using its globally unique `ID`.""" 175 | beverageByNodeId( 176 | """The globally unique `ID` to be used in selecting a single `Beverage`.""" 177 | nodeId: ID! 178 | ): Beverage 179 | 180 | """Reads a single `Company` using its globally unique `ID`.""" 181 | companyByNodeId( 182 | """The globally unique `ID` to be used in selecting a single `Company`.""" 183 | nodeId: ID! 184 | ): Company 185 | } 186 | 187 | """An object with a globally unique `ID`.""" 188 | interface Node { 189 | """ 190 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 191 | """ 192 | nodeId: ID! 193 | } 194 | 195 | """A connection to a list of `Beverage` values.""" 196 | type BeveragesConnection { 197 | """A list of `Beverage` objects.""" 198 | nodes: [Beverage]! 199 | 200 | """ 201 | A list of edges which contains the `Beverage` and cursor to aid in pagination. 202 | """ 203 | edges: [BeveragesEdge!]! 204 | 205 | """Information to aid in pagination.""" 206 | pageInfo: PageInfo! 207 | 208 | """The count of *all* `Beverage` you could get from the connection.""" 209 | totalCount: Int! 210 | } 211 | 212 | type Beverage implements Node { 213 | """ 214 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 215 | """ 216 | nodeId: ID! 217 | id: Int! 218 | companyId: Int! 219 | distributorId: Int 220 | name: String! 221 | 222 | """Reads a single `Company` that is related to this `Beverage`.""" 223 | company: Company 224 | 225 | """Reads a single `Company` that is related to this `Beverage`.""" 226 | distributor: Company 227 | } 228 | 229 | type Company implements Node { 230 | """ 231 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 232 | """ 233 | nodeId: ID! 234 | id: Int! 235 | name: String! 236 | 237 | """Reads and enables pagination through a set of `Beverage`.""" 238 | beverages( 239 | """Only read the first `n` values of the set.""" 240 | first: Int 241 | 242 | """Only read the last `n` values of the set.""" 243 | last: Int 244 | 245 | """ 246 | Skip the first `n` values from our `after` cursor, an alternative to cursor 247 | based pagination. May not be used with `last`. 248 | """ 249 | offset: Int 250 | 251 | """Read all values in the set before (above) this cursor.""" 252 | before: Cursor 253 | 254 | """Read all values in the set after (below) this cursor.""" 255 | after: Cursor 256 | 257 | """The method to use when ordering `Beverage`.""" 258 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 259 | 260 | """ 261 | A condition to be used in determining which values should be returned by the collection. 262 | """ 263 | condition: BeverageCondition 264 | ): BeveragesConnection! 265 | 266 | """Reads and enables pagination through a set of `Beverage`.""" 267 | beveragesList( 268 | """Only read the first `n` values of the set.""" 269 | first: Int 270 | 271 | """Skip the first `n` values.""" 272 | offset: Int 273 | 274 | """The method to use when ordering `Beverage`.""" 275 | orderBy: [BeveragesOrderBy!] 276 | 277 | """ 278 | A condition to be used in determining which values should be returned by the collection. 279 | """ 280 | condition: BeverageCondition 281 | ): [Beverage!]! 282 | 283 | """Reads and enables pagination through a set of `Beverage`.""" 284 | distributedBeverages( 285 | """Only read the first `n` values of the set.""" 286 | first: Int 287 | 288 | """Only read the last `n` values of the set.""" 289 | last: Int 290 | 291 | """ 292 | Skip the first `n` values from our `after` cursor, an alternative to cursor 293 | based pagination. May not be used with `last`. 294 | """ 295 | offset: Int 296 | 297 | """Read all values in the set before (above) this cursor.""" 298 | before: Cursor 299 | 300 | """Read all values in the set after (below) this cursor.""" 301 | after: Cursor 302 | 303 | """The method to use when ordering `Beverage`.""" 304 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 305 | 306 | """ 307 | A condition to be used in determining which values should be returned by the collection. 308 | """ 309 | condition: BeverageCondition 310 | ): BeveragesConnection! 311 | 312 | """Reads and enables pagination through a set of `Beverage`.""" 313 | distributedBeveragesListing( 314 | """Only read the first `n` values of the set.""" 315 | first: Int 316 | 317 | """Skip the first `n` values.""" 318 | offset: Int 319 | 320 | """The method to use when ordering `Beverage`.""" 321 | orderBy: [BeveragesOrderBy!] 322 | 323 | """ 324 | A condition to be used in determining which values should be returned by the collection. 325 | """ 326 | condition: BeverageCondition 327 | ): [Beverage!]! 328 | 329 | """Reads and enables pagination through a set of `Beverage`.""" 330 | computedListInclude( 331 | """Only read the first `n` values of the set.""" 332 | first: Int 333 | 334 | """Only read the last `n` values of the set.""" 335 | last: Int 336 | 337 | """ 338 | Skip the first `n` values from our `after` cursor, an alternative to cursor 339 | based pagination. May not be used with `last`. 340 | """ 341 | offset: Int 342 | 343 | """Read all values in the set before (above) this cursor.""" 344 | before: Cursor 345 | 346 | """Read all values in the set after (below) this cursor.""" 347 | after: Cursor 348 | ): BeveragesConnection! 349 | 350 | """Reads and enables pagination through a set of `Beverage`.""" 351 | computedListIncludeList( 352 | """Only read the first `n` values of the set.""" 353 | first: Int 354 | 355 | """Skip the first `n` values.""" 356 | offset: Int 357 | ): [Beverage] 358 | 359 | """Reads and enables pagination through a set of `Beverage`.""" 360 | computedListOmitConnection( 361 | """Only read the first `n` values of the set.""" 362 | first: Int 363 | 364 | """Only read the last `n` values of the set.""" 365 | last: Int 366 | 367 | """ 368 | Skip the first `n` values from our `after` cursor, an alternative to cursor 369 | based pagination. May not be used with `last`. 370 | """ 371 | offset: Int 372 | 373 | """Read all values in the set before (above) this cursor.""" 374 | before: Cursor 375 | 376 | """Read all values in the set after (below) this cursor.""" 377 | after: Cursor 378 | ): BeveragesConnection! 379 | 380 | """Reads and enables pagination through a set of `Beverage`.""" 381 | computedListOmit( 382 | """Only read the first `n` values of the set.""" 383 | first: Int 384 | 385 | """Skip the first `n` values.""" 386 | offset: Int 387 | ): [Beverage] 388 | } 389 | 390 | """A location in a connection that can be used for resuming pagination.""" 391 | scalar Cursor 392 | 393 | """Methods to use when ordering `Beverage`.""" 394 | enum BeveragesOrderBy { 395 | NATURAL 396 | ID_ASC 397 | ID_DESC 398 | COMPANY_ID_ASC 399 | COMPANY_ID_DESC 400 | DISTRIBUTOR_ID_ASC 401 | DISTRIBUTOR_ID_DESC 402 | NAME_ASC 403 | NAME_DESC 404 | PRIMARY_KEY_ASC 405 | PRIMARY_KEY_DESC 406 | } 407 | 408 | """ 409 | A condition to be used against `Beverage` object types. All fields are tested 410 | for equality and combined with a logical ‘and.’ 411 | """ 412 | input BeverageCondition { 413 | """Checks for equality with the object’s `id` field.""" 414 | id: Int 415 | 416 | """Checks for equality with the object’s `companyId` field.""" 417 | companyId: Int 418 | 419 | """Checks for equality with the object’s `distributorId` field.""" 420 | distributorId: Int 421 | 422 | """Checks for equality with the object’s `name` field.""" 423 | name: String 424 | } 425 | 426 | """A `Beverage` edge in the connection.""" 427 | type BeveragesEdge { 428 | """A cursor for use in pagination.""" 429 | cursor: Cursor 430 | 431 | """The `Beverage` at the end of the edge.""" 432 | node: Beverage 433 | } 434 | 435 | """Information about pagination in a connection.""" 436 | type PageInfo { 437 | """When paginating forwards, are there more items?""" 438 | hasNextPage: Boolean! 439 | 440 | """When paginating backwards, are there more items?""" 441 | hasPreviousPage: Boolean! 442 | 443 | """When paginating backwards, the cursor to continue.""" 444 | startCursor: Cursor 445 | 446 | """When paginating forwards, the cursor to continue.""" 447 | endCursor: Cursor 448 | } 449 | 450 | """A connection to a list of `Company` values.""" 451 | type CompaniesConnection { 452 | """A list of `Company` objects.""" 453 | nodes: [Company]! 454 | 455 | """ 456 | A list of edges which contains the `Company` and cursor to aid in pagination. 457 | """ 458 | edges: [CompaniesEdge!]! 459 | 460 | """Information to aid in pagination.""" 461 | pageInfo: PageInfo! 462 | 463 | """The count of *all* `Company` you could get from the connection.""" 464 | totalCount: Int! 465 | } 466 | 467 | """A `Company` edge in the connection.""" 468 | type CompaniesEdge { 469 | """A cursor for use in pagination.""" 470 | cursor: Cursor 471 | 472 | """The `Company` at the end of the edge.""" 473 | node: Company 474 | } 475 | 476 | """Methods to use when ordering `Company`.""" 477 | enum CompaniesOrderBy { 478 | NATURAL 479 | ID_ASC 480 | ID_DESC 481 | NAME_ASC 482 | NAME_DESC 483 | PRIMARY_KEY_ASC 484 | PRIMARY_KEY_DESC 485 | } 486 | 487 | """ 488 | A condition to be used against `Company` object types. All fields are tested for equality and combined with a logical ‘and.’ 489 | """ 490 | input CompanyCondition { 491 | """Checks for equality with the object’s `id` field.""" 492 | id: Int 493 | 494 | """Checks for equality with the object’s `name` field.""" 495 | name: String 496 | } 497 | -------------------------------------------------------------------------------- /tests/pg_omit_list_suffix/schema.sql: -------------------------------------------------------------------------------- 1 | create table companies ( 2 | id serial primary key, 3 | name text not null 4 | ); 5 | comment on table companies is E'@listSuffix include'; 6 | 7 | create table beverages ( 8 | id serial primary key, 9 | company_id int not null references companies, 10 | distributor_id int references companies, 11 | name text not null 12 | ); 13 | comment on constraint "beverages_company_id_fkey" on "beverages" is E'@listSuffix include'; 14 | -- @omitListSuffix has no effect when @foreignSimpleFieldName is set 15 | comment on constraint "beverages_distributor_id_fkey" on "beverages" is 16 | E'@foreignFieldName distributedBeverages\n@foreignSimpleFieldName distributedBeveragesListing\n@listSuffix include'; 17 | 18 | create function list_include() returns setof beverages as $$ 19 | select * from beverages; 20 | $$ language sql stable; 21 | comment on function list_include() is E'@listSuffix include'; 22 | 23 | create function list_omit() returns setof beverages as $$ 24 | select * from beverages; 25 | $$ language sql stable; 26 | 27 | create function companies_computed_list_include(company companies) returns setof beverages as $$ 28 | select * from beverages; 29 | $$ language sql stable; 30 | comment on function companies_computed_list_include(companies) is E'@listSuffix include'; 31 | 32 | create function companies_computed_list_omit(company companies) returns setof beverages as $$ 33 | select * from beverages; 34 | $$ language sql stable; 35 | -------------------------------------------------------------------------------- /tests/pg_omit_list_suffix/schema.unsimplified.graphql: -------------------------------------------------------------------------------- 1 | """The root query type which gives access points into the data universe.""" 2 | type Query implements Node { 3 | """ 4 | Exposes the root query type nested one level down. This is helpful for Relay 1 5 | which can only query top level fields if they are in a particular form. 6 | """ 7 | query: Query! 8 | 9 | """ 10 | The root query type must be a `Node` to work well with Relay 1 mutations. This just resolves to `query`. 11 | """ 12 | nodeId: ID! 13 | 14 | """Fetches an object given its globally unique `ID`.""" 15 | node( 16 | """The globally unique `ID`.""" 17 | nodeId: ID! 18 | ): Node 19 | 20 | """Reads and enables pagination through a set of `Beverage`.""" 21 | allBeverages( 22 | """Only read the first `n` values of the set.""" 23 | first: Int 24 | 25 | """Only read the last `n` values of the set.""" 26 | last: Int 27 | 28 | """ 29 | Skip the first `n` values from our `after` cursor, an alternative to cursor 30 | based pagination. May not be used with `last`. 31 | """ 32 | offset: Int 33 | 34 | """Read all values in the set before (above) this cursor.""" 35 | before: Cursor 36 | 37 | """Read all values in the set after (below) this cursor.""" 38 | after: Cursor 39 | 40 | """The method to use when ordering `Beverage`.""" 41 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 42 | 43 | """ 44 | A condition to be used in determining which values should be returned by the collection. 45 | """ 46 | condition: BeverageCondition 47 | ): BeveragesConnection 48 | 49 | """Reads a set of `Beverage`.""" 50 | allBeveragesList( 51 | """Only read the first `n` values of the set.""" 52 | first: Int 53 | 54 | """Skip the first `n` values.""" 55 | offset: Int 56 | 57 | """The method to use when ordering `Beverage`.""" 58 | orderBy: [BeveragesOrderBy!] 59 | 60 | """ 61 | A condition to be used in determining which values should be returned by the collection. 62 | """ 63 | condition: BeverageCondition 64 | ): [Beverage!] 65 | 66 | """Reads and enables pagination through a set of `Company`.""" 67 | allCompanies( 68 | """Only read the first `n` values of the set.""" 69 | first: Int 70 | 71 | """Only read the last `n` values of the set.""" 72 | last: Int 73 | 74 | """ 75 | Skip the first `n` values from our `after` cursor, an alternative to cursor 76 | based pagination. May not be used with `last`. 77 | """ 78 | offset: Int 79 | 80 | """Read all values in the set before (above) this cursor.""" 81 | before: Cursor 82 | 83 | """Read all values in the set after (below) this cursor.""" 84 | after: Cursor 85 | 86 | """The method to use when ordering `Company`.""" 87 | orderBy: [CompaniesOrderBy!] = [PRIMARY_KEY_ASC] 88 | 89 | """ 90 | A condition to be used in determining which values should be returned by the collection. 91 | """ 92 | condition: CompanyCondition 93 | ): CompaniesConnection 94 | 95 | """Reads a set of `Company`.""" 96 | allCompaniesList( 97 | """Only read the first `n` values of the set.""" 98 | first: Int 99 | 100 | """Skip the first `n` values.""" 101 | offset: Int 102 | 103 | """The method to use when ordering `Company`.""" 104 | orderBy: [CompaniesOrderBy!] 105 | 106 | """ 107 | A condition to be used in determining which values should be returned by the collection. 108 | """ 109 | condition: CompanyCondition 110 | ): [Company!] 111 | beverageById(id: Int!): Beverage 112 | companyById(id: Int!): Company 113 | 114 | """Reads and enables pagination through a set of `Beverage`.""" 115 | listInclude( 116 | """Only read the first `n` values of the set.""" 117 | first: Int 118 | 119 | """Only read the last `n` values of the set.""" 120 | last: Int 121 | 122 | """ 123 | Skip the first `n` values from our `after` cursor, an alternative to cursor 124 | based pagination. May not be used with `last`. 125 | """ 126 | offset: Int 127 | 128 | """Read all values in the set before (above) this cursor.""" 129 | before: Cursor 130 | 131 | """Read all values in the set after (below) this cursor.""" 132 | after: Cursor 133 | ): BeveragesConnection 134 | 135 | """Reads and enables pagination through a set of `Beverage`.""" 136 | listIncludeList( 137 | """Only read the first `n` values of the set.""" 138 | first: Int 139 | 140 | """Skip the first `n` values.""" 141 | offset: Int 142 | ): [Beverage] 143 | 144 | """Reads and enables pagination through a set of `Beverage`.""" 145 | listOmit( 146 | """Only read the first `n` values of the set.""" 147 | first: Int 148 | 149 | """Only read the last `n` values of the set.""" 150 | last: Int 151 | 152 | """ 153 | Skip the first `n` values from our `after` cursor, an alternative to cursor 154 | based pagination. May not be used with `last`. 155 | """ 156 | offset: Int 157 | 158 | """Read all values in the set before (above) this cursor.""" 159 | before: Cursor 160 | 161 | """Read all values in the set after (below) this cursor.""" 162 | after: Cursor 163 | ): BeveragesConnection 164 | 165 | """Reads and enables pagination through a set of `Beverage`.""" 166 | listOmitList( 167 | """Only read the first `n` values of the set.""" 168 | first: Int 169 | 170 | """Skip the first `n` values.""" 171 | offset: Int 172 | ): [Beverage] 173 | 174 | """Reads a single `Beverage` using its globally unique `ID`.""" 175 | beverage( 176 | """The globally unique `ID` to be used in selecting a single `Beverage`.""" 177 | nodeId: ID! 178 | ): Beverage 179 | 180 | """Reads a single `Company` using its globally unique `ID`.""" 181 | company( 182 | """The globally unique `ID` to be used in selecting a single `Company`.""" 183 | nodeId: ID! 184 | ): Company 185 | } 186 | 187 | """An object with a globally unique `ID`.""" 188 | interface Node { 189 | """ 190 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 191 | """ 192 | nodeId: ID! 193 | } 194 | 195 | """A connection to a list of `Beverage` values.""" 196 | type BeveragesConnection { 197 | """A list of `Beverage` objects.""" 198 | nodes: [Beverage]! 199 | 200 | """ 201 | A list of edges which contains the `Beverage` and cursor to aid in pagination. 202 | """ 203 | edges: [BeveragesEdge!]! 204 | 205 | """Information to aid in pagination.""" 206 | pageInfo: PageInfo! 207 | 208 | """The count of *all* `Beverage` you could get from the connection.""" 209 | totalCount: Int! 210 | } 211 | 212 | type Beverage implements Node { 213 | """ 214 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 215 | """ 216 | nodeId: ID! 217 | id: Int! 218 | companyId: Int! 219 | distributorId: Int 220 | name: String! 221 | 222 | """Reads a single `Company` that is related to this `Beverage`.""" 223 | companyByCompanyId: Company 224 | 225 | """Reads a single `Company` that is related to this `Beverage`.""" 226 | companyByDistributorId: Company 227 | } 228 | 229 | type Company implements Node { 230 | """ 231 | A globally unique identifier. Can be used in various places throughout the system to identify this single value. 232 | """ 233 | nodeId: ID! 234 | id: Int! 235 | name: String! 236 | 237 | """Reads and enables pagination through a set of `Beverage`.""" 238 | beveragesByCompanyId( 239 | """Only read the first `n` values of the set.""" 240 | first: Int 241 | 242 | """Only read the last `n` values of the set.""" 243 | last: Int 244 | 245 | """ 246 | Skip the first `n` values from our `after` cursor, an alternative to cursor 247 | based pagination. May not be used with `last`. 248 | """ 249 | offset: Int 250 | 251 | """Read all values in the set before (above) this cursor.""" 252 | before: Cursor 253 | 254 | """Read all values in the set after (below) this cursor.""" 255 | after: Cursor 256 | 257 | """The method to use when ordering `Beverage`.""" 258 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 259 | 260 | """ 261 | A condition to be used in determining which values should be returned by the collection. 262 | """ 263 | condition: BeverageCondition 264 | ): BeveragesConnection! 265 | 266 | """Reads and enables pagination through a set of `Beverage`.""" 267 | beveragesByCompanyIdList( 268 | """Only read the first `n` values of the set.""" 269 | first: Int 270 | 271 | """Skip the first `n` values.""" 272 | offset: Int 273 | 274 | """The method to use when ordering `Beverage`.""" 275 | orderBy: [BeveragesOrderBy!] 276 | 277 | """ 278 | A condition to be used in determining which values should be returned by the collection. 279 | """ 280 | condition: BeverageCondition 281 | ): [Beverage!]! 282 | 283 | """Reads and enables pagination through a set of `Beverage`.""" 284 | distributedBeverages( 285 | """Only read the first `n` values of the set.""" 286 | first: Int 287 | 288 | """Only read the last `n` values of the set.""" 289 | last: Int 290 | 291 | """ 292 | Skip the first `n` values from our `after` cursor, an alternative to cursor 293 | based pagination. May not be used with `last`. 294 | """ 295 | offset: Int 296 | 297 | """Read all values in the set before (above) this cursor.""" 298 | before: Cursor 299 | 300 | """Read all values in the set after (below) this cursor.""" 301 | after: Cursor 302 | 303 | """The method to use when ordering `Beverage`.""" 304 | orderBy: [BeveragesOrderBy!] = [PRIMARY_KEY_ASC] 305 | 306 | """ 307 | A condition to be used in determining which values should be returned by the collection. 308 | """ 309 | condition: BeverageCondition 310 | ): BeveragesConnection! 311 | 312 | """Reads and enables pagination through a set of `Beverage`.""" 313 | distributedBeveragesListing( 314 | """Only read the first `n` values of the set.""" 315 | first: Int 316 | 317 | """Skip the first `n` values.""" 318 | offset: Int 319 | 320 | """The method to use when ordering `Beverage`.""" 321 | orderBy: [BeveragesOrderBy!] 322 | 323 | """ 324 | A condition to be used in determining which values should be returned by the collection. 325 | """ 326 | condition: BeverageCondition 327 | ): [Beverage!]! 328 | 329 | """Reads and enables pagination through a set of `Beverage`.""" 330 | computedListInclude( 331 | """Only read the first `n` values of the set.""" 332 | first: Int 333 | 334 | """Only read the last `n` values of the set.""" 335 | last: Int 336 | 337 | """ 338 | Skip the first `n` values from our `after` cursor, an alternative to cursor 339 | based pagination. May not be used with `last`. 340 | """ 341 | offset: Int 342 | 343 | """Read all values in the set before (above) this cursor.""" 344 | before: Cursor 345 | 346 | """Read all values in the set after (below) this cursor.""" 347 | after: Cursor 348 | ): BeveragesConnection! 349 | 350 | """Reads and enables pagination through a set of `Beverage`.""" 351 | computedListIncludeList( 352 | """Only read the first `n` values of the set.""" 353 | first: Int 354 | 355 | """Skip the first `n` values.""" 356 | offset: Int 357 | ): [Beverage] 358 | 359 | """Reads and enables pagination through a set of `Beverage`.""" 360 | computedListOmit( 361 | """Only read the first `n` values of the set.""" 362 | first: Int 363 | 364 | """Only read the last `n` values of the set.""" 365 | last: Int 366 | 367 | """ 368 | Skip the first `n` values from our `after` cursor, an alternative to cursor 369 | based pagination. May not be used with `last`. 370 | """ 371 | offset: Int 372 | 373 | """Read all values in the set before (above) this cursor.""" 374 | before: Cursor 375 | 376 | """Read all values in the set after (below) this cursor.""" 377 | after: Cursor 378 | ): BeveragesConnection! 379 | 380 | """Reads and enables pagination through a set of `Beverage`.""" 381 | computedListOmitList( 382 | """Only read the first `n` values of the set.""" 383 | first: Int 384 | 385 | """Skip the first `n` values.""" 386 | offset: Int 387 | ): [Beverage] 388 | } 389 | 390 | """A location in a connection that can be used for resuming pagination.""" 391 | scalar Cursor 392 | 393 | """Methods to use when ordering `Beverage`.""" 394 | enum BeveragesOrderBy { 395 | NATURAL 396 | ID_ASC 397 | ID_DESC 398 | COMPANY_ID_ASC 399 | COMPANY_ID_DESC 400 | DISTRIBUTOR_ID_ASC 401 | DISTRIBUTOR_ID_DESC 402 | NAME_ASC 403 | NAME_DESC 404 | PRIMARY_KEY_ASC 405 | PRIMARY_KEY_DESC 406 | } 407 | 408 | """ 409 | A condition to be used against `Beverage` object types. All fields are tested 410 | for equality and combined with a logical ‘and.’ 411 | """ 412 | input BeverageCondition { 413 | """Checks for equality with the object’s `id` field.""" 414 | id: Int 415 | 416 | """Checks for equality with the object’s `companyId` field.""" 417 | companyId: Int 418 | 419 | """Checks for equality with the object’s `distributorId` field.""" 420 | distributorId: Int 421 | 422 | """Checks for equality with the object’s `name` field.""" 423 | name: String 424 | } 425 | 426 | """A `Beverage` edge in the connection.""" 427 | type BeveragesEdge { 428 | """A cursor for use in pagination.""" 429 | cursor: Cursor 430 | 431 | """The `Beverage` at the end of the edge.""" 432 | node: Beverage 433 | } 434 | 435 | """Information about pagination in a connection.""" 436 | type PageInfo { 437 | """When paginating forwards, are there more items?""" 438 | hasNextPage: Boolean! 439 | 440 | """When paginating backwards, are there more items?""" 441 | hasPreviousPage: Boolean! 442 | 443 | """When paginating backwards, the cursor to continue.""" 444 | startCursor: Cursor 445 | 446 | """When paginating forwards, the cursor to continue.""" 447 | endCursor: Cursor 448 | } 449 | 450 | """A connection to a list of `Company` values.""" 451 | type CompaniesConnection { 452 | """A list of `Company` objects.""" 453 | nodes: [Company]! 454 | 455 | """ 456 | A list of edges which contains the `Company` and cursor to aid in pagination. 457 | """ 458 | edges: [CompaniesEdge!]! 459 | 460 | """Information to aid in pagination.""" 461 | pageInfo: PageInfo! 462 | 463 | """The count of *all* `Company` you could get from the connection.""" 464 | totalCount: Int! 465 | } 466 | 467 | """A `Company` edge in the connection.""" 468 | type CompaniesEdge { 469 | """A cursor for use in pagination.""" 470 | cursor: Cursor 471 | 472 | """The `Company` at the end of the edge.""" 473 | node: Company 474 | } 475 | 476 | """Methods to use when ordering `Company`.""" 477 | enum CompaniesOrderBy { 478 | NATURAL 479 | ID_ASC 480 | ID_DESC 481 | NAME_ASC 482 | NAME_DESC 483 | PRIMARY_KEY_ASC 484 | PRIMARY_KEY_DESC 485 | } 486 | 487 | """ 488 | A condition to be used against `Company` object types. All fields are tested for equality and combined with a logical ‘and.’ 489 | """ 490 | input CompanyCondition { 491 | """Checks for equality with the object’s `id` field.""" 492 | id: Int 493 | 494 | """Checks for equality with the object’s `name` field.""" 495 | name: String 496 | } 497 | -------------------------------------------------------------------------------- /tests/pg_omit_list_suffix/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "disableDefaultMutations": true, 3 | "graphileBuildOptions": { 4 | "pgOmitListSuffix": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": false, 7 | "sourceMap": false, 8 | "strict": true, 9 | "moduleResolution": "node", 10 | "forceConsistentCasingInFileNames": true 11 | } 12 | } 13 | --------------------------------------------------------------------------------