Skip to content

Commit

Permalink
orchid-graphql: type check for graph.resolve query
Browse files Browse the repository at this point in the history
  • Loading branch information
IlyaSemenov committed Jun 25, 2024
1 parent 9de87aa commit 67f3343
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .changeset/slow-buses-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"orchid-graphql": minor
---

Basic type check for `graph.resolve()` query argument. Use orchid-orm 1.13.5 types.
7 changes: 4 additions & 3 deletions packages/orchid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,25 @@
"scripts": {
"dev": "vite-node --watch playground/readme.sample.ts",
"build": "tsup",
"test": "vitest run && tsc --noEmit",
"test": "vitest run && tsc --noEmit && tstyche",
"prepublishOnly": "pnpm build"
},
"dependencies": {
"graphql-parse-resolve-info": "^4.13.0"
},
"peerDependencies": {
"graphql": "^16",
"orchid-orm": "^1.22.0"
"orchid-orm": "^1.31.5"
},
"devDependencies": {
"@apollo/server": "^4.7.1",
"graphql": "^16.6.0",
"graphql-orm": "workspace:*",
"graphql-request": "^6.0.0",
"graphql-tag": "^2.12.6",
"orchid-orm": "^1.30.0",
"orchid-orm": "^1.31.5",
"tsconfig-vite-node": "^1.1.2",
"tstyche": "^2.0.0",
"tsup": "^8.1.0",
"vite": "^5.3.1",
"vite-node": "^1.6.0",
Expand Down
13 changes: 7 additions & 6 deletions packages/orchid/src/orm/orm.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { OrmAdapter } from "graphql-orm"
import type { Query } from "orchid-orm"
import { DbTable, raw } from "orchid-orm"
import type { Query, Table } from "orchid-orm"
import { raw } from "orchid-orm"

export type OrchidOrm = OrmAdapter<
DbTable<any>,
Table,
Query,
// TODO: Type as QueryTransform once it's published.
Pick<Promise<any>, "then" | "catch">
Expand All @@ -17,7 +17,7 @@ export const orm: OrchidOrm = {
// Reflection

get_table_table(table) {
return table.table
return table.table!
},

get_table_relations(table) {
Expand Down Expand Up @@ -47,9 +47,10 @@ export const orm: OrchidOrm = {
},

select_relation(query, { relation, as, modify }) {
// as any casts needed in orchid-orm 1.31+
return query.select({
[as]: (q) => modify((q as any)[relation]),
})
[as]: (q: any) => modify(q[relation]),
} as any)
},

// Find
Expand Down
34 changes: 32 additions & 2 deletions packages/orchid/src/resolvers/graph.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
import { GraphResolver, GraphResolverOptions, TableResolver } from "graphql-orm"
import {
GraphResolveOptions,
GraphResolver,
GraphResolverOptions,
TableResolver,
} from "graphql-orm"
import { Query } from "orchid-orm"

import { OrchidOrm, orm } from "../orm/orm"

export function createGraphResolver<Context = unknown>(
types: Record<string, TableResolver<OrchidOrm, Context>>,
options?: GraphResolverOptions<OrchidOrm, Context>,
) {
return new GraphResolver<OrchidOrm, Context>(orm, types, options)
return new OrchidGraphResolver<Context>(types, options)
}

class OrchidGraphResolver<Context> extends GraphResolver<OrchidOrm, Context> {
constructor(
public readonly types: Record<string, TableResolver<OrchidOrm, Context>>,
public readonly options: GraphResolverOptions<OrchidOrm, Context> = {},
) {
super(orm, types, options)
}

resolve<T>(
query: Query & {
returnType: unknown extends T
? any
: T extends unknown[]
? undefined | "all"
: T extends undefined
? "one"
: "oneOrThrow"
},
options: GraphResolveOptions<Context>,
): Promise<T> {
return super.resolve(query, options)
}
}
8 changes: 4 additions & 4 deletions packages/orchid/src/resolvers/table.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { TableResolver, TableResolverOptions } from "graphql-orm"
import { DbTable, Table } from "orchid-orm"
import { Table } from "orchid-orm"

import { OrchidOrm, orm } from "../orm/orm"

export function defineTableResolver<TableT extends Table, Context = unknown>(
table: DbTable<TableT>,
export function defineTableResolver<Context = unknown>(
table: Table,
options: TableResolverOptions<OrchidOrm, Context> = {},
) {
return new TableResolver(orm, table as unknown as DbTable<any>, options)
return new TableResolver(orm, table as any, options)
}
3 changes: 2 additions & 1 deletion packages/orchid/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
"orchid-graphql": ["./src"],
"graphql-orm": ["../base/src"]
}
}
},
"exclude": ["./typetests"]
}
66 changes: 66 additions & 0 deletions packages/orchid/typetests/main.tst.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as r from "orchid-graphql"
import { createBaseTable, orchidORM } from "orchid-orm"
import { expect, test } from "tstyche"

const BaseTable = createBaseTable()

class PostTable extends BaseTable {
readonly table = "post"

columns = this.setColumns((t) => ({
id: t.serial().primaryKey(),
text: t.text(),
}))
}

const db = orchidORM(
{},
{
post: PostTable,
},
)

// Similar to db.post, but not necessary so.
interface Post {
id: number
text: number
excerpt: string
tags: string[]
}

const graph = r.graph({
Post: r.table(db.post),
})

const ctx = 0 as any

test("valid query type", () => {
expect(graph.resolve(db.post, ctx)).type.toBe<Promise<unknown>>()
expect(graph.resolve<Post[]>(db.post.all(), ctx)).type.toBe<Promise<Post[]>>()
expect(graph.resolve<Post[]>(db.post.where({ text: "foo" }), ctx)).type.toBe<
Promise<Post[]>
>()
expect(graph.resolve<Post>(db.post.find(1), ctx)).type.toBe<Promise<Post>>()
expect(
graph.resolve<Post | undefined>(db.post.findOptional(1), ctx),
).type.toBe<Promise<Post | undefined>>()
})

test("invalid query type", () => {
expect(graph.resolve<Post>(db.post.all(), ctx)).type.toRaiseError()
expect(graph.resolve<Post>(db.post.all(), ctx)).type.toRaiseError()
expect(graph.resolve<Post>(db.post.count(), ctx)).type.toRaiseError()
expect(graph.resolve<Post>(db.post.get("text"), ctx)).type.toRaiseError()
expect(graph.resolve<Post>(db.post.findOptional(1), ctx)).type.toRaiseError()
expect(graph.resolve<Post[]>(db.post.find(1), ctx)).type.toRaiseError()
})

test("query type inference", () => {
;((): Promise<Post> => {
return graph.resolve(db.post.find(1), ctx)
})()
;((): Promise<Post[]> => {
// @ts-expect-error find(1) query should not be allowed for Post[] return type
return graph.resolve(db.post.find(1), ctx)
})()
})
5 changes: 5 additions & 0 deletions packages/orchid/typetests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../tsconfig.json",
"include": ["./"],
"exclude": []
}
48 changes: 32 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 67f3343

Please sign in to comment.