From 9f8c270c8fcf03df4a6b2b258ad0953b024dee0b Mon Sep 17 00:00:00 2001 From: Baris Tikir Date: Sun, 14 Aug 2022 14:38:58 +0200 Subject: [PATCH 1/9] Initial EdgeDB Plugin Setup --- packages/plugin-edgedb/.npmignore | 5 + packages/plugin-edgedb/CHANGELOG.md | 340 +++++++++++++++++++++ packages/plugin-edgedb/LICENSE | 6 + packages/plugin-edgedb/README.md | 4 + packages/plugin-edgedb/esm/.gitignore | 4 + packages/plugin-edgedb/esm/.npmignore | 0 packages/plugin-edgedb/esm/package.json | 3 + packages/plugin-edgedb/jest.config.js | 6 + packages/plugin-edgedb/package.json | 50 +++ packages/plugin-edgedb/src/global-types.ts | 36 +++ packages/plugin-edgedb/src/index.ts | 81 +++++ packages/plugin-edgedb/src/types.ts | 3 + packages/plugin-edgedb/tsconfig.json | 14 + packages/plugin-edgedb/tsconfig.type.json | 10 + 14 files changed, 562 insertions(+) create mode 100644 packages/plugin-edgedb/.npmignore create mode 100644 packages/plugin-edgedb/CHANGELOG.md create mode 100644 packages/plugin-edgedb/LICENSE create mode 100644 packages/plugin-edgedb/README.md create mode 100755 packages/plugin-edgedb/esm/.gitignore create mode 100755 packages/plugin-edgedb/esm/.npmignore create mode 100755 packages/plugin-edgedb/esm/package.json create mode 100644 packages/plugin-edgedb/jest.config.js create mode 100644 packages/plugin-edgedb/package.json create mode 100644 packages/plugin-edgedb/src/global-types.ts create mode 100644 packages/plugin-edgedb/src/index.ts create mode 100644 packages/plugin-edgedb/src/types.ts create mode 100644 packages/plugin-edgedb/tsconfig.json create mode 100644 packages/plugin-edgedb/tsconfig.type.json diff --git a/packages/plugin-edgedb/.npmignore b/packages/plugin-edgedb/.npmignore new file mode 100644 index 000000000..8f7d745f8 --- /dev/null +++ b/packages/plugin-edgedb/.npmignore @@ -0,0 +1,5 @@ +test +tests +.turbo +babel.config.js +tsconfig.tsbuildinfo \ No newline at end of file diff --git a/packages/plugin-edgedb/CHANGELOG.md b/packages/plugin-edgedb/CHANGELOG.md new file mode 100644 index 000000000..6e8763dfc --- /dev/null +++ b/packages/plugin-edgedb/CHANGELOG.md @@ -0,0 +1,340 @@ +# Change Log + +## 3.4.0 + +### Minor Changes + +- 3a7ff291: Refactor internal imports to remove import cycles + +### Patch Changes + +- 3a7ff291: Update dev dependencies + +## 3.3.0 + +### Minor Changes + +- ecb2714c: Add types entry to export map in package.json and update dev dependencies + + This should fix compatibility with typescripts new `"moduleResolution": "node12"` + +## 3.2.0 + +### Minor Changes + +- 241a385f: Add peer dependency on @pothos/core + +## 3.1.0 + +### Minor Changes + +- 6279235f: Update build process to use swc and move type definitions to dts directory + +### Patch Changes + +- 21a2454e: update dev dependencies + +## 3.0.2 + +### Patch Changes + +- 03aecf76: update .npmignore + +## 3.0.1 + +### Patch Changes + +- 2d9b21cd: Use workspace:\* for dev dependencies on pothos packages + +## 3.0.0 + +### Major Changes + +- 4caad5e4: Rename GiraphQL to Pothos + +## 2.11.0 + +### Minor Changes + +- 9307635a: Migrate build process to use turborepo + +## 2.10.1 + +### Patch Changes + +- c6aa732: graphql@15 type compatibility fix + +## 2.10.0 + +### Minor Changes + +- 48e9fd8: Add missing exports field to package.json + +## 2.9.1 + +### Patch Changes + +- c85dc33: Add types entry in package.json + +## 2.9.0 + +### Minor Changes + +- aeef5e5: Update dependencies + +## 2.8.0 + +### Minor Changes + +- 9107f29: Update dependencies (includes graphql 16) + +## 2.7.0 + +### Minor Changes + +- 17db3bd: Make type refs extendable by plugins + +## 2.6.2 + +### Patch Changes + +- c976bfe: Update dependencies + +## 2.6.1 + +### Patch Changes + +- 4150f92: Fixed esm transformer for path-imports from dependencies + +## 2.6.0 + +### Minor Changes + +- dc87e68: update esm build process so extensions are added during build rather than in source + +## 2.5.1 + +### Patch Changes + +- b4b8381: Updrade deps (typescript 4.4) + +## 2.5.0 + +### Minor Changes + +- a4c87cf: Use ".js" extensions everywhere and add module and exports to package.json to better + support ems in node + +## 2.4.2 + +### Patch Changes + +- f13208c: bump to fix latest tag + +## 2.4.1 + +### Patch Changes + +- 9ab8fbc: re-release previous version due to build-process issue + +## 2.4.0 + +### Minor Changes + +- 3dd3ff14: Updated dev dependencies, switched to pnpm, and added changesets for releases + +All notable changes to this project will be documented in this file. See +[Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +### 2.3.1 - 2021-08-03 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.3.1-alpha.0 - 2021-08-02 + +**Note:** Version bump only for package @giraphql/plugin-example + +## 2.3.0 - 2021-07-30 + +#### 🚀 Updates + +- add prisma plugin ([d427c82](https://github.com/hayes/giraphql/commit/d427c82)) + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.2.4 - 2021-07-23 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.2.4-alpha.0 - 2021-07-17 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.2.3 - 2021-07-10 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.2.2 - 2021-07-04 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.2.2-alpha.0 - 2021-07-04 + +#### 📦 Dependencies + +- upgrade typescript ([675f6a2](https://github.com/hayes/giraphql/commit/675f6a2)) + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.2.1 - 2021-07-02 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.2.0 - 2021-06-28 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.2.0-alpha.1 - 2021-06-28 + +**Note:** Version bump only for package @giraphql/plugin-example + +## 2.2.0-alpha.0 - 2021-06-28 + +#### 🚀 Updates + +- add errors plugin ([88509b4](https://github.com/hayes/giraphql/commit/88509b4)) + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.1.7 - 2021-06-11 + +#### 📦 Dependencies + +- update dev deps ([813d9d0](https://github.com/hayes/giraphql/commit/813d9d0)) + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.1.6 - 2021-06-10 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.1.6-alpha.0 - 2021-06-09 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.1.5 - 2021-05-18 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.1.4 - 2021-05-13 + +#### 📘 Docs + +- add docs for loadableNode ([1ae01e8](https://github.com/hayes/giraphql/commit/1ae01e8)) + +#### 🛠 Internals + +- add tests for loadableNode ([c1b49a0](https://github.com/hayes/giraphql/commit/c1b49a0)) + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.1.3 - 2021-05-12 + +#### 🛠 Internals + +- udate dev deps ([3251227](https://github.com/hayes/giraphql/commit/3251227)) + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.1.2 - 2021-05-10 + +#### 🐞 Fixes + +- update ci build command ([7e1d1d2](https://github.com/hayes/giraphql/commit/7e1d1d2)) + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.1.1 - 2021-05-10 + +#### 🐞 Fixes + +- force new version to fix esm build issue + ([25f1fd2](https://github.com/hayes/giraphql/commit/25f1fd2)) + +**Note:** Version bump only for package @giraphql/plugin-example + +## 2.1.0 - 2021-05-10 + +#### 🚀 Updates + +- add esm build for all packages ([d8bbdc9](https://github.com/hayes/giraphql/commit/d8bbdc9)) + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.8 - 2021-05-09 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.8-alpha.0 - 2021-05-08 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.7 - 2021-05-05 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.6 - 2021-05-05 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.6-alpha.0 - 2021-05-05 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.5 - 2021-05-02 + +#### 🛠 Internals + +- force version bumps and update validation to 2.0 range + ([07730b3](https://github.com/hayes/giraphql/commit/07730b3)) + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.4 - 2021-05-02 + +#### 🛠 Internals + +- migrate to @beemo/dev for dev tool configs + ([1da1283](https://github.com/hayes/giraphql/commit/1da1283)) + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.3 - 2021-04-16 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.3-alpha.0 - 2021-04-12 + +#### 📦 Dependencies + +- update dev dependencies ([25a15d4](https://github.com/hayes/giraphql/commit/25a15d4)) +- update dev deps ([cbfa0a4](https://github.com/hayes/giraphql/commit/cbfa0a4)) + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.2 - 2021-03-16 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.1 - 2021-02-19 + +**Note:** Version bump only for package @giraphql/plugin-example + +### 2.0.0 - 2021-02-16 + +#### 📘 Docs + +- add more docs o writing plugins ([b996fc6](https://github.com/hayes/giraphql/commit/b996fc6)) +- wip - plugin guide ([cf9c6ec](https://github.com/hayes/giraphql/commit/cf9c6ec)) + +**Note:** Version bump only for package @giraphql/plugin-example diff --git a/packages/plugin-edgedb/LICENSE b/packages/plugin-edgedb/LICENSE new file mode 100644 index 000000000..4d74f2bcf --- /dev/null +++ b/packages/plugin-edgedb/LICENSE @@ -0,0 +1,6 @@ +ISC License (ISC) +Copyright 2021 Michael Hayes + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/packages/plugin-edgedb/README.md b/packages/plugin-edgedb/README.md new file mode 100644 index 000000000..41574af8d --- /dev/null +++ b/packages/plugin-edgedb/README.md @@ -0,0 +1,4 @@ +# Example plugin for Pothos + +For details on writing your own Pothos plugin, check out the full guide at +https://pothos-graphql.dev/docs/guide/writing-plugins diff --git a/packages/plugin-edgedb/esm/.gitignore b/packages/plugin-edgedb/esm/.gitignore new file mode 100755 index 000000000..440de5113 --- /dev/null +++ b/packages/plugin-edgedb/esm/.gitignore @@ -0,0 +1,4 @@ +* +!.gitignore +!.npmignore +!package.json \ No newline at end of file diff --git a/packages/plugin-edgedb/esm/.npmignore b/packages/plugin-edgedb/esm/.npmignore new file mode 100755 index 000000000..e69de29bb diff --git a/packages/plugin-edgedb/esm/package.json b/packages/plugin-edgedb/esm/package.json new file mode 100755 index 000000000..3dbc1ca59 --- /dev/null +++ b/packages/plugin-edgedb/esm/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/packages/plugin-edgedb/jest.config.js b/packages/plugin-edgedb/jest.config.js new file mode 100644 index 000000000..7d6620a47 --- /dev/null +++ b/packages/plugin-edgedb/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + transformIgnorePatterns: [`node_modules`], + transform: { + '^.+\\.(t|j)sx?$': ['@swc/jest'], + }, +}; diff --git a/packages/plugin-edgedb/package.json b/packages/plugin-edgedb/package.json new file mode 100644 index 000000000..80764e825 --- /dev/null +++ b/packages/plugin-edgedb/package.json @@ -0,0 +1,50 @@ +{ + "name": "@pothos/plugin-edgedb", + "version": "3.4.0", + "description": "An edgedb plugin for Pothos", + "main": "./lib/index.js", + "types": "./dts/index.d.ts", + "module": "./esm/index.js", + "exports": { + "types": "./dts/index.d.ts", + "import": "./esm/index.js", + "require": "./lib/index.js" + }, + "scripts": { + "type": "tsc --project tsconfig.type.json", + "build": "pnpm build:clean && pnpm build:cjs && pnpm build:esm && pnpm build:dts", + "build:clean": "git clean -dfX esm lib", + "build:cjs": "swc src -d lib --config-file ../../.swcrc -C module.type=commonjs", + "build:esm": "swc src -d esm --config-file ../../.swcrc -C module.type=es6 && pnpm esm:extensions", + "build:dts": "tsc", + "esm:extensions": "TS_NODE_PROJECT=../../tsconfig.json node -r @swc-node/register ../../.config/esm-transformer.ts" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hayes/pothos.git" + }, + "author": "Michael Hayes", + "license": "ISC", + "keywords": [ + "giraphql", + "pothos", + "graphql", + "schema", + "edgedb", + "plugin" + ], + "publishConfig": { + "access": "public" + }, + "peerDependencies": { + "@pothos/core": "*", + "graphql": ">=15.1.0" + }, + "devDependencies": { + "@pothos/core": "workspace:*", + "@pothos/test-utils": "workspace:*", + "graphql": "16.5.0", + "graphql-tag": "^2.12.6" + }, + "gitHead": "9dfe52f1975f41a111e01bf96a20033a914e2acc" +} diff --git a/packages/plugin-edgedb/src/global-types.ts b/packages/plugin-edgedb/src/global-types.ts new file mode 100644 index 000000000..55fff687c --- /dev/null +++ b/packages/plugin-edgedb/src/global-types.ts @@ -0,0 +1,36 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { FieldNullability, InputFieldMap, SchemaTypes, TypeParam } from '@pothos/core'; +import { EdgeDBPluginOptions } from './types'; + +import type { PothosEdgeDBPlugin } from '.'; + +declare global { + export namespace PothosSchemaTypes { + export interface Plugins { + edgedb: PothosEdgeDBPlugin; + } + + export interface SchemaBuilderOptions { + optionInRootOfConfig?: boolean; + nestedOptionsObject?: EdgeDBPluginOptions; + } + + export interface BuildSchemaOptions { + customBuildTimeOptions?: boolean; + } + + export interface ObjectTypeOptions { + optionOnObject?: boolean; + } + + export interface MutationFieldOptions< + Types extends SchemaTypes, + Type extends TypeParam, + Nullable extends FieldNullability, + Args extends InputFieldMap, + ResolveReturnShape, + > { + customMutationFieldOption?: boolean; + } + } +} diff --git a/packages/plugin-edgedb/src/index.ts b/packages/plugin-edgedb/src/index.ts new file mode 100644 index 000000000..2ef90c386 --- /dev/null +++ b/packages/plugin-edgedb/src/index.ts @@ -0,0 +1,81 @@ +/* eslint-disable no-console */ +import './global-types'; +import { GraphQLFieldResolver, GraphQLSchema, GraphQLTypeResolver } from 'graphql'; +import SchemaBuilder, { + BasePlugin, + PothosEnumValueConfig, + PothosInputFieldConfig, + PothosInterfaceTypeConfig, + PothosOutputFieldConfig, + PothosTypeConfig, + PothosUnionTypeConfig, + SchemaTypes, +} from '@pothos/core'; + +export * from './types'; + +const pluginName = 'edgedb' as const; + +export default pluginName; + +export class PothosEdgeDBPlugin extends BasePlugin { + override onTypeConfig(typeConfig: PothosTypeConfig) { + console.log(this.builder.options.nestedOptionsObject?.exampleOption); + console.log(this.options.customBuildTimeOptions); + + if (typeConfig.kind === 'Object') { + console.log(typeConfig.pothosOptions.optionOnObject); + } + + return typeConfig; + } + + override onOutputFieldConfig(fieldConfig: PothosOutputFieldConfig) { + if (fieldConfig.kind === 'Mutation') { + console.log(fieldConfig.pothosOptions.customMutationFieldOption); + } + + return fieldConfig; + } + + override onInputFieldConfig(fieldConfig: PothosInputFieldConfig) { + return fieldConfig; + } + + override onEnumValueConfig(valueConfig: PothosEnumValueConfig) { + return valueConfig; + } + + override beforeBuild() {} + + override afterBuild(schema: GraphQLSchema): GraphQLSchema { + return schema; + } + + override wrapResolve( + resolver: GraphQLFieldResolver, + fieldConfig: PothosOutputFieldConfig, + ): GraphQLFieldResolver { + return (parent, args, context, info) => { + console.log(`Resolving ${info.parentType}.${info.fieldName}`); + + return resolver(parent, args, context, info); + }; + } + + override wrapSubscribe( + subscribe: GraphQLFieldResolver | undefined, + fieldConfig: PothosOutputFieldConfig, + ) { + return subscribe; + } + + override wrapResolveType( + resolveType: GraphQLTypeResolver, + typeConfig: PothosInterfaceTypeConfig | PothosUnionTypeConfig, + ) { + return resolveType; + } +} + +SchemaBuilder.registerPlugin(pluginName, PothosEdgeDBPlugin); diff --git a/packages/plugin-edgedb/src/types.ts b/packages/plugin-edgedb/src/types.ts new file mode 100644 index 000000000..67a1d897d --- /dev/null +++ b/packages/plugin-edgedb/src/types.ts @@ -0,0 +1,3 @@ +export interface EdgeDBPluginOptions { + exampleOption?: boolean; +} diff --git a/packages/plugin-edgedb/tsconfig.json b/packages/plugin-edgedb/tsconfig.json new file mode 100644 index 000000000..b5a6d2490 --- /dev/null +++ b/packages/plugin-edgedb/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "noEmit": false, + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "dts", + "rootDir": "src" + }, + "include": [ + "src/**/*" + ], + "extends": "../../tsconfig.options.json", +} \ No newline at end of file diff --git a/packages/plugin-edgedb/tsconfig.type.json b/packages/plugin-edgedb/tsconfig.type.json new file mode 100644 index 000000000..31e222cc4 --- /dev/null +++ b/packages/plugin-edgedb/tsconfig.type.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "skipLibCheck": false + }, + "extends": "../../tsconfig.options.json", + "include": [ + "src/**/*", + "tests/**/*" + ] +} From 3c28ac76355246cd1e6ebb4fce3dd6463e07d8cc Mon Sep 17 00:00:00 2001 From: Baris Tikir Date: Fri, 19 Aug 2022 01:34:22 +0200 Subject: [PATCH 2/9] Add Type System for EdgeDBObject API --- packages/plugin-edgedb/.gitignore | 4 + packages/plugin-edgedb/.npmignore | 2 + packages/plugin-edgedb/README.md | 94 ++++- packages/plugin-edgedb/dbschema/default.esdl | 64 +++ .../dbschema/migrations/00001.edgeql | 18 + .../dbschema/migrations/00002.edgeql | 56 +++ packages/plugin-edgedb/package.json | 9 +- .../plugin-edgedb/src/edgedb-field-builder.ts | 196 +++++++++ packages/plugin-edgedb/src/field-builder.ts | 11 + packages/plugin-edgedb/src/global-types.ts | 92 ++++- packages/plugin-edgedb/src/index.ts | 77 ++-- packages/plugin-edgedb/src/object-ref.ts | 12 + packages/plugin-edgedb/src/schema-builder.ts | 41 ++ packages/plugin-edgedb/src/types.ts | 390 +++++++++++++++++- packages/plugin-edgedb/src/util/datamodel.ts | 68 +++ .../plugin-edgedb/src/util/description.ts | 17 + packages/plugin-edgedb/src/util/get-client.ts | 23 ++ packages/plugin-edgedb/src/util/links.ts | 5 + .../plugin-edgedb/src/util/relation-map.ts | 41 ++ packages/plugin-edgedb/src/util/target.ts | 17 + packages/plugin-edgedb/tests/.gitignore | 1 + .../tests/__snapshots__/index.test.ts.snap | 34 ++ .../plugin-edgedb/tests/example/builder.ts | 35 ++ packages/plugin-edgedb/tests/example/db.ts | 14 + .../tests/example/schema/index.ts | 68 +++ .../plugin-edgedb/tests/example/server.ts | 12 + packages/plugin-edgedb/tests/index.test.ts | 45 ++ packages/plugin-edgedb/tsconfig.json | 7 +- pnpm-lock.yaml | 46 +++ 29 files changed, 1441 insertions(+), 58 deletions(-) create mode 100644 packages/plugin-edgedb/.gitignore create mode 100644 packages/plugin-edgedb/dbschema/default.esdl create mode 100644 packages/plugin-edgedb/dbschema/migrations/00001.edgeql create mode 100644 packages/plugin-edgedb/dbschema/migrations/00002.edgeql create mode 100644 packages/plugin-edgedb/src/edgedb-field-builder.ts create mode 100644 packages/plugin-edgedb/src/field-builder.ts create mode 100644 packages/plugin-edgedb/src/object-ref.ts create mode 100644 packages/plugin-edgedb/src/schema-builder.ts create mode 100644 packages/plugin-edgedb/src/util/datamodel.ts create mode 100644 packages/plugin-edgedb/src/util/description.ts create mode 100644 packages/plugin-edgedb/src/util/get-client.ts create mode 100644 packages/plugin-edgedb/src/util/links.ts create mode 100644 packages/plugin-edgedb/src/util/relation-map.ts create mode 100644 packages/plugin-edgedb/src/util/target.ts create mode 100644 packages/plugin-edgedb/tests/.gitignore create mode 100644 packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap create mode 100644 packages/plugin-edgedb/tests/example/builder.ts create mode 100644 packages/plugin-edgedb/tests/example/db.ts create mode 100644 packages/plugin-edgedb/tests/example/schema/index.ts create mode 100644 packages/plugin-edgedb/tests/example/server.ts create mode 100644 packages/plugin-edgedb/tests/index.test.ts diff --git a/packages/plugin-edgedb/.gitignore b/packages/plugin-edgedb/.gitignore new file mode 100644 index 000000000..29fe61ab8 --- /dev/null +++ b/packages/plugin-edgedb/.gitignore @@ -0,0 +1,4 @@ +edgedb.toml +dbschema/edgeql-js/* +TODO.md +api-design.md \ No newline at end of file diff --git a/packages/plugin-edgedb/.npmignore b/packages/plugin-edgedb/.npmignore index 8f7d745f8..fc61aaad2 100644 --- a/packages/plugin-edgedb/.npmignore +++ b/packages/plugin-edgedb/.npmignore @@ -1,3 +1,5 @@ +dbschema +edgedb.toml test tests .turbo diff --git a/packages/plugin-edgedb/README.md b/packages/plugin-edgedb/README.md index 41574af8d..365a64d56 100644 --- a/packages/plugin-edgedb/README.md +++ b/packages/plugin-edgedb/README.md @@ -1,4 +1,92 @@ -# Example plugin for Pothos +# EdgeDB Plugin for Pothos -For details on writing your own Pothos plugin, check out the full guide at -https://pothos-graphql.dev/docs/guide/writing-plugins +This plugin provides tighter integration with `edgedb`, making it easier to define edgedb based +object types. + +## Example + +Here is a quick example of what an API using this plugin might look like. There is a more thorough +breakdown of what the methods and options used in the example below. + +```typescript +// Create an object type based on a edgedb model +// without providing any custom type information +builder.edgeDBObject('User', { + fields: (t) => ({ + // expose fields from the database + id: t.exposeID('id'), + email: t.exposeString('email'), + name: t.exposeString('name', { nullable: true }), + // Multi Link field for Users Posts + posts: t.link("posts"), + }), +}); + +builder.queryType({ + fields: (t) => ({ + // Define a field that issues an optimized edgedb query + me: t.edgeDBField({ + type: 'User', + resolve: async (query, root, args, ctx, info) => { + const db_query = e + .select(e.User, (user) => { + // the `query` argument will add in the `select`s fields to + // resolve as much of the request in a single query as possible + ...query, + filter: e.op(user.id, '=', ctx.user.id), + }); + + return await db.run(db_query); + } + }), + }), +}); +``` + +Given this schema, you would be able to resolve a query like the following with a single edgedb +query. + +```graphql +query { + me { + email + posts { + title + author { + id + } + } + } +} +``` + +## EdgeDB Expressions + +The `e` variable provides everything you need to build an edgedb query. All EdgeQL commands, +standard library functions, and types are available as properties on e. + +[Source](https://www.edgedb.com/docs/clients/js/querybuilder#expressions) + +```ts +import e from './dbschema/edgeql-js'; + +// commands +e.select; +e.insert; +e.update; +e.delete; + +// types +e.str; +e.bool; +e.cal.local_date; +e.User; +e.Post; +e....; + +// functions +e.str_upper; +e.len; +e.count; +e.math.stddev; +``` diff --git a/packages/plugin-edgedb/dbschema/default.esdl b/packages/plugin-edgedb/dbschema/default.esdl new file mode 100644 index 000000000..e62103918 --- /dev/null +++ b/packages/plugin-edgedb/dbschema/default.esdl @@ -0,0 +1,64 @@ +module default { + type Post { + required property big_int_id -> bigint { + constraint exclusive; + }; + required property created_at -> datetime { + default := datetime_current(); + constraint exclusive; + readonly := true; + }; + required property updated_at -> datetime; + + required property title -> str; + property content -> str; + required property published -> bool; + + link author -> User; + } + + type Media { + required property url -> str; + multi link posts -> PostMedia; + link uploaded_by -> User; + } + + type PostMedia { + required link post -> Post; + required link media -> Media; + } + + type Comment { + required property created_at -> datetime { + default := datetime_current(); + constraint exclusive; + readonly := true; + }; + required property content -> str; + required link author -> User; + required link post -> Post; + } + + type Profile { + property bio -> str; + required link user -> User; + } + + type User { + required property email -> str { + constraint exclusive; + }; + property name -> str; + multi link posts -> Post; + multi link comments -> Comment; + link profile -> Profile; + multi link followers -> Follow; + multi link following -> Follow; + multi link media -> Media; + } + + type Follow { + required link from_user -> User; + required link to_user -> User; + } +} diff --git a/packages/plugin-edgedb/dbschema/migrations/00001.edgeql b/packages/plugin-edgedb/dbschema/migrations/00001.edgeql new file mode 100644 index 000000000..f4d0bdc60 --- /dev/null +++ b/packages/plugin-edgedb/dbschema/migrations/00001.edgeql @@ -0,0 +1,18 @@ +CREATE MIGRATION m1co7oxdqc52kxete3rozfhbkb67kofxnqspppu7ek6pe327a5mtyq + ONTO initial +{ + CREATE TYPE default::Post { + CREATE REQUIRED PROPERTY big_int_id -> std::bigint { + CREATE CONSTRAINT std::exclusive; + }; + CREATE PROPERTY content -> std::str; + CREATE REQUIRED PROPERTY created_at -> std::datetime { + SET default := (std::datetime_current()); + SET readonly := true; + CREATE CONSTRAINT std::exclusive; + }; + CREATE REQUIRED PROPERTY published -> std::bool; + CREATE REQUIRED PROPERTY title -> std::str; + CREATE REQUIRED PROPERTY updated_at -> std::datetime; + }; +}; diff --git a/packages/plugin-edgedb/dbschema/migrations/00002.edgeql b/packages/plugin-edgedb/dbschema/migrations/00002.edgeql new file mode 100644 index 000000000..0a0c90dd2 --- /dev/null +++ b/packages/plugin-edgedb/dbschema/migrations/00002.edgeql @@ -0,0 +1,56 @@ +CREATE MIGRATION m1goni6inys6wt4wokvqvemtqiiambmkfxudnf6rsculrh645bozla + ONTO m1co7oxdqc52kxete3rozfhbkb67kofxnqspppu7ek6pe327a5mtyq +{ + CREATE TYPE default::Comment { + CREATE REQUIRED LINK post -> default::Post; + CREATE REQUIRED PROPERTY content -> std::str; + CREATE REQUIRED PROPERTY created_at -> std::datetime { + SET default := (std::datetime_current()); + SET readonly := true; + CREATE CONSTRAINT std::exclusive; + }; + }; + CREATE TYPE default::User { + CREATE MULTI LINK comments -> default::Comment; + CREATE MULTI LINK posts -> default::Post; + CREATE REQUIRED PROPERTY email -> std::str { + CREATE CONSTRAINT std::exclusive; + }; + CREATE PROPERTY name -> std::str; + }; + ALTER TYPE default::Comment { + CREATE REQUIRED LINK author -> default::User; + }; + CREATE TYPE default::Follow { + CREATE REQUIRED LINK from_user -> default::User; + CREATE REQUIRED LINK to_user -> default::User; + }; + ALTER TYPE default::User { + CREATE MULTI LINK followers -> default::Follow; + CREATE MULTI LINK following -> default::Follow; + }; + CREATE TYPE default::Media { + CREATE LINK uploaded_by -> default::User; + CREATE REQUIRED PROPERTY url -> std::str; + }; + CREATE TYPE default::PostMedia { + CREATE REQUIRED LINK media -> default::Media; + CREATE REQUIRED LINK post -> default::Post; + }; + ALTER TYPE default::Media { + CREATE MULTI LINK posts -> default::PostMedia; + }; + ALTER TYPE default::User { + CREATE MULTI LINK media -> default::Media; + }; + ALTER TYPE default::Post { + CREATE LINK author -> default::User; + }; + CREATE TYPE default::Profile { + CREATE REQUIRED LINK user -> default::User; + CREATE PROPERTY bio -> std::str; + }; + ALTER TYPE default::User { + CREATE LINK profile -> default::Profile; + }; +}; diff --git a/packages/plugin-edgedb/package.json b/packages/plugin-edgedb/package.json index 80764e825..b6d04d06f 100644 --- a/packages/plugin-edgedb/package.json +++ b/packages/plugin-edgedb/package.json @@ -11,13 +11,15 @@ "require": "./lib/index.js" }, "scripts": { + "generate": "edgeql-js --output-dir tests/client/", "type": "tsc --project tsconfig.type.json", "build": "pnpm build:clean && pnpm build:cjs && pnpm build:esm && pnpm build:dts", "build:clean": "git clean -dfX esm lib", "build:cjs": "swc src -d lib --config-file ../../.swcrc -C module.type=commonjs", "build:esm": "swc src -d esm --config-file ../../.swcrc -C module.type=es6 && pnpm esm:extensions", "build:dts": "tsc", - "esm:extensions": "TS_NODE_PROJECT=../../tsconfig.json node -r @swc-node/register ../../.config/esm-transformer.ts" + "esm:extensions": "TS_NODE_PROJECT=../../tsconfig.json node -r @swc-node/register ../../.config/esm-transformer.ts", + "test": "jest --runInBand" }, "repository": { "type": "git", @@ -42,7 +44,12 @@ }, "devDependencies": { "@pothos/core": "workspace:*", + "@pothos/plugin-complexity": "workspace:*", + "@pothos/plugin-errors": "workspace:*", + "@pothos/plugin-relay": "workspace:*", + "@pothos/plugin-simple-objects": "workspace:*", "@pothos/test-utils": "workspace:*", + "edgedb": "^0.21.3", "graphql": "16.5.0", "graphql-tag": "^2.12.6" }, diff --git a/packages/plugin-edgedb/src/edgedb-field-builder.ts b/packages/plugin-edgedb/src/edgedb-field-builder.ts new file mode 100644 index 000000000..68430df0c --- /dev/null +++ b/packages/plugin-edgedb/src/edgedb-field-builder.ts @@ -0,0 +1,196 @@ +import { GraphQLResolveInfo } from 'graphql'; +import { + CompatibleTypes, + FieldKind, + FieldRef, + InputFieldMap, + NormalizeArgs, + RootFieldBuilder, + SchemaTypes, + ShapeFromTypeParam, + TypeParam, +} from '@pothos/core'; +import { FieldMap } from './util/relation-map'; +import { getLink, getRefFromModel } from './util/datamodel'; +import { extractTargetTypeName } from './util/target'; +import { isMultiLink } from './util/links'; +import { RelatedFieldOptions } from './types'; + +// Workaround for FieldKind not being extended on Builder classes +const RootBuilder: { + // eslint-disable-next-line @typescript-eslint/prefer-function-type + new ( + name: string, + builder: PothosSchemaTypes.SchemaBuilder, + kind: FieldKind, + graphqlKind: PothosSchemaTypes.PothosKindToGraphQLType[FieldKind], + ): PothosSchemaTypes.RootFieldBuilder; +} = RootFieldBuilder as never; + +export class EdgeDBObjectFieldBuilder< + Types extends SchemaTypes, + Model extends + | ({ [ModelKey in keyof Model]: Model[ModelKey] extends infer U ? U : never } & { + Fields: string | never; + Links: { + [Key in Model['Fields']]: { Shape: Model['Links'][Key] }; + }; + }) + | never, + Shape extends object = Model, +> extends RootBuilder { + model: string; + edgeDBFieldMap: FieldMap; + + exposeBoolean = this.createExpose('Boolean'); + exposeFloat = this.createExpose('Float'); + exposeInt = this.createExpose('Int'); + exposeID = this.createExpose('ID'); + exposeString = this.createExpose('String'); + exposeBooleanList = this.createExpose(['Boolean']); + exposeFloatList = this.createExpose(['Float']); + exposeIntList = this.createExpose(['Int']); + exposeIDList = this.createExpose(['ID']); + exposeStringList = this.createExpose(['String']); + + constructor( + name: string, + builder: PothosSchemaTypes.SchemaBuilder, + model: string, + fieldMap: FieldMap, + ) { + super(name, builder, 'EdgeDBObject', 'Object'); + + this.model = model; + this.edgeDBFieldMap = fieldMap; + } + + link< + Field extends Model['Fields'], + Nullable extends boolean, + Args extends InputFieldMap, + ResolveReturnShape, + >( + ...allArgs: NormalizeArgs< + [ + name: Field, + options?: RelatedFieldOptions< + Types, + Model, + Field, + Nullable, + Args, + ResolveReturnShape, + Shape + >, + ] + > + ): FieldRef { + const [name, { ...options } = {} as never] = allArgs; + const relationField = getLink(this.model, this.builder, name); + const ref = getRefFromModel(extractTargetTypeName(relationField.target), this.builder); + + const { resolve, extensions, ...rest } = options; + + const isList = isMultiLink(relationField); + + console.log('[debug] link() -> name: ', name); + console.log('[debug] link() -> relationField: ', relationField, relationField.target); + console.log('[debug] link() -> ref: ', ref); + console.log('[debug] link() -> isList: ', isList); + + return this.field({ + ...(rest as {}), + type: isList ? [ref] : ref, + // TODO: Descriptions + // description: getFieldDescription(this.model, this.builder, name, description), + extensions: { + ...extensions, + pothosEdgeDBFallback: + resolve && + ((q: {}, parent: Shape, args: {}, context: {}, info: GraphQLResolveInfo) => + resolve({ ...q } as never, parent, args as never, context, info)), + }, + resolve: (parent) => (parent as Record)[name], + }) as FieldRef; + } + + expose< + Type extends TypeParam, + Nullable extends boolean, + ResolveReturnShape, + Name extends CompatibleTypes, + >( + ...args: NormalizeArgs< + [ + name: Name, + options?: Omit< + PothosSchemaTypes.ObjectFieldOptions< + Types, + Shape, // TODO Rename to model? + Type, + Nullable, + {}, + ResolveReturnShape + >, + 'resolve' | 'select' + >, + ] + > + ) { + const [name, options = {} as never] = args; + + const typeConfig = this.builder.configStore.getTypeConfig(this.typename, 'Object'); + const usingSelect = !!typeConfig.extensions?.pothosPrismaSelect; + + return this.exposeField(name as never, { + ...options, + extensions: { + ...options.extensions, + pothosPrismaVariant: name, + pothosPrismaSelect: usingSelect && { + [name]: true, + }, + }, + }); + } + + private createExpose>(type: Type) { + return < + Nullable extends boolean, + ResolveReturnShape, + Name extends CompatibleTypes, + >( + ...args: NormalizeArgs< + [ + name: Name, + options?: Omit< + PothosSchemaTypes.ObjectFieldOptions< + Types, + Shape, + Type, + Nullable, + {}, + ResolveReturnShape + >, + 'resolve' | 'type' | 'select' | 'description' + > & { description?: string | false }, + ] + > + ): FieldRef, 'EdgeDBObject'> => { + const [name, { description, ...options } = {} as never] = args; + + return this.expose(name as never, { + ...options, + // TODO: Descriptions + // description: getFieldDescription( + // this.model, + // this.builder, + // name as string, + // description, + // ) as never, + type, + }); + }; + } +} diff --git a/packages/plugin-edgedb/src/field-builder.ts b/packages/plugin-edgedb/src/field-builder.ts new file mode 100644 index 000000000..3e5b09097 --- /dev/null +++ b/packages/plugin-edgedb/src/field-builder.ts @@ -0,0 +1,11 @@ +import { FieldKind, RootFieldBuilder, SchemaTypes } from '@pothos/core'; + +export * from './edgedb-field-builder'; + +const fieldBuilderProto = RootFieldBuilder.prototype as PothosSchemaTypes.RootFieldBuilder< + SchemaTypes, + unknown, + FieldKind +>; + +// fieldBuilderProto.edgeDBField = function edgeDBField(...) diff --git a/packages/plugin-edgedb/src/global-types.ts b/packages/plugin-edgedb/src/global-types.ts index 55fff687c..43a807da6 100644 --- a/packages/plugin-edgedb/src/global-types.ts +++ b/packages/plugin-edgedb/src/global-types.ts @@ -1,18 +1,53 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import { FieldNullability, InputFieldMap, SchemaTypes, TypeParam } from '@pothos/core'; -import { EdgeDBPluginOptions } from './types'; - -import type { PothosEdgeDBPlugin } from '.'; +/* eslint-disable @typescript-eslint/no-unud-vars */ +import { + FieldNullability, + InputFieldMap, + InterfaceParam, + SchemaTypes, + TypeParam, +} from '@pothos/core'; +import { + EdgeDBDriver, + EdgeDBModelShape, + EdgeDBObjectFieldOptions, + EdgeDBObjectTypeOptions, + EdgeDBQueryBuilder, + EdgeDBTypeKeys, +} from './types'; +import type { EdgeDBPlugin } from '.'; +import { EdgeDBObjectFieldBuilder as InternalEdgeDBObjectFieldBuilder } from './edgedb-field-builder'; +import { EdgeDBObjectRef } from './object-ref'; declare global { export namespace PothosSchemaTypes { export interface Plugins { - edgedb: PothosEdgeDBPlugin; + edgedb: EdgeDBPlugin; + } + + export interface UserSchemaTypes { + EdgeDBTypes: { default: { [key: string]: unknown } }; + } + + export interface ExtendDefaultTypes> { + EdgeDBTypes: PartialTypes['EdgeDBTypes'] & {}; + } + + export interface PothosKindToGraphQLType { + EdgeDBObject: 'Object'; } export interface SchemaBuilderOptions { - optionInRootOfConfig?: boolean; - nestedOptionsObject?: EdgeDBPluginOptions; + edgeDB: { + qb: EdgeDBQueryBuilder; + client: EdgeDBDriver; + extensions?: {}; + exposeDescriptions?: + | boolean + | { + types?: boolean; + fields?: boolean; + }; + }; } export interface BuildSchemaOptions { @@ -23,14 +58,51 @@ declare global { optionOnObject?: boolean; } - export interface MutationFieldOptions< + export interface FieldOptionsByKind< Types extends SchemaTypes, + ParentShape, Type extends TypeParam, Nullable extends FieldNullability, Args extends InputFieldMap, + ResolveShape, ResolveReturnShape, > { - customMutationFieldOption?: boolean; + EdgeDBObject: EdgeDBObjectFieldOptions< + Types, + ParentShape, + Type, + Nullable, + Args, + ResolveShape, + ResolveReturnShape + >; + } + + export interface SchemaBuilder { + edgeDBObject: < + Name extends EdgeDBTypeKeys, + Interfaces extends InterfaceParam[], + Model extends EdgeDBModelShape, + >( + name: Name, + options: EdgeDBObjectTypeOptions, + ) => EdgeDBObjectRef; } + + export interface EdgeDBObjectFieldBuilder< + Types extends SchemaTypes, + Model extends + | ({ [ModelKey in keyof Model]: Model[ModelKey] extends infer U ? U : never } & { + Fields: string | never; + Links: { + [Key in Model['Fields']]: { + Shape: Model['Links'][Key]; + }; + }; + }) + | never, + Shape extends object = Model, + > extends InternalEdgeDBObjectFieldBuilder, + RootFieldBuilder {} } } diff --git a/packages/plugin-edgedb/src/index.ts b/packages/plugin-edgedb/src/index.ts index 2ef90c386..aa46a6341 100644 --- a/packages/plugin-edgedb/src/index.ts +++ b/packages/plugin-edgedb/src/index.ts @@ -1,16 +1,16 @@ /* eslint-disable no-console */ import './global-types'; -import { GraphQLFieldResolver, GraphQLSchema, GraphQLTypeResolver } from 'graphql'; +import './schema-builder'; +import { GraphQLFieldResolver, GraphQLTypeResolver } from 'graphql'; import SchemaBuilder, { BasePlugin, - PothosEnumValueConfig, - PothosInputFieldConfig, + BuildCache, PothosInterfaceTypeConfig, PothosOutputFieldConfig, - PothosTypeConfig, PothosUnionTypeConfig, SchemaTypes, } from '@pothos/core'; +import { EdgeDBObjectFieldBuilder as InternalEdgeDBObjectFieldBuilder } from './field-builder'; export * from './types'; @@ -18,44 +18,50 @@ const pluginName = 'edgedb' as const; export default pluginName; -export class PothosEdgeDBPlugin extends BasePlugin { - override onTypeConfig(typeConfig: PothosTypeConfig) { - console.log(this.builder.options.nestedOptionsObject?.exampleOption); - console.log(this.options.customBuildTimeOptions); - - if (typeConfig.kind === 'Object') { - console.log(typeConfig.pothosOptions.optionOnObject); - } - - return typeConfig; +export type EdgeDBObjectFieldBuilder< + Types extends SchemaTypes, + ParentShape, +> = PothosSchemaTypes.ObjectFieldBuilder; + +export const ObjectFieldBuilder = InternalEdgeDBObjectFieldBuilder as new < + Types extends SchemaTypes, + Model extends + | ({ [ModelKey in keyof Model]: Model[ModelKey] extends infer U ? U : never } & { + Fields: string | never; + Links: { + [Key in Model['Fields']]: { + Shape: Model['Links'][Key]; + }; + }; + }) + | never, + Shape extends object = Model, +>( + name: string, + builder: PothosSchemaTypes.SchemaBuilder, +) => PothosSchemaTypes.EdgeDBObjectFieldBuilder; + +export class EdgeDBPlugin extends BasePlugin { + constructor(cache: BuildCache) { + super(cache, pluginName); } override onOutputFieldConfig(fieldConfig: PothosOutputFieldConfig) { - if (fieldConfig.kind === 'Mutation') { - console.log(fieldConfig.pothosOptions.customMutationFieldOption); + if (fieldConfig.kind === 'EdgeDBObject') { + console.log('[plugin-edgedb] Received object of type `EdgeDBObject` '); } return fieldConfig; } - override onInputFieldConfig(fieldConfig: PothosInputFieldConfig) { - return fieldConfig; - } - - override onEnumValueConfig(valueConfig: PothosEnumValueConfig) { - return valueConfig; - } - - override beforeBuild() {} - - override afterBuild(schema: GraphQLSchema): GraphQLSchema { - return schema; - } - override wrapResolve( - resolver: GraphQLFieldResolver, + resolver: GraphQLFieldResolver, fieldConfig: PothosOutputFieldConfig, ): GraphQLFieldResolver { + if (fieldConfig.kind !== 'EdgeDBObject') { + return resolver; + } + return (parent, args, context, info) => { console.log(`Resolving ${info.parentType}.${info.fieldName}`); @@ -63,13 +69,6 @@ export class PothosEdgeDBPlugin extends BasePlugin | undefined, - fieldConfig: PothosOutputFieldConfig, - ) { - return subscribe; - } - override wrapResolveType( resolveType: GraphQLTypeResolver, typeConfig: PothosInterfaceTypeConfig | PothosUnionTypeConfig, @@ -78,4 +77,4 @@ export class PothosEdgeDBPlugin extends BasePlugin, + T = {}, +> extends ObjectRef { + [edgeDBModelKey]!: Model; +} diff --git a/packages/plugin-edgedb/src/schema-builder.ts b/packages/plugin-edgedb/src/schema-builder.ts new file mode 100644 index 000000000..480ce2124 --- /dev/null +++ b/packages/plugin-edgedb/src/schema-builder.ts @@ -0,0 +1,41 @@ +import './global-types'; +import SchemaBuilder, { SchemaTypes } from '@pothos/core'; +import { EdgeDBObjectFieldBuilder } from './edgedb-field-builder'; +import { getRefFromModel } from './util/datamodel'; +import { getModelDescription } from './util/description'; +import { getRelationMap } from './util/relation-map'; +import { getObjectsTypes } from './util/get-client'; + +const schemaBuilderProto = SchemaBuilder.prototype as PothosSchemaTypes.SchemaBuilder; + +schemaBuilderProto.edgeDBObject = function edgeDBObject(type, { fields, description, ...options }) { + const ref = getRefFromModel(type as string, this); + const name: string = type as string; + const fieldMap = getRelationMap(getObjectsTypes(this)).get(type as string)!; + + ref.name = name; + + this.objectType(ref, { + ...(options as {}), + description: getModelDescription(type, this, description), + extensions: { + ...options.extensions, + pothosEdgeDBModel: type, + pothosEdgeDBFieldMap: fieldMap, + }, + name, + fields: fields + ? () => + fields( + new EdgeDBObjectFieldBuilder( + name, + this, + type, + getRelationMap(getObjectsTypes(this)).get(type)!, + ), + ) + : undefined, + }); + + return ref as never; +}; diff --git a/packages/plugin-edgedb/src/types.ts b/packages/plugin-edgedb/src/types.ts index 67a1d897d..25f44d6eb 100644 --- a/packages/plugin-edgedb/src/types.ts +++ b/packages/plugin-edgedb/src/types.ts @@ -1,3 +1,389 @@ -export interface EdgeDBPluginOptions { - exampleOption?: boolean; +import type { Client } from 'edgedb'; +import { + FieldMap, + FieldNullability, + InputFieldMap, + InputShapeFromFields, + InterfaceParam, + MaybePromise, + Normalize, + ObjectRef, + SchemaTypes, + ShapeWithNullability, + TypeParam, +} from '@pothos/core'; +import type { EdgeDBObjectFieldBuilder } from './edgedb-field-builder'; +import { GraphQLResolveInfo } from 'graphql'; + +export interface EdgeDBQueryBuilder { + default: unknown; } +export interface EdgeDBDriver extends Client {} + +export type EdgeDBModels< + Name extends EdgeDBTypeKeys, + Types extends SchemaTypes = SchemaTypes, +> = Types['EdgeDBTypes']['default'][Name] extends infer T ? T : never; + +export type EdgeDBTypeKeys = + keyof Types['EdgeDBTypes']['default'] extends infer Key + ? Key extends string + ? Key + : never + : never; + +export type EdgeDBDefaultExportKeyTypes = { + [K in keyof DefaultExports]-?: K extends string + ? string + : K extends number + ? number + : K extends symbol + ? symbol + : never; +}[keyof DefaultExports]; + +type EdgeDBDefaultExportKey< + DefaultExports, + KeyType extends string | number | symbol = EdgeDBDefaultExportKeyTypes, +> = Extract; + +export type EdgeDBModelTypes = + Types['EdgeDBTypes']['default'] extends infer ObjectTypeMap + ? ObjectTypeMap extends object + ? ObjectTypeMap + : never + : never; + +export type EdgeDBModelShape< + Types extends SchemaTypes, + Name extends EdgeDBTypeKeys, +> = EdgeDBModelTypes[Name] extends infer Property + ? Property extends BaseObject & EdgeDBModelTypes[Name] + ? Property & { + Fields: extractLinks extends infer Field + ? Field extends string + ? SplitLT + : null + : never; + Links: { + [Key in Property['Fields']]: { + Shape: Property['Fields'][Key] extends infer Shape + ? Shape extends BaseObject + ? Shape + : Shape extends Array + ? Shape[0] + : never + : never; + }; + }; + } + : never + : never; + +export type SelectedKeys = { [K in keyof T]: T[K] extends false ? never : K }[keyof T]; + +// -- +// EdgeDB Types +// -- +export enum Cardinality { + AtMostOne = 'AtMostOne', + One = 'One', + Many = 'Many', + AtLeastOne = 'AtLeastOne', + Empty = 'Empty', +} + +export enum TypeKind { + scalar = 'scalar', + // castonlyscalar = "castonlyscalar", + enum = 'enum', + object = 'object', + namedtuple = 'namedtuple', + tuple = 'tuple', + array = 'array', + range = 'range', +} +export type tupleOf = [T, ...T[]] | []; +export type cardinalityAssignable = C extends Cardinality.Empty + ? Cardinality.Empty + : C extends Cardinality.One + ? Cardinality.One + : C extends Cardinality.AtMostOne + ? Cardinality.One | Cardinality.AtMostOne | Cardinality.Empty + : C extends Cardinality.AtLeastOne + ? Cardinality.One | Cardinality.AtLeastOne | Cardinality.Many + : C extends Cardinality.Many + ? Cardinality + : never; + +export interface BaseObject { + id: any; + __type__: any; +} +export interface BaseType { + __kind__: TypeKind; + __name__: string; +} +export type BaseTypeSet = { + __element__: BaseType; + __cardinality__: Cardinality; +}; +export type BaseTypeTuple = tupleOf; + +export interface ScalarType< + Name extends string = string, + TsType extends any = any, + TsConstType extends TsType = TsType, +> extends BaseType { + __kind__: TypeKind.scalar; + __tstype__: TsType; + __tsconsttype__: TsConstType; + __name__: Name; +} + +export interface TypeSet { + __element__: T; + __cardinality__: Card; +} + +export type PropertyShape = { + [k: string]: PropertyDesc; +}; + +export interface PropertyDesc< + Type extends BaseType = BaseType, + Card extends Cardinality = Cardinality, + Exclusive extends boolean = boolean, + Computed extends boolean = boolean, + Readonly extends boolean = boolean, + HasDefault extends boolean = boolean, +> { + __kind__: 'property'; + target: Type; + cardinality: Card; + exclusive: Exclusive; + computed: Computed; + readonly: Readonly; + hasDefault: HasDefault; +} + +export interface LinkDesc< + Type extends ObjectType = any, + Card extends Cardinality = Cardinality, + LinkProps extends PropertyShape = any, + Exclusive extends boolean = boolean, + Computed extends boolean = boolean, + Readonly extends boolean = boolean, + HasDefault extends boolean = boolean, +> { + __kind__: 'link'; + target: Type; + cardinality: Card; + properties: LinkProps; + exclusive: Exclusive; + computed: Computed; + readonly: Readonly; + hasDefault: HasDefault; +} + +export type ObjectTypeSet = TypeSet; +export type ObjectTypeExpression = TypeSet; + +export interface ObjectType< + Name extends string = string, + Pointers extends ObjectTypePointers = ObjectTypePointers, + Shape extends object | null = any, + // Polys extends Poly[] = any[] +> extends BaseType { + __kind__: TypeKind.object; + __name__: Name; + __pointers__: Pointers; + __shape__: Shape; +} + +export type ObjectTypePointers = { + [k: string]: PropertyDesc | LinkDesc; +}; + +export interface PathParent { + type: Parent; + linkName: string; +} + +export namespace EdgeDB { + export interface Datamodel { + [key: string]: TypeSet; + } +} + +type pointersToObjectType

= ObjectType; + +export type SelectModifiers = { + filter?: TypeSet, Cardinality>; + order_by?: any; + offset?: any | number; + limit?: any | number; +}; + +// --- + +// object types -> pointers +// pointers -> links +// links -> target object type +// links -> link properties +export type extractObjectShapeToSelectShape = Partial<{ + [k in keyof TObject['__pointers__']]: TObject['__pointers__'][k] extends PropertyDesc + ? + | boolean + | TypeSet< + TObject['__pointers__'][k]['target'], + cardinalityAssignable + > + : TObject['__pointers__'][k] extends LinkDesc + ? {} // as link, currently no type + : any; +}> & { [key: string]: unknown }; + +type extractLinksToPartial = Shape extends infer T + ? T extends object + ? { + [Key in keyof Shape]: Shape[Key] extends infer Link + ? Link extends BaseObject | Array + ? boolean + : never + : never; + } + : never + : never; + +// Extract links from queryBuilder object, not from type +// Shape extends infer T +// ? T extends TypeSet +// ? { +// [K in keyof T['__element__']['__pointers__']]: T['__element__']['__pointers__'][K] extends LinkDesc +// ? boolean +// : never; +// } +// : never +// : never; + +// Filter out links from model type +export type extractLinks< + Model extends object, + PartialLinks extends extractLinksToPartial = extractLinksToPartial, +> = PartialLinks extends infer Links + ? { + [K in keyof Links]: [boolean] extends [Links[K]] ? K : never; + }[keyof Links] + : null; + +type Split = S extends `${infer T}${D}${infer U}` + ? never + : [S][0]; + +// For removing backlinks from `link` fields +// eg. fields: "posts" | "comments" | " fields: "posts" | "comments" +export type SplitLT = Split; + +export type EdgeDBObjectFieldOptions< + Types extends SchemaTypes, + ParentShape, + Type extends TypeParam, + Nullable extends FieldNullability, + Args extends InputFieldMap, + Select, + ResolveReturnShape, +> = PothosSchemaTypes.ObjectFieldOptions< + Types, + ParentShape, + Type, + Nullable, + Args, + ResolveReturnShape +>; + +type EdgeDBObjectFieldsShape< + Types extends SchemaTypes, + Model extends + | ({ + [key: string]: Model[keyof Model] extends infer U ? U : never; + } & { + Fields: string | never; + Links: { + [Key in Model['Fields']]: { Shape: Model['Links'][Key] }; + }; + }) + | never, + Shape extends object = Extract, +> = (t: EdgeDBObjectFieldBuilder) => FieldMap; + +export type EdgeDBObjectTypeOptions< + Types extends SchemaTypes, + Interfaces extends InterfaceParam[], + Model extends + | ({ + [key: string]: Model[keyof Model] extends infer U ? U : never; + } & { + Fields: string | never; + Links: { + [Key in Model['Fields']]: { Shape: Model['Links'][Key] }; + }; + }) + | never, +> = Omit< + | PothosSchemaTypes.ObjectTypeOptions + | PothosSchemaTypes.ObjectTypeWithInterfaceOptions, + 'fields' | 'description' +> & { + description?: string | false; + fields?: EdgeDBObjectFieldsShape; +}; + +type RefForLink< + Model extends + | ({ [ModelKey in keyof Model]: Model[ModelKey] extends infer U ? U : never } & { + Fields: string | never; + Links: { + [Key in Model['Fields']]: { Shape: Model['Links'][Key] }; + }; + }) + | never, + Field extends keyof Model['Links'], +> = Model['Links'][Field] extends unknown[] + ? [ObjectRef] + : ObjectRef; + +export type RelatedFieldOptions< + Types extends SchemaTypes, + Model extends + | ({ [ModelKey in keyof Model]: Model[ModelKey] extends infer U ? U : never } & { + Fields: string | never; + Links: { + [Key in Model['Fields']]: { Shape: Model['Links'][Key] }; + }; + }) + | never, + Field extends keyof Model['Links'], + Nullable extends boolean, + Args extends InputFieldMap, + ResolveReturnShape, + Shape, +> = Omit< + PothosSchemaTypes.ObjectFieldOptions< + Types, + Shape, + RefForLink, + Nullable, + Args, + ResolveReturnShape + >, + 'resolve' | 'type' | 'description' +> & { + resolve?: ( + query: never, + parent: Shape, + args: InputShapeFromFields, + context: Types['Context'], + info: GraphQLResolveInfo, + ) => MaybePromise>; +}; diff --git a/packages/plugin-edgedb/src/util/datamodel.ts b/packages/plugin-edgedb/src/util/datamodel.ts new file mode 100644 index 000000000..634caa450 --- /dev/null +++ b/packages/plugin-edgedb/src/util/datamodel.ts @@ -0,0 +1,68 @@ +import { SchemaTypes } from '@pothos/core'; +import { EdgeDBObjectRef } from '../object-ref'; +import { EdgeDBModelTypes } from '../types'; +import { getObjectsTypes } from './get-client'; + +export const refMap = new WeakMap< + object, + Map>> +>(); + +export function getRefFromModel( + name: string, + builder: PothosSchemaTypes.SchemaBuilder, +): EdgeDBObjectRef> { + if (!refMap.has(builder)) { + refMap.set(builder, new Map()); + } + const cache = refMap.get(builder)!; + + if (!cache.has(name)) { + cache.set(name, new EdgeDBObjectRef(name)); + } + + return cache.get(name)!; +} + +export function getLink( + name: string, + builder: PothosSchemaTypes.SchemaBuilder, + link: string, +) { + const fieldData = getFieldData(name, builder, link); + + if (fieldData.__kind__ !== 'link') { + throw new Error(`Field ${link} of model '${name}' is not a link (${fieldData.__kind__})`); + } + + return fieldData; +} + +export function getFieldData( + name: string, + builder: PothosSchemaTypes.SchemaBuilder, + fieldName: string, +) { + const modelData = getModel(name, builder); + const fieldData = modelData.__element__.__pointers__[fieldName]; + + if (!fieldData) { + throw new Error(`Field '${fieldName}' not found in model '${name}'`); + } + + return fieldData; +} + +export function getModel( + name: string, + builder: PothosSchemaTypes.SchemaBuilder, +) { + const typeFields = getObjectsTypes(builder); + + const typeData = typeFields[name]; + if (!typeData) { + throw new Error(`Type '${name}' not found in schema's default exports.`); + } + + return typeData; +} diff --git a/packages/plugin-edgedb/src/util/description.ts b/packages/plugin-edgedb/src/util/description.ts new file mode 100644 index 000000000..e6b288670 --- /dev/null +++ b/packages/plugin-edgedb/src/util/description.ts @@ -0,0 +1,17 @@ +import { SchemaTypes } from '@pothos/core'; +import { getModel } from './datamodel'; + +export function getModelDescription( + model: string, + builder: PothosSchemaTypes.SchemaBuilder, + description?: string | false, +) { + const { exposeDescriptions } = builder.options.edgeDB; + const useEdgeDBDescription = exposeDescriptions === true; + // || (typeof exposeDescriptions === 'object' && exposeDescriptions?.models === true); + + return ( + // (useEdgeDBDescription ? description ?? getModel(model, builder).documentation : description) || + undefined + ); +} diff --git a/packages/plugin-edgedb/src/util/get-client.ts b/packages/plugin-edgedb/src/util/get-client.ts new file mode 100644 index 000000000..502d5ecc9 --- /dev/null +++ b/packages/plugin-edgedb/src/util/get-client.ts @@ -0,0 +1,23 @@ +import { SchemaTypes } from '@pothos/core'; +import { EdgeDB, EdgeDBDriver } from '../types'; + +export function getDriver( + builder: PothosSchemaTypes.SchemaBuilder, + context: Types['Context'], +): EdgeDBDriver { + if (typeof builder.options.edgeDB.client === 'function') { + console.warn('Not implemented yet'); + } + + return builder.options.edgeDB.client; +} + +export function getObjectsTypes( + builder: PothosSchemaTypes.SchemaBuilder, +): EdgeDB.Datamodel { + if (!builder.options.edgeDB.qb.default) { + console.warn(`Missing EdgeDB Query Builder.`); + } + + return builder.options.edgeDB.qb.default as EdgeDB.Datamodel; +} diff --git a/packages/plugin-edgedb/src/util/links.ts b/packages/plugin-edgedb/src/util/links.ts new file mode 100644 index 000000000..1cf0adcf1 --- /dev/null +++ b/packages/plugin-edgedb/src/util/links.ts @@ -0,0 +1,5 @@ +import { Cardinality, LinkDesc } from '../types'; + +export function isMultiLink(field: LinkDesc): boolean { + return field.cardinality === Cardinality.Many || field.cardinality === Cardinality.AtLeastOne; +} diff --git a/packages/plugin-edgedb/src/util/relation-map.ts b/packages/plugin-edgedb/src/util/relation-map.ts new file mode 100644 index 000000000..0d937e86f --- /dev/null +++ b/packages/plugin-edgedb/src/util/relation-map.ts @@ -0,0 +1,41 @@ +import { createContextCache } from '@pothos/core'; +import { EdgeDB } from '../types'; + +export interface FieldMap { + type: string; + links: Map; +} +export type RelationMap = Map; + +export const getRelationMap = createContextCache((edgeDBDefaultExports: unknown) => + createRelationMap(edgeDBDefaultExports as EdgeDB.Datamodel), +); + +export function createRelationMap(edgeDBDefault: EdgeDB.Datamodel) { + const relationMap: RelationMap = new Map(); + + Object.entries(edgeDBDefault).forEach(([type]) => { + relationMap.set(type, { + type, + links: new Map([]), + }); + }); + + Object.entries(edgeDBDefault).forEach(([type, { __element__ }]) => { + const map = relationMap.get(type)!.links; + + Object.entries(__element__.__pointers__) + .filter( + ([key, entry]) => + !String(key).startsWith('<') && + String(key) !== '__type__' && + String(entry.__kind__) === 'link', + ) + .forEach(([linkName, entry]) => { + const typeName = entry.target.__name__.replace('default::', ''); + map.set(linkName, relationMap.get(typeName)!); + }); + }); + + return relationMap; +} diff --git a/packages/plugin-edgedb/src/util/target.ts b/packages/plugin-edgedb/src/util/target.ts new file mode 100644 index 000000000..1b9378590 --- /dev/null +++ b/packages/plugin-edgedb/src/util/target.ts @@ -0,0 +1,17 @@ +import { ObjectType } from '../types'; + +// To get the type name of an object type, extract the `target`s `__name__` prop. +// eg. { target: { __name__: "default::User" } } -> "default::User" -> "User" +export function extractTargetTypeName(target: ObjectType): string { + const targetName = target.__name__; + if (!targetName || typeof targetName !== 'string') { + throw new Error(`Target type has no name: ${target}.`); + } + + let prefix: string = ''; + if (targetName.startsWith('default::')) { + prefix = 'default::'; + } + + return targetName.replace(prefix, ''); +} diff --git a/packages/plugin-edgedb/tests/.gitignore b/packages/plugin-edgedb/tests/.gitignore new file mode 100644 index 000000000..859ae3bfb --- /dev/null +++ b/packages/plugin-edgedb/tests/.gitignore @@ -0,0 +1 @@ +client/* \ No newline at end of file diff --git a/packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap b/packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap new file mode 100644 index 000000000..53522e55d --- /dev/null +++ b/packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap @@ -0,0 +1,34 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`edgedb generates schema 1`] = ` +"scalar DateTime + +type ExplicitEdgeDBUser { + email: String! + id: ID! + name: String +} + +type Post { + author: User! + content: String + id: ID! + published: Boolean! + title: String! +} + +type PostPreview { + id: ID! + preview: String +} + +type Query { + me: ExplicitEdgeDBUser +} + +type User { + email: String! + name: String + posts: [Post!]! +}" +`; diff --git a/packages/plugin-edgedb/tests/example/builder.ts b/packages/plugin-edgedb/tests/example/builder.ts new file mode 100644 index 000000000..0cdd6e157 --- /dev/null +++ b/packages/plugin-edgedb/tests/example/builder.ts @@ -0,0 +1,35 @@ +import SchemaBuilder from '@pothos/core'; +import SimpleObjects from '@pothos/plugin-simple-objects'; +import EdgeDBPlugin from '../../src'; +import { db as edgeDBDriver } from './db'; +import edgeDBQB, { types as EdgeDBTypes } from '../../dbschema/edgeql-js'; + +const builder = new SchemaBuilder<{ + Context: { + user: { id: string }; + }; + Scalars: { + ID: { Input: string; Output: string | number }; + DateTime: { Input: Date; Output: Date }; + }; + EdgeDBTypes: EdgeDBTypes; +}>({ + plugins: [EdgeDBPlugin, SimpleObjects], + edgeDB: { + qb: edgeDBQB, + client: edgeDBDriver, + }, +}); + +builder.scalarType('DateTime', { + serialize: (date) => date.toISOString(), + parseValue: (date) => { + if (typeof date !== 'string') { + throw new Error('Unknown date value.'); + } + + return new Date(date); + }, +}); + +export default builder; diff --git a/packages/plugin-edgedb/tests/example/db.ts b/packages/plugin-edgedb/tests/example/db.ts new file mode 100644 index 000000000..f8aaa078b --- /dev/null +++ b/packages/plugin-edgedb/tests/example/db.ts @@ -0,0 +1,14 @@ +import createClient, { Client } from 'edgedb'; + +let db: Client; + +declare global { + var __db__: Client | undefined; +} + +if (process.env.NODE_ENV !== 'production') { + if (!global.__db__) global.__db__ = createClient({ logging: true }); + db = global.__db__; +} + +export { db }; diff --git a/packages/plugin-edgedb/tests/example/schema/index.ts b/packages/plugin-edgedb/tests/example/schema/index.ts new file mode 100644 index 000000000..ad8f82053 --- /dev/null +++ b/packages/plugin-edgedb/tests/example/schema/index.ts @@ -0,0 +1,68 @@ +import e, { Post, User } from '../../client'; +import { db } from '../db'; +import builder from '../builder'; + +const PostPreview = builder.objectRef('PostPreview'); +PostPreview.implement({ + fields: (t) => ({ + id: t.exposeID('id'), + preview: t.string({ + nullable: true, + resolve: (post) => post.content?.slice(10), + }), + }), +}); + +const UserRef = builder.objectRef('ExplicitEdgeDBUser'); +UserRef.implement({ + fields: (t) => ({ + id: t.exposeID('id'), + email: t.exposeString('email'), + name: t.exposeString('name', { nullable: true }), + }), +}); + +builder.edgeDBObject('Post', { + fields: (t) => ({ + id: t.exposeID('id'), + title: t.exposeString('title'), + content: t.exposeString('content', { nullable: true }), + published: t.exposeBoolean('published'), + // createdAt: t.expose('created_at'), + author: t.link('author'), + }), +}); + +builder.edgeDBObject('User', { + fields: (t) => ({ + email: t.exposeString('email'), + name: t.exposeString('name', { nullable: true }), + posts: t.link('posts'), + }), +}); + +builder.queryType({ + fields: (t) => ({ + me: t.field({ + type: UserRef, + nullable: true, + //@ts-ignore + resolve: async (root, args, ctx, info) => { + const user = await e + .select(e.User, (user) => ({ + email: user.email, + filter: e.op(user.id, '=', e.uuid(ctx.user.id)), + })) + .run(db); + + console.log(await e.count(e.User).run(db)); + + console.log('[query] user: ', user); + + return user; + }, + }), + }), +}); + +export default builder.toSchema({}); diff --git a/packages/plugin-edgedb/tests/example/server.ts b/packages/plugin-edgedb/tests/example/server.ts new file mode 100644 index 000000000..15ef95266 --- /dev/null +++ b/packages/plugin-edgedb/tests/example/server.ts @@ -0,0 +1,12 @@ +import { createTestServer } from '@pothos/test-utils'; +import { db } from './db'; +import schema from './schema'; + +const server = createTestServer({ + schema, + contextFactory: () => ({ user: { id: 'a04bf8b8-1bfd-11ed-93f8-836b78753212' } }), +}); + +server.listen(3000, () => { + console.log('🚀 Server started at http://127.0.0.1:3000'); +}); diff --git a/packages/plugin-edgedb/tests/index.test.ts b/packages/plugin-edgedb/tests/index.test.ts new file mode 100644 index 000000000..3c8fa50c1 --- /dev/null +++ b/packages/plugin-edgedb/tests/index.test.ts @@ -0,0 +1,45 @@ +import schema from './example/schema'; +import { db as edgedb } from './example/db'; +import { execute, printSchema } from 'graphql'; +import { gql } from 'graphql-tag'; + +let queries = []; + +describe('edgedb', () => { + afterEach(() => { + queries = []; + }); + + it('generates schema', () => { + expect(printSchema(schema)).toMatchSnapshot(); + }); + + it('queries for single item', async () => { + const query = gql` + query { + me { + id + } + } + `; + + const result = await execute({ + schema, + document: query, + contextValue: { user: { id: 'a04bf8b8-1bfd-11ed-93f8-836b78753212' } }, + }); + + console.log('result::', result); + + expect(result).toMatchInlineSnapshot(` + Object { + "data": Object { + "me": null, + }, + "errors": Array [ + [GraphQLError: Cannot return null for non-nullable field ExplicitEdgeDBUser.id.], + ], + } + `); + }); +}); diff --git a/packages/plugin-edgedb/tsconfig.json b/packages/plugin-edgedb/tsconfig.json index b5a6d2490..49e0952e0 100644 --- a/packages/plugin-edgedb/tsconfig.json +++ b/packages/plugin-edgedb/tsconfig.json @@ -8,7 +8,8 @@ "rootDir": "src" }, "include": [ - "src/**/*" + "src/**/*", + "dbschema/**/*" ], - "extends": "../../tsconfig.options.json", -} \ No newline at end of file + "extends": "../../tsconfig.options.json" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 028bd8442..1dba6d36e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -223,6 +223,9 @@ importers: graphql: 16.5.0 prisma: 4.1.1 + examples/prisma-federation/prisma/client: + specifiers: {} + examples/prisma-relay: specifiers: '@faker-js/faker': ^7.4.0 @@ -243,6 +246,12 @@ importers: graphql: 16.5.0 prisma: 4.1.1 + examples/prisma-relay/prisma/client: + specifiers: {} + + examples/prisma/prisma/client: + specifiers: {} + examples/relay-windowed-pagination: specifiers: '@faker-js/faker': ^7.4.0 @@ -400,6 +409,31 @@ importers: packages/plugin-directives/esm: specifiers: {} + packages/plugin-edgedb: + specifiers: + '@pothos/core': workspace:* + '@pothos/plugin-complexity': workspace:* + '@pothos/plugin-errors': workspace:* + '@pothos/plugin-relay': workspace:* + '@pothos/plugin-simple-objects': workspace:* + '@pothos/test-utils': workspace:* + edgedb: ^0.21.3 + graphql: 16.5.0 + graphql-tag: ^2.12.6 + devDependencies: + '@pothos/core': link:../core + '@pothos/plugin-complexity': link:../plugin-complexity + '@pothos/plugin-errors': link:../plugin-errors + '@pothos/plugin-relay': link:../plugin-relay + '@pothos/plugin-simple-objects': link:../plugin-simple-objects + '@pothos/test-utils': link:../test-utils + edgedb: 0.21.3 + graphql: 16.5.0 + graphql-tag: 2.12.6_graphql@16.5.0 + + packages/plugin-edgedb/esm: + specifiers: {} + packages/plugin-errors: specifiers: '@pothos/core': workspace:* @@ -508,6 +542,9 @@ importers: packages/plugin-prisma/esm: specifiers: {} + packages/plugin-prisma/tests/client: + specifiers: {} + packages/plugin-relay: specifiers: '@pothos/core': workspace:* @@ -548,6 +585,9 @@ importers: packages/plugin-scope-auth/esm: specifiers: {} + packages/plugin-scope-auth/prisma/client: + specifiers: {} + packages/plugin-simple-objects: specifiers: '@pothos/core': workspace:* @@ -8473,6 +8513,12 @@ packages: safe-buffer: 5.2.1 dev: true + /edgedb/0.21.3: + resolution: {integrity: sha512-iFSvVRFpVVRaiMCFo3Cslol7C6xLD3mGM8MUWQYd8nSFvR2nsnyA2KjVmZw5MY0qZjdlm+8aJiCZffhjdtnLDw==} + engines: {node: '>= 10.0.0'} + hasBin: true + dev: true + /ee-first/1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} From f46dd33758c5008270b7da6bddb32b0049d0cb41 Mon Sep 17 00:00:00 2001 From: Baris Tikir Date: Fri, 19 Aug 2022 15:47:54 +0200 Subject: [PATCH 3/9] Fix Types --- .../plugin-edgedb/src/edgedb-field-builder.ts | 15 ++++----------- .../tests/__snapshots__/index.test.ts.snap | 1 + .../plugin-edgedb/tests/example/schema/index.ts | 13 ++++++------- packages/plugin-edgedb/tests/index.test.ts | 12 +++++------- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/packages/plugin-edgedb/src/edgedb-field-builder.ts b/packages/plugin-edgedb/src/edgedb-field-builder.ts index 68430df0c..968d95661 100644 --- a/packages/plugin-edgedb/src/edgedb-field-builder.ts +++ b/packages/plugin-edgedb/src/edgedb-field-builder.ts @@ -37,7 +37,7 @@ export class EdgeDBObjectFieldBuilder< }; }) | never, - Shape extends object = Model, + Shape extends object = Exclude, > extends RootBuilder { model: string; edgeDBFieldMap: FieldMap; @@ -119,7 +119,7 @@ export class EdgeDBObjectFieldBuilder< Type extends TypeParam, Nullable extends boolean, ResolveReturnShape, - Name extends CompatibleTypes, + Name extends CompatibleTypes, >( ...args: NormalizeArgs< [ @@ -127,7 +127,7 @@ export class EdgeDBObjectFieldBuilder< options?: Omit< PothosSchemaTypes.ObjectFieldOptions< Types, - Shape, // TODO Rename to model? + Shape, Type, Nullable, {}, @@ -141,17 +141,10 @@ export class EdgeDBObjectFieldBuilder< const [name, options = {} as never] = args; const typeConfig = this.builder.configStore.getTypeConfig(this.typename, 'Object'); - const usingSelect = !!typeConfig.extensions?.pothosPrismaSelect; + const usingSelect = false; return this.exposeField(name as never, { ...options, - extensions: { - ...options.extensions, - pothosPrismaVariant: name, - pothosPrismaSelect: usingSelect && { - [name]: true, - }, - }, }); } diff --git a/packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap b/packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap index 53522e55d..93e4c05ee 100644 --- a/packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap +++ b/packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap @@ -12,6 +12,7 @@ type ExplicitEdgeDBUser { type Post { author: User! content: String + createdAt: DateTime! id: ID! published: Boolean! title: String! diff --git a/packages/plugin-edgedb/tests/example/schema/index.ts b/packages/plugin-edgedb/tests/example/schema/index.ts index ad8f82053..bcff41692 100644 --- a/packages/plugin-edgedb/tests/example/schema/index.ts +++ b/packages/plugin-edgedb/tests/example/schema/index.ts @@ -28,7 +28,7 @@ builder.edgeDBObject('Post', { title: t.exposeString('title'), content: t.exposeString('content', { nullable: true }), published: t.exposeBoolean('published'), - // createdAt: t.expose('created_at'), + createdAt: t.expose('created_at', { type: 'DateTime' }), author: t.link('author'), }), }); @@ -46,19 +46,18 @@ builder.queryType({ me: t.field({ type: UserRef, nullable: true, - //@ts-ignore + // Temporarily since `User` doesnt have the links defined yet. + // @ts-ignore resolve: async (root, args, ctx, info) => { const user = await e .select(e.User, (user) => ({ - email: user.email, + id: true, + email: true, + name: true, filter: e.op(user.id, '=', e.uuid(ctx.user.id)), })) .run(db); - console.log(await e.count(e.User).run(db)); - - console.log('[query] user: ', user); - return user; }, }), diff --git a/packages/plugin-edgedb/tests/index.test.ts b/packages/plugin-edgedb/tests/index.test.ts index 3c8fa50c1..8eb561c56 100644 --- a/packages/plugin-edgedb/tests/index.test.ts +++ b/packages/plugin-edgedb/tests/index.test.ts @@ -11,7 +11,8 @@ describe('edgedb', () => { }); it('generates schema', () => { - expect(printSchema(schema)).toMatchSnapshot(); + const graphqlSchema = printSchema(schema); + expect(graphqlSchema).toMatchSnapshot(); }); it('queries for single item', async () => { @@ -29,16 +30,13 @@ describe('edgedb', () => { contextValue: { user: { id: 'a04bf8b8-1bfd-11ed-93f8-836b78753212' } }, }); - console.log('result::', result); - expect(result).toMatchInlineSnapshot(` Object { "data": Object { - "me": null, + "me": Object { + "id": "a04bf8b8-1bfd-11ed-93f8-836b78753212", + }, }, - "errors": Array [ - [GraphQLError: Cannot return null for non-nullable field ExplicitEdgeDBUser.id.], - ], } `); }); From fceaa3f79519d550c6c72cbefb34e68105a0f729 Mon Sep 17 00:00:00 2001 From: Baris Tikir Date: Sat, 20 Aug 2022 12:47:39 +0200 Subject: [PATCH 4/9] Remove CHANGELOG --- packages/plugin-edgedb/CHANGELOG.md | 340 ---------------------------- 1 file changed, 340 deletions(-) delete mode 100644 packages/plugin-edgedb/CHANGELOG.md diff --git a/packages/plugin-edgedb/CHANGELOG.md b/packages/plugin-edgedb/CHANGELOG.md deleted file mode 100644 index 6e8763dfc..000000000 --- a/packages/plugin-edgedb/CHANGELOG.md +++ /dev/null @@ -1,340 +0,0 @@ -# Change Log - -## 3.4.0 - -### Minor Changes - -- 3a7ff291: Refactor internal imports to remove import cycles - -### Patch Changes - -- 3a7ff291: Update dev dependencies - -## 3.3.0 - -### Minor Changes - -- ecb2714c: Add types entry to export map in package.json and update dev dependencies - - This should fix compatibility with typescripts new `"moduleResolution": "node12"` - -## 3.2.0 - -### Minor Changes - -- 241a385f: Add peer dependency on @pothos/core - -## 3.1.0 - -### Minor Changes - -- 6279235f: Update build process to use swc and move type definitions to dts directory - -### Patch Changes - -- 21a2454e: update dev dependencies - -## 3.0.2 - -### Patch Changes - -- 03aecf76: update .npmignore - -## 3.0.1 - -### Patch Changes - -- 2d9b21cd: Use workspace:\* for dev dependencies on pothos packages - -## 3.0.0 - -### Major Changes - -- 4caad5e4: Rename GiraphQL to Pothos - -## 2.11.0 - -### Minor Changes - -- 9307635a: Migrate build process to use turborepo - -## 2.10.1 - -### Patch Changes - -- c6aa732: graphql@15 type compatibility fix - -## 2.10.0 - -### Minor Changes - -- 48e9fd8: Add missing exports field to package.json - -## 2.9.1 - -### Patch Changes - -- c85dc33: Add types entry in package.json - -## 2.9.0 - -### Minor Changes - -- aeef5e5: Update dependencies - -## 2.8.0 - -### Minor Changes - -- 9107f29: Update dependencies (includes graphql 16) - -## 2.7.0 - -### Minor Changes - -- 17db3bd: Make type refs extendable by plugins - -## 2.6.2 - -### Patch Changes - -- c976bfe: Update dependencies - -## 2.6.1 - -### Patch Changes - -- 4150f92: Fixed esm transformer for path-imports from dependencies - -## 2.6.0 - -### Minor Changes - -- dc87e68: update esm build process so extensions are added during build rather than in source - -## 2.5.1 - -### Patch Changes - -- b4b8381: Updrade deps (typescript 4.4) - -## 2.5.0 - -### Minor Changes - -- a4c87cf: Use ".js" extensions everywhere and add module and exports to package.json to better - support ems in node - -## 2.4.2 - -### Patch Changes - -- f13208c: bump to fix latest tag - -## 2.4.1 - -### Patch Changes - -- 9ab8fbc: re-release previous version due to build-process issue - -## 2.4.0 - -### Minor Changes - -- 3dd3ff14: Updated dev dependencies, switched to pnpm, and added changesets for releases - -All notable changes to this project will be documented in this file. See -[Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -### 2.3.1 - 2021-08-03 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.3.1-alpha.0 - 2021-08-02 - -**Note:** Version bump only for package @giraphql/plugin-example - -## 2.3.0 - 2021-07-30 - -#### 🚀 Updates - -- add prisma plugin ([d427c82](https://github.com/hayes/giraphql/commit/d427c82)) - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.2.4 - 2021-07-23 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.2.4-alpha.0 - 2021-07-17 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.2.3 - 2021-07-10 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.2.2 - 2021-07-04 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.2.2-alpha.0 - 2021-07-04 - -#### 📦 Dependencies - -- upgrade typescript ([675f6a2](https://github.com/hayes/giraphql/commit/675f6a2)) - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.2.1 - 2021-07-02 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.2.0 - 2021-06-28 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.2.0-alpha.1 - 2021-06-28 - -**Note:** Version bump only for package @giraphql/plugin-example - -## 2.2.0-alpha.0 - 2021-06-28 - -#### 🚀 Updates - -- add errors plugin ([88509b4](https://github.com/hayes/giraphql/commit/88509b4)) - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.1.7 - 2021-06-11 - -#### 📦 Dependencies - -- update dev deps ([813d9d0](https://github.com/hayes/giraphql/commit/813d9d0)) - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.1.6 - 2021-06-10 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.1.6-alpha.0 - 2021-06-09 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.1.5 - 2021-05-18 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.1.4 - 2021-05-13 - -#### 📘 Docs - -- add docs for loadableNode ([1ae01e8](https://github.com/hayes/giraphql/commit/1ae01e8)) - -#### 🛠 Internals - -- add tests for loadableNode ([c1b49a0](https://github.com/hayes/giraphql/commit/c1b49a0)) - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.1.3 - 2021-05-12 - -#### 🛠 Internals - -- udate dev deps ([3251227](https://github.com/hayes/giraphql/commit/3251227)) - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.1.2 - 2021-05-10 - -#### 🐞 Fixes - -- update ci build command ([7e1d1d2](https://github.com/hayes/giraphql/commit/7e1d1d2)) - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.1.1 - 2021-05-10 - -#### 🐞 Fixes - -- force new version to fix esm build issue - ([25f1fd2](https://github.com/hayes/giraphql/commit/25f1fd2)) - -**Note:** Version bump only for package @giraphql/plugin-example - -## 2.1.0 - 2021-05-10 - -#### 🚀 Updates - -- add esm build for all packages ([d8bbdc9](https://github.com/hayes/giraphql/commit/d8bbdc9)) - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.8 - 2021-05-09 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.8-alpha.0 - 2021-05-08 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.7 - 2021-05-05 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.6 - 2021-05-05 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.6-alpha.0 - 2021-05-05 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.5 - 2021-05-02 - -#### 🛠 Internals - -- force version bumps and update validation to 2.0 range - ([07730b3](https://github.com/hayes/giraphql/commit/07730b3)) - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.4 - 2021-05-02 - -#### 🛠 Internals - -- migrate to @beemo/dev for dev tool configs - ([1da1283](https://github.com/hayes/giraphql/commit/1da1283)) - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.3 - 2021-04-16 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.3-alpha.0 - 2021-04-12 - -#### 📦 Dependencies - -- update dev dependencies ([25a15d4](https://github.com/hayes/giraphql/commit/25a15d4)) -- update dev deps ([cbfa0a4](https://github.com/hayes/giraphql/commit/cbfa0a4)) - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.2 - 2021-03-16 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.1 - 2021-02-19 - -**Note:** Version bump only for package @giraphql/plugin-example - -### 2.0.0 - 2021-02-16 - -#### 📘 Docs - -- add more docs o writing plugins ([b996fc6](https://github.com/hayes/giraphql/commit/b996fc6)) -- wip - plugin guide ([cf9c6ec](https://github.com/hayes/giraphql/commit/cf9c6ec)) - -**Note:** Version bump only for package @giraphql/plugin-example From fbd3fe017f89276d5e1b983fef44d10161791ea0 Mon Sep 17 00:00:00 2001 From: Baris Tikir Date: Sat, 20 Aug 2022 12:48:40 +0200 Subject: [PATCH 5/9] Fix CI --- packages/plugin-edgedb/.gitignore | 1 - packages/plugin-edgedb/edgedb.toml | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 packages/plugin-edgedb/edgedb.toml diff --git a/packages/plugin-edgedb/.gitignore b/packages/plugin-edgedb/.gitignore index 29fe61ab8..261d7b54f 100644 --- a/packages/plugin-edgedb/.gitignore +++ b/packages/plugin-edgedb/.gitignore @@ -1,4 +1,3 @@ -edgedb.toml dbschema/edgeql-js/* TODO.md api-design.md \ No newline at end of file diff --git a/packages/plugin-edgedb/edgedb.toml b/packages/plugin-edgedb/edgedb.toml new file mode 100644 index 000000000..ddcda65a9 --- /dev/null +++ b/packages/plugin-edgedb/edgedb.toml @@ -0,0 +1,2 @@ +[edgedb] +server-version = "2.1" From 054b72387601de4dc2c4857b1ebc003a743e4957 Mon Sep 17 00:00:00 2001 From: Baris Tikir Date: Sat, 20 Aug 2022 15:58:56 +0200 Subject: [PATCH 6/9] Types Clean Up ++ Add basic logic of edgeDBField --- .../plugin-edgedb/src/edgedb-field-builder.ts | 21 +- packages/plugin-edgedb/src/field-builder.ts | 23 +- packages/plugin-edgedb/src/global-types.ts | 78 +++++-- packages/plugin-edgedb/src/index.ts | 14 +- packages/plugin-edgedb/src/object-ref.ts | 8 +- packages/plugin-edgedb/src/types.ts | 205 +++++++++++------- packages/plugin-edgedb/src/util/datamodel.ts | 7 +- .../tests/__snapshots__/index.test.ts.snap | 2 + .../tests/example/schema/index.ts | 19 ++ 9 files changed, 240 insertions(+), 137 deletions(-) diff --git a/packages/plugin-edgedb/src/edgedb-field-builder.ts b/packages/plugin-edgedb/src/edgedb-field-builder.ts index 968d95661..83413db78 100644 --- a/packages/plugin-edgedb/src/edgedb-field-builder.ts +++ b/packages/plugin-edgedb/src/edgedb-field-builder.ts @@ -14,7 +14,7 @@ import { FieldMap } from './util/relation-map'; import { getLink, getRefFromModel } from './util/datamodel'; import { extractTargetTypeName } from './util/target'; import { isMultiLink } from './util/links'; -import { RelatedFieldOptions } from './types'; +import { EdgeDBModelTypes, RelatedFieldOptions } from './types'; // Workaround for FieldKind not being extended on Builder classes const RootBuilder: { @@ -29,16 +29,9 @@ const RootBuilder: { export class EdgeDBObjectFieldBuilder< Types extends SchemaTypes, - Model extends - | ({ [ModelKey in keyof Model]: Model[ModelKey] extends infer U ? U : never } & { - Fields: string | never; - Links: { - [Key in Model['Fields']]: { Shape: Model['Links'][Key] }; - }; - }) - | never, - Shape extends object = Exclude, -> extends RootBuilder { + Model extends EdgeDBModelTypes, + Shape extends object = Model['Shape'], +> extends RootBuilder { model: string; edgeDBFieldMap: FieldMap; @@ -66,7 +59,7 @@ export class EdgeDBObjectFieldBuilder< } link< - Field extends Model['Fields'], + Field extends Model['LinkName'], Nullable extends boolean, Args extends InputFieldMap, ResolveReturnShape, @@ -119,7 +112,7 @@ export class EdgeDBObjectFieldBuilder< Type extends TypeParam, Nullable extends boolean, ResolveReturnShape, - Name extends CompatibleTypes, + Name extends CompatibleTypes, >( ...args: NormalizeArgs< [ @@ -152,7 +145,7 @@ export class EdgeDBObjectFieldBuilder< return < Nullable extends boolean, ResolveReturnShape, - Name extends CompatibleTypes, + Name extends CompatibleTypes, >( ...args: NormalizeArgs< [ diff --git a/packages/plugin-edgedb/src/field-builder.ts b/packages/plugin-edgedb/src/field-builder.ts index 3e5b09097..0cb185c1d 100644 --- a/packages/plugin-edgedb/src/field-builder.ts +++ b/packages/plugin-edgedb/src/field-builder.ts @@ -1,4 +1,6 @@ -import { FieldKind, RootFieldBuilder, SchemaTypes } from '@pothos/core'; +import { FieldKind, ObjectRef, RootFieldBuilder, SchemaTypes } from '@pothos/core'; +import { GraphQLResolveInfo } from 'graphql'; +import { getRefFromModel } from './util/datamodel'; export * from './edgedb-field-builder'; @@ -8,4 +10,21 @@ const fieldBuilderProto = RootFieldBuilder.prototype as PothosSchemaTypes.RootFi FieldKind >; -// fieldBuilderProto.edgeDBField = function edgeDBField(...) +fieldBuilderProto.edgeDBField = function edgeDBField({ type, resolve, ...options }) { + const modelOrRef = Array.isArray(type) ? type[0] : type; + const typeRef = + typeof modelOrRef === 'string' + ? getRefFromModel(modelOrRef, this.builder) + : (modelOrRef as ObjectRef); + const typeParam = Array.isArray(type) ? ([typeRef] as [ObjectRef]) : typeRef; + + return this.field({ + ...(options as {}), + type: typeParam, + resolve: (parent: unknown, args: unknown, ctx: {}, info: GraphQLResolveInfo) => { + const query = null; + + return resolve(query as never, parent as never, args as never, ctx, info) as never; + }, + }) as never; +}; diff --git a/packages/plugin-edgedb/src/global-types.ts b/packages/plugin-edgedb/src/global-types.ts index 43a807da6..00f27a177 100644 --- a/packages/plugin-edgedb/src/global-types.ts +++ b/packages/plugin-edgedb/src/global-types.ts @@ -1,22 +1,28 @@ /* eslint-disable @typescript-eslint/no-unud-vars */ import { + FieldKind, FieldNullability, + FieldRef, InputFieldMap, InterfaceParam, + PluginName, SchemaTypes, + ShapeFromTypeParam, TypeParam, } from '@pothos/core'; import { EdgeDBDriver, + EdgeDBFieldOptions, + EdgeDBSchemaTypeKeys, EdgeDBModelShape, + EdgeDBModelTypes, EdgeDBObjectFieldOptions, EdgeDBObjectTypeOptions, EdgeDBQueryBuilder, - EdgeDBTypeKeys, } from './types'; import type { EdgeDBPlugin } from '.'; import { EdgeDBObjectFieldBuilder as InternalEdgeDBObjectFieldBuilder } from './edgedb-field-builder'; -import { EdgeDBObjectRef } from './object-ref'; +import { edgeDBModelKey, EdgeDBObjectRef } from './object-ref'; declare global { export namespace PothosSchemaTypes { @@ -78,31 +84,67 @@ declare global { >; } + export interface RootFieldBuilder< + Types extends SchemaTypes, + ParentShape, + Kind extends FieldKind = FieldKind, + > { + edgeDBField: < + Args extends InputFieldMap, + TypeParam extends + | EdgeDBObjectRef + | keyof Types['EdgeDBTypes']['default'] + | [keyof Types['EdgeDBTypes']['default']] + | [EdgeDBObjectRef], + Nullable extends FieldNullability, + ResolveShape, + ResolveReturnShape, + Type extends TypeParam extends [unknown] + ? [ObjectRef] + : ObjectRef, + Model extends EdgeDBModelTypes = EdgeDBModelTypes & + (TypeParam extends [keyof Types['EdgeDBTypes']['default']] + ? Types['EdgeDBTypes']['default'][TypeParam[0]] + : TypeParam extends [EdgeDBObjectRef] + ? TypeParam[0][typeof edgeDBModelKey] + : TypeParam extends EdgeDBObjectRef + ? TypeParam[typeof edgeDBModelKey] + : TypeParam extends keyof Types['EdgeDBTypes']['default'] + ? Types['EdgeDBTypes']['default'][TypeParam] + : never), + >( + options: EdgeDBFieldOptions< + Types, + ParentShape, + TypeParam, + Model, + Type, + Args, + Nullable, + ResolveShape, + ResolveReturnShape, + Kind + >, + ) => FieldRef>; + } + export interface SchemaBuilder { edgeDBObject: < - Name extends EdgeDBTypeKeys, + Name extends EdgeDBSchemaTypeKeys, Interfaces extends InterfaceParam[], - Model extends EdgeDBModelShape, + Model extends EdgeDBModelShape & Types['EdgeDBTypes']['default'][Name], + Shape extends object = Model['Shape'], >( name: Name, - options: EdgeDBObjectTypeOptions, - ) => EdgeDBObjectRef; + options: EdgeDBObjectTypeOptions, + ) => EdgeDBObjectRef; } export interface EdgeDBObjectFieldBuilder< Types extends SchemaTypes, - Model extends - | ({ [ModelKey in keyof Model]: Model[ModelKey] extends infer U ? U : never } & { - Fields: string | never; - Links: { - [Key in Model['Fields']]: { - Shape: Model['Links'][Key]; - }; - }; - }) - | never, - Shape extends object = Model, + Model extends EdgeDBModelTypes, + Shape extends object = Model['Shape'], > extends InternalEdgeDBObjectFieldBuilder, - RootFieldBuilder {} + RootFieldBuilder {} } } diff --git a/packages/plugin-edgedb/src/index.ts b/packages/plugin-edgedb/src/index.ts index aa46a6341..ec38ad8b5 100644 --- a/packages/plugin-edgedb/src/index.ts +++ b/packages/plugin-edgedb/src/index.ts @@ -11,6 +11,7 @@ import SchemaBuilder, { SchemaTypes, } from '@pothos/core'; import { EdgeDBObjectFieldBuilder as InternalEdgeDBObjectFieldBuilder } from './field-builder'; +import { EdgeDBModelTypes } from './types'; export * from './types'; @@ -25,17 +26,8 @@ export type EdgeDBObjectFieldBuilder< export const ObjectFieldBuilder = InternalEdgeDBObjectFieldBuilder as new < Types extends SchemaTypes, - Model extends - | ({ [ModelKey in keyof Model]: Model[ModelKey] extends infer U ? U : never } & { - Fields: string | never; - Links: { - [Key in Model['Fields']]: { - Shape: Model['Links'][Key]; - }; - }; - }) - | never, - Shape extends object = Model, + Model extends EdgeDBModelTypes, + Shape extends object = Model['Shape'], >( name: string, builder: PothosSchemaTypes.SchemaBuilder, diff --git a/packages/plugin-edgedb/src/object-ref.ts b/packages/plugin-edgedb/src/object-ref.ts index 644cf1f5b..047de53fb 100644 --- a/packages/plugin-edgedb/src/object-ref.ts +++ b/packages/plugin-edgedb/src/object-ref.ts @@ -1,12 +1,8 @@ -import { ObjectRef, SchemaTypes } from '@pothos/core'; +import { ObjectRef } from '@pothos/core'; import type { EdgeDBModelTypes } from './types'; export const edgeDBModelKey = Symbol.for('Pothos.edgeDBModelKey'); -export class EdgeDBObjectRef< - Types extends SchemaTypes, - Model extends EdgeDBModelTypes, - T = {}, -> extends ObjectRef { +export class EdgeDBObjectRef extends ObjectRef { [edgeDBModelKey]!: Model; } diff --git a/packages/plugin-edgedb/src/types.ts b/packages/plugin-edgedb/src/types.ts index 25f44d6eb..c7d2aa5b7 100644 --- a/packages/plugin-edgedb/src/types.ts +++ b/packages/plugin-edgedb/src/types.ts @@ -1,31 +1,31 @@ import type { Client } from 'edgedb'; import { + FieldKind, FieldMap, FieldNullability, + FieldOptionsFromKind, InputFieldMap, InputShapeFromFields, InterfaceParam, + ListResolveValue, MaybePromise, Normalize, ObjectRef, SchemaTypes, + ShapeFromTypeParam, ShapeWithNullability, TypeParam, } from '@pothos/core'; import type { EdgeDBObjectFieldBuilder } from './edgedb-field-builder'; import { GraphQLResolveInfo } from 'graphql'; +import { EdgeDBObjectRef } from './object-ref'; export interface EdgeDBQueryBuilder { default: unknown; } export interface EdgeDBDriver extends Client {} -export type EdgeDBModels< - Name extends EdgeDBTypeKeys, - Types extends SchemaTypes = SchemaTypes, -> = Types['EdgeDBTypes']['default'][Name] extends infer T ? T : never; - -export type EdgeDBTypeKeys = +export type EdgeDBSchemaTypeKeys = keyof Types['EdgeDBTypes']['default'] extends infer Key ? Key extends string ? Key @@ -47,36 +47,58 @@ type EdgeDBDefaultExportKey< KeyType extends string | number | symbol = EdgeDBDefaultExportKeyTypes, > = Extract; -export type EdgeDBModelTypes = +export type EdgeDBSchemaTypes = Types['EdgeDBTypes']['default'] extends infer ObjectTypeMap ? ObjectTypeMap extends object ? ObjectTypeMap : never : never; +export interface EdgeDBModelTypes { + Name: string; + Shape: {}; + MultiLinks: string; + LinkName: string; + Links: Record< + string, + { + Shape: unknown; + Types: EdgeDBModelTypes; + } + >; +} + export type EdgeDBModelShape< Types extends SchemaTypes, - Name extends EdgeDBTypeKeys, -> = EdgeDBModelTypes[Name] extends infer Property - ? Property extends BaseObject & EdgeDBModelTypes[Name] - ? Property & { - Fields: extractLinks extends infer Field - ? Field extends string - ? SplitLT - : null + Name extends EdgeDBSchemaTypeKeys, +> = EdgeDBSchemaTypes[Name] extends infer Property + ? Property extends BaseObject + ? { + Name: Name; + Shape: Property; + MultiLinks: ''; + LinkName: extractLinks extends infer Link + ? Link extends string + ? SplitLT + : never : never; - Links: { - [Key in Property['Fields']]: { - Shape: Property['Fields'][Key] extends infer Shape - ? Shape extends BaseObject - ? Shape - : Shape extends Array - ? Shape[0] - : never - : never; - }; - }; - } + } extends infer ModelTypesWithoutLinks + ? ModelTypesWithoutLinks extends { LinkName: string; Shape: Record } + ? ModelTypesWithoutLinks & { + Links: { + [Key in ModelTypesWithoutLinks['LinkName']]: { + Shape: ModelTypesWithoutLinks['Shape'][Key] extends infer Shape + ? Shape extends BaseObject + ? Shape + : Shape extends Array + ? Shape[0] + : never + : never; + }; + }; + } + : ModelTypesWithoutLinks & { Links: {} } + : never : never : never; @@ -104,17 +126,14 @@ export enum TypeKind { range = 'range', } export type tupleOf = [T, ...T[]] | []; -export type cardinalityAssignable = C extends Cardinality.Empty - ? Cardinality.Empty - : C extends Cardinality.One - ? Cardinality.One - : C extends Cardinality.AtMostOne - ? Cardinality.One | Cardinality.AtMostOne | Cardinality.Empty - : C extends Cardinality.AtLeastOne - ? Cardinality.One | Cardinality.AtLeastOne | Cardinality.Many - : C extends Cardinality.Many - ? Cardinality - : never; + +export type CardinalityAssignable = { + Empty: Cardinality.Empty; + One: Cardinality.One; + AtLeastOne: Cardinality.One | Cardinality.Many | Cardinality.AtLeastOne; + AtMostOne: Cardinality.One | Cardinality.AtMostOne | Cardinality.Empty; + Many: Cardinality.Many; +}[Card]; export interface BaseObject { id: any; @@ -216,7 +235,7 @@ export namespace EdgeDB { } } -type pointersToObjectType

= ObjectType; +type PointersToObjectType

= ObjectType; export type SelectModifiers = { filter?: TypeSet, Cardinality>; @@ -237,7 +256,7 @@ export type extractObjectShapeToSelectShape + CardinalityAssignable > : TObject['__pointers__'][k] extends LinkDesc ? {} // as link, currently no type @@ -304,50 +323,26 @@ export type EdgeDBObjectFieldOptions< type EdgeDBObjectFieldsShape< Types extends SchemaTypes, - Model extends - | ({ - [key: string]: Model[keyof Model] extends infer U ? U : never; - } & { - Fields: string | never; - Links: { - [Key in Model['Fields']]: { Shape: Model['Links'][Key] }; - }; - }) - | never, - Shape extends object = Extract, + Model extends EdgeDBModelTypes, + Shape extends object, > = (t: EdgeDBObjectFieldBuilder) => FieldMap; export type EdgeDBObjectTypeOptions< Types extends SchemaTypes, Interfaces extends InterfaceParam[], - Model extends - | ({ - [key: string]: Model[keyof Model] extends infer U ? U : never; - } & { - Fields: string | never; - Links: { - [Key in Model['Fields']]: { Shape: Model['Links'][Key] }; - }; - }) - | never, + Model extends EdgeDBModelTypes, + Shape extends object, > = Omit< - | PothosSchemaTypes.ObjectTypeOptions - | PothosSchemaTypes.ObjectTypeWithInterfaceOptions, + | PothosSchemaTypes.ObjectTypeOptions + | PothosSchemaTypes.ObjectTypeWithInterfaceOptions, 'fields' | 'description' > & { description?: string | false; - fields?: EdgeDBObjectFieldsShape; + fields?: EdgeDBObjectFieldsShape; }; type RefForLink< - Model extends - | ({ [ModelKey in keyof Model]: Model[ModelKey] extends infer U ? U : never } & { - Fields: string | never; - Links: { - [Key in Model['Fields']]: { Shape: Model['Links'][Key] }; - }; - }) - | never, + Model extends EdgeDBModelTypes, Field extends keyof Model['Links'], > = Model['Links'][Field] extends unknown[] ? [ObjectRef] @@ -355,14 +350,7 @@ type RefForLink< export type RelatedFieldOptions< Types extends SchemaTypes, - Model extends - | ({ [ModelKey in keyof Model]: Model[ModelKey] extends infer U ? U : never } & { - Fields: string | never; - Links: { - [Key in Model['Fields']]: { Shape: Model['Links'][Key] }; - }; - }) - | never, + Model extends EdgeDBModelTypes, Field extends keyof Model['Links'], Nullable extends boolean, Args extends InputFieldMap, @@ -385,5 +373,60 @@ export type RelatedFieldOptions< args: InputShapeFromFields, context: Types['Context'], info: GraphQLResolveInfo, - ) => MaybePromise>; + ) => MaybePromise> & { + type?: EdgeDBObjectRef; + }; }; + +export type EdgeDBFieldResolver< + Types extends SchemaTypes, + Model extends EdgeDBModelTypes, + Parent, + Param extends TypeParam, + Args extends InputFieldMap, + Nullable extends FieldNullability, + ResolveReturnShape, +> = ( + query: {}, + parent: Parent, + args: InputShapeFromFields, + context: Types['Context'], + info: GraphQLResolveInfo, +) => ShapeFromTypeParam extends infer Shape + ? [Shape] extends [[readonly (infer Item)[] | null | undefined]] + ? ListResolveValue + : MaybePromise + : never; + +export type EdgeDBFieldOptions< + Types extends SchemaTypes, + ParentShape, + Type extends + | EdgeDBObjectRef + | keyof Types['EdgeDBTypes'] + | [keyof Types['EdgeDBTypes']] + | [EdgeDBObjectRef], + Model extends EdgeDBModelTypes, + Param extends TypeParam, + Args extends InputFieldMap, + Nullable extends FieldNullability, + ResolveShape, + ResolveReturnShape, + Kind extends FieldKind = FieldKind, +> = FieldOptionsFromKind< + Types, + ParentShape, + Param, + Nullable, + Args, + Kind, + ResolveShape, + ResolveReturnShape +> extends infer FieldOptions + ? Omit & { + type: Type; + resolve: FieldOptions extends { resolve?: (parent: infer Parent, ...args: any[]) => unknown } + ? EdgeDBFieldResolver + : EdgeDBFieldResolver; + } + : never; diff --git a/packages/plugin-edgedb/src/util/datamodel.ts b/packages/plugin-edgedb/src/util/datamodel.ts index 634caa450..0c6bd2ec9 100644 --- a/packages/plugin-edgedb/src/util/datamodel.ts +++ b/packages/plugin-edgedb/src/util/datamodel.ts @@ -3,15 +3,12 @@ import { EdgeDBObjectRef } from '../object-ref'; import { EdgeDBModelTypes } from '../types'; import { getObjectsTypes } from './get-client'; -export const refMap = new WeakMap< - object, - Map>> ->(); +export const refMap = new WeakMap>>(); export function getRefFromModel( name: string, builder: PothosSchemaTypes.SchemaBuilder, -): EdgeDBObjectRef> { +): EdgeDBObjectRef { if (!refMap.has(builder)) { refMap.set(builder, new Map()); } diff --git a/packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap b/packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap index 93e4c05ee..83a4c5e41 100644 --- a/packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap +++ b/packages/plugin-edgedb/tests/__snapshots__/index.test.ts.snap @@ -25,10 +25,12 @@ type PostPreview { type Query { me: ExplicitEdgeDBUser + users: [User!] } type User { email: String! + id: ID! name: String posts: [Post!]! }" diff --git a/packages/plugin-edgedb/tests/example/schema/index.ts b/packages/plugin-edgedb/tests/example/schema/index.ts index bcff41692..ae76d6a4d 100644 --- a/packages/plugin-edgedb/tests/example/schema/index.ts +++ b/packages/plugin-edgedb/tests/example/schema/index.ts @@ -1,6 +1,7 @@ import e, { Post, User } from '../../client'; import { db } from '../db'; import builder from '../builder'; +import { getNamedType } from 'graphql'; const PostPreview = builder.objectRef('PostPreview'); PostPreview.implement({ @@ -35,6 +36,7 @@ builder.edgeDBObject('Post', { builder.edgeDBObject('User', { fields: (t) => ({ + id: t.exposeID('id'), email: t.exposeString('email'), name: t.exposeString('name', { nullable: true }), posts: t.link('posts'), @@ -49,6 +51,9 @@ builder.queryType({ // Temporarily since `User` doesnt have the links defined yet. // @ts-ignore resolve: async (root, args, ctx, info) => { + console.log(info.returnType); + console.log(getNamedType(info.returnType)); + const user = await e .select(e.User, (user) => ({ id: true, @@ -61,6 +66,20 @@ builder.queryType({ return user; }, }), + users: t.edgeDBField({ + type: ['User'], + nullable: true, + resolve: async (_query, _, __, ctx) => { + const user = await e + .select(e.User, () => ({ + email: true, + title: true, + })) + .run(db); + + return user; + }, + }), }), }); From eb18ba487d2b3ddfe5219fe9c3b0d0c38a31af84 Mon Sep 17 00:00:00 2001 From: Baris Tikir Date: Sun, 21 Aug 2022 00:23:46 +0200 Subject: [PATCH 7/9] Fix resolve return types --- packages/plugin-edgedb/src/global-types.ts | 14 +++++++++++++- packages/plugin-edgedb/src/types.ts | 10 +++++----- .../plugin-edgedb/tests/example/schema/index.ts | 14 ++++++-------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/packages/plugin-edgedb/src/global-types.ts b/packages/plugin-edgedb/src/global-types.ts index 00f27a177..09f0c15cf 100644 --- a/packages/plugin-edgedb/src/global-types.ts +++ b/packages/plugin-edgedb/src/global-types.ts @@ -102,7 +102,19 @@ declare global { Type extends TypeParam extends [unknown] ? [ObjectRef] : ObjectRef, - Model extends EdgeDBModelTypes = EdgeDBModelTypes & + Model extends EdgeDBModelTypes = EdgeDBModelShape< + Types, + // @ts-expect-error -> string | number | symbol not assignable to .. + TypeParam extends [keyof Types['EdgeDBTypes']['default']] + ? keyof Types['EdgeDBTypes']['default'][TypeParam[0]] + : TypeParam extends [EdgeDBObjectRef] + ? TypeParam[0][typeof edgeDBModelKey] + : TypeParam extends EdgeDBObjectRef + ? TypeParam[typeof edgeDBModelKey] + : TypeParam extends keyof Types['EdgeDBTypes']['default'] + ? TypeParam + : never + > & (TypeParam extends [keyof Types['EdgeDBTypes']['default']] ? Types['EdgeDBTypes']['default'][TypeParam[0]] : TypeParam extends [EdgeDBObjectRef] diff --git a/packages/plugin-edgedb/src/types.ts b/packages/plugin-edgedb/src/types.ts index c7d2aa5b7..4ce88a557 100644 --- a/packages/plugin-edgedb/src/types.ts +++ b/packages/plugin-edgedb/src/types.ts @@ -42,7 +42,7 @@ export type EdgeDBDefaultExportKeyTypes = { : never; }[keyof DefaultExports]; -type EdgeDBDefaultExportKey< +type EdgeDBModelKeyAsString< DefaultExports, KeyType extends string | number | symbol = EdgeDBDefaultExportKeyTypes, > = Extract; @@ -394,8 +394,8 @@ export type EdgeDBFieldResolver< info: GraphQLResolveInfo, ) => ShapeFromTypeParam extends infer Shape ? [Shape] extends [[readonly (infer Item)[] | null | undefined]] - ? ListResolveValue - : MaybePromise + ? ListResolveValue, Item, ResolveReturnShape> + : MaybePromise> : never; export type EdgeDBFieldOptions< @@ -403,8 +403,8 @@ export type EdgeDBFieldOptions< ParentShape, Type extends | EdgeDBObjectRef - | keyof Types['EdgeDBTypes'] - | [keyof Types['EdgeDBTypes']] + | keyof Types['EdgeDBTypes']['default'] + | [keyof Types['EdgeDBTypes']['default']] | [EdgeDBObjectRef], Model extends EdgeDBModelTypes, Param extends TypeParam, diff --git a/packages/plugin-edgedb/tests/example/schema/index.ts b/packages/plugin-edgedb/tests/example/schema/index.ts index ae76d6a4d..3d781e8e5 100644 --- a/packages/plugin-edgedb/tests/example/schema/index.ts +++ b/packages/plugin-edgedb/tests/example/schema/index.ts @@ -1,7 +1,6 @@ import e, { Post, User } from '../../client'; import { db } from '../db'; import builder from '../builder'; -import { getNamedType } from 'graphql'; const PostPreview = builder.objectRef('PostPreview'); PostPreview.implement({ @@ -51,9 +50,6 @@ builder.queryType({ // Temporarily since `User` doesnt have the links defined yet. // @ts-ignore resolve: async (root, args, ctx, info) => { - console.log(info.returnType); - console.log(getNamedType(info.returnType)); - const user = await e .select(e.User, (user) => ({ id: true, @@ -67,13 +63,15 @@ builder.queryType({ }, }), users: t.edgeDBField({ - type: ['User'], + type: 'User', nullable: true, - resolve: async (_query, _, __, ctx) => { + resolve: async (_query, _parent, _args, ctx) => { const user = await e - .select(e.User, () => ({ + .select(e.User, (user) => ({ + id: true, email: true, - title: true, + name: true, + filter: e.op(user.id, '=', e.uuid(ctx.user.id)), })) .run(db); From f853b76a8a0bc58ec6fa9b4578bb7aa706515958 Mon Sep 17 00:00:00 2001 From: Baris Tikir Date: Mon, 22 Aug 2022 13:13:01 +0200 Subject: [PATCH 8/9] Add ReturnShape to Model Types ++ Fix edgeDBFields resolve return type on array types --- packages/plugin-edgedb/src/global-types.ts | 21 ++--- packages/plugin-edgedb/src/types.ts | 85 ++++++++++++++----- .../tests/example/schema/index.ts | 20 ++++- 3 files changed, 86 insertions(+), 40 deletions(-) diff --git a/packages/plugin-edgedb/src/global-types.ts b/packages/plugin-edgedb/src/global-types.ts index 09f0c15cf..60e3e0211 100644 --- a/packages/plugin-edgedb/src/global-types.ts +++ b/packages/plugin-edgedb/src/global-types.ts @@ -100,30 +100,21 @@ declare global { ResolveShape, ResolveReturnShape, Type extends TypeParam extends [unknown] - ? [ObjectRef] - : ObjectRef, + ? [ObjectRef] + : ObjectRef, Model extends EdgeDBModelTypes = EdgeDBModelShape< Types, // @ts-expect-error -> string | number | symbol not assignable to .. TypeParam extends [keyof Types['EdgeDBTypes']['default']] - ? keyof Types['EdgeDBTypes']['default'][TypeParam[0]] + ? TypeParam[0] : TypeParam extends [EdgeDBObjectRef] - ? TypeParam[0][typeof edgeDBModelKey] + ? TypeParam[0][typeof edgeDBModelKey]['Name'] : TypeParam extends EdgeDBObjectRef - ? TypeParam[typeof edgeDBModelKey] + ? TypeParam[typeof edgeDBModelKey]['Name'] : TypeParam extends keyof Types['EdgeDBTypes']['default'] ? TypeParam : never - > & - (TypeParam extends [keyof Types['EdgeDBTypes']['default']] - ? Types['EdgeDBTypes']['default'][TypeParam[0]] - : TypeParam extends [EdgeDBObjectRef] - ? TypeParam[0][typeof edgeDBModelKey] - : TypeParam extends EdgeDBObjectRef - ? TypeParam[typeof edgeDBModelKey] - : TypeParam extends keyof Types['EdgeDBTypes']['default'] - ? Types['EdgeDBTypes']['default'][TypeParam] - : never), + >, >( options: EdgeDBFieldOptions< Types, diff --git a/packages/plugin-edgedb/src/types.ts b/packages/plugin-edgedb/src/types.ts index 4ce88a557..ec2fc85e5 100644 --- a/packages/plugin-edgedb/src/types.ts +++ b/packages/plugin-edgedb/src/types.ts @@ -57,7 +57,8 @@ export type EdgeDBSchemaTypes = export interface EdgeDBModelTypes { Name: string; Shape: {}; - MultiLinks: string; + ReturnShape: {}; + MultiLink: string; LinkName: string; Links: Record< string, @@ -71,29 +72,57 @@ export interface EdgeDBModelTypes { export type EdgeDBModelShape< Types extends SchemaTypes, Name extends EdgeDBSchemaTypeKeys, -> = EdgeDBSchemaTypes[Name] extends infer Property - ? Property extends BaseObject +> = EdgeDBSchemaTypes[Name] extends infer ModelProperties + ? ModelProperties extends BaseObject ? { Name: Name; - Shape: Property; - MultiLinks: ''; - LinkName: extractLinks extends infer Link + Shape: ModelProperties; + ReturnShape: { + [K in keyof ModelProperties]?: ModelProperties[K] extends ObjectType + ? ModelProperties[K] extends infer Link + ? { [K in keyof Link]?: Link[K] } + : ModelProperties[K] + : ModelProperties[K]; + }; + MultiLink: extractMultiLinks extends infer Link + ? Link extends string + ? SplitLT + : never + : never; + LinkName: extractLinks extends infer Link ? Link extends string ? SplitLT : never : never; } extends infer ModelTypesWithoutLinks - ? ModelTypesWithoutLinks extends { LinkName: string; Shape: Record } + ? ModelTypesWithoutLinks extends { + LinkName: string; + Shape: Record; + } ? ModelTypesWithoutLinks & { Links: { [Key in ModelTypesWithoutLinks['LinkName']]: { Shape: ModelTypesWithoutLinks['Shape'][Key] extends infer Shape ? Shape extends BaseObject - ? Shape + ? { [K in keyof Shape]?: Shape[K] } : Shape extends Array - ? Shape[0] + ? { [K in keyof Shape[0]]?: Shape[0][K] } : never : never; + Types: EdgeDBModelShape< + Types, + ModelTypesWithoutLinks['Shape'][Key] extends infer Base + ? Base extends TypeSet + ? Base['__element__']['__name__'] extends infer ModelName + ? ModelName extends string + ? SplitDefault extends EdgeDBSchemaTypeKeys + ? SplitDefault + : never + : never + : never + : never + : never + >; }; }; } @@ -275,16 +304,17 @@ type extractLinksToPartial = Shape extends : never : never; -// Extract links from queryBuilder object, not from type -// Shape extends infer T -// ? T extends TypeSet -// ? { -// [K in keyof T['__element__']['__pointers__']]: T['__element__']['__pointers__'][K] extends LinkDesc -// ? boolean -// : never; -// } -// : never -// : never; +type extractMultiLinksToPartial = Shape extends infer T + ? T extends object + ? { + [Key in keyof Shape]: Shape[Key] extends infer Link + ? Link extends Array + ? boolean + : never + : never; + } + : never + : never; // Filter out links from model type export type extractLinks< @@ -295,14 +325,25 @@ export type extractLinks< [K in keyof Links]: [boolean] extends [Links[K]] ? K : never; }[keyof Links] : null; +export type extractMultiLinks< + Model extends object, + PartialLinks extends extractMultiLinksToPartial = extractMultiLinksToPartial, +> = PartialLinks extends infer Links + ? { + [K in keyof Links]: [boolean] extends [Links[K]] ? K : never; + }[keyof Links] + : null; type Split = S extends `${infer T}${D}${infer U}` ? never : [S][0]; // For removing backlinks from `link` fields -// eg. fields: "posts" | "comments" | " fields: "posts" | "comments" +// eg. SplitLT<"posts" | "comments" | " -> "posts" | "comments" export type SplitLT = Split; +// Only way to get models name is to split it on its BaseObject __name__ +// eg. SplitDefault<"default::Post"> -> "Post" +export type SplitDefault = Split; export type EdgeDBObjectFieldOptions< Types extends SchemaTypes, @@ -394,8 +435,8 @@ export type EdgeDBFieldResolver< info: GraphQLResolveInfo, ) => ShapeFromTypeParam extends infer Shape ? [Shape] extends [[readonly (infer Item)[] | null | undefined]] - ? ListResolveValue, Item, ResolveReturnShape> - : MaybePromise> + ? ListResolveValue + : MaybePromise : never; export type EdgeDBFieldOptions< diff --git a/packages/plugin-edgedb/tests/example/schema/index.ts b/packages/plugin-edgedb/tests/example/schema/index.ts index 3d781e8e5..648f9dd17 100644 --- a/packages/plugin-edgedb/tests/example/schema/index.ts +++ b/packages/plugin-edgedb/tests/example/schema/index.ts @@ -62,20 +62,34 @@ builder.queryType({ return user; }, }), - users: t.edgeDBField({ + user: t.edgeDBField({ type: 'User', nullable: true, resolve: async (_query, _parent, _args, ctx) => { const user = await e + .select(e.User, (user) => ({ + ...e.User['*'], + filter: e.op(user.id, '=', e.uuid(ctx.user.id)), + })) + .run(db); + return user; + // ^? + }, + }), + users: t.edgeDBField({ + type: ['User'], + nullable: true, + resolve: async (_query, _parent, _args, ctx) => { + const users = await e .select(e.User, (user) => ({ id: true, email: true, name: true, - filter: e.op(user.id, '=', e.uuid(ctx.user.id)), })) .run(db); - return user; + return users; + // ^? }, }), }), From 8ec9e25d91d12fa9059a8ec0314c8b493944444e Mon Sep 17 00:00:00 2001 From: Baris Tikir Date: Mon, 22 Aug 2022 16:39:04 +0200 Subject: [PATCH 9/9] Fix ReturnShape's optionality with DeepPartial --- packages/plugin-edgedb/src/types.ts | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/plugin-edgedb/src/types.ts b/packages/plugin-edgedb/src/types.ts index ec2fc85e5..7d2ef094f 100644 --- a/packages/plugin-edgedb/src/types.ts +++ b/packages/plugin-edgedb/src/types.ts @@ -69,6 +69,14 @@ export interface EdgeDBModelTypes { >; } +export type DeepPartial = T extends Promise + ? Promise> + : T extends object + ? { [P in keyof T]?: DeepPartial } + : T extends (infer U)[] + ? DeepPartial[] + : T; + export type EdgeDBModelShape< Types extends SchemaTypes, Name extends EdgeDBSchemaTypeKeys, @@ -77,13 +85,7 @@ export type EdgeDBModelShape< ? { Name: Name; Shape: ModelProperties; - ReturnShape: { - [K in keyof ModelProperties]?: ModelProperties[K] extends ObjectType - ? ModelProperties[K] extends infer Link - ? { [K in keyof Link]?: Link[K] } - : ModelProperties[K] - : ModelProperties[K]; - }; + ReturnShape: DeepPartial; MultiLink: extractMultiLinks extends infer Link ? Link extends string ? SplitLT @@ -102,13 +104,7 @@ export type EdgeDBModelShape< ? ModelTypesWithoutLinks & { Links: { [Key in ModelTypesWithoutLinks['LinkName']]: { - Shape: ModelTypesWithoutLinks['Shape'][Key] extends infer Shape - ? Shape extends BaseObject - ? { [K in keyof Shape]?: Shape[K] } - : Shape extends Array - ? { [K in keyof Shape[0]]?: Shape[0][K] } - : never - : never; + Shape: ModelTypesWithoutLinks['Shape'][Key]; Types: EdgeDBModelShape< Types, ModelTypesWithoutLinks['Shape'][Key] extends infer Base