Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Support for deeply nested relation filters + Issue: GraphQL input <Model>WhereUniqueInput shouldn’t include Relation fields #86

Merged
merged 7 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/boilerplate/prisma/postgres.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
email String @unique
name String?
username String?
role Role? @default(USER)
posts Post[]
comments Comment[]
Expand Down Expand Up @@ -49,7 +49,7 @@ enum Category {

model Comment {
id Int @id @default(autoincrement())
text String
message String
author User @relation(fields: [authorId], references: [id])
authorId Int
post Post @relation(fields: [postId], references: [id])
Expand Down
126 changes: 63 additions & 63 deletions packages/generator/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ export class PrismaAppSyncCompiler {
isList: this.isFieldList(field),
isEnum: this.isFieldEnum(field),
isEditable: !this.isFieldGeneratedRelation(field, model),
isUnique: this.isFieldUnique(field, model),
isUnique: this.isFieldUnique(field),
...(field.relationName && {
relation: {
name: this.getFieldRelationName(field, model),
Expand Down Expand Up @@ -685,8 +685,8 @@ export class PrismaAppSyncCompiler {
}

// Return true if field is unique (meaning it can be used for WhereUniqueInputs)
private isFieldUnique(searchField: DMMF.Field, model: DMMF.Model): boolean {
return searchField.isId || searchField.isUnique || this.isFieldGeneratedRelation(searchField, model)
private isFieldUnique(searchField: DMMF.Field): boolean {
return searchField.isId || searchField.isUnique
}

// Return true if field is required
Expand Down Expand Up @@ -748,27 +748,27 @@ export class PrismaAppSyncCompiler {
let parserOpt: prettier.RequiredOptions['parser'] | boolean

switch (extname(outputFilename)) {
case '.ts':
parserOpt = 'typescript'
break
case '.json':
parserOpt = 'json'
break
case '.gql':
parserOpt = 'graphql'
break
case '.md':
parserOpt = 'markdown'
break
case '.yaml':
parserOpt = 'yaml'
break
case '.js':
parserOpt = 'babel'
break
default:
parserOpt = false
break
case '.ts':
parserOpt = 'typescript'
break
case '.json':
parserOpt = 'json'
break
case '.gql':
parserOpt = 'graphql'
break
case '.md':
parserOpt = 'markdown'
break
case '.yaml':
parserOpt = 'yaml'
break
case '.js':
parserOpt = 'babel'
break
default:
parserOpt = false
break
}

// pretiffy output
Expand Down Expand Up @@ -803,20 +803,20 @@ export class PrismaAppSyncCompiler {
// Return field sample for demo/docs
private getFieldSample(field: DMMF.Field): any {
switch (field.type) {
case 'Int':
return '2'
case 'String':
return '"Foo"'
case 'Json':
return { foo: 'bar' }
case 'Float':
return '2.5'
case 'Boolean':
return 'false'
case 'DateTime':
return '"dd/mm/YYYY"'
default:
return field.type
case 'Int':
return '2'
case 'String':
return '"Foo"'
case 'Json':
return { foo: 'bar' }
case 'Float':
return '2.5'
case 'Boolean':
return 'false'
case 'DateTime':
return '"dd/mm/YYYY"'
default:
return field.type
}
}

Expand All @@ -827,7 +827,7 @@ export class PrismaAppSyncCompiler {

// Return relation kind (`one` or `many`) from Prisma type
private getFieldRelationKind(field: DMMF.Field): 'one' | 'many' {
return field.relationFromFields && field.relationFromFields.length === 1 ? 'one' : 'many'
return !field.isList ? 'one' : 'many'
}

// Get AppSync scalar from Prisma type
Expand All @@ -836,34 +836,34 @@ export class PrismaAppSyncCompiler {

if (field.kind === 'scalar' && typeof field.type === 'string') {
switch (field.type.toLocaleLowerCase()) {
case 'int':
type = 'Int'
break
case 'datetime':
type = 'AWSDateTime'
break
case 'json':
type = 'AWSJSON'
break
case 'float':
type = 'Float'
break
case 'boolean':
type = 'Boolean'
break
case 'string':
type = 'String'
break
case 'int':
type = 'Int'
break
case 'datetime':
type = 'AWSDateTime'
break
case 'json':
type = 'AWSJSON'
break
case 'float':
type = 'Float'
break
case 'boolean':
type = 'Boolean'
break
case 'string':
type = 'String'
break
}

if (type === 'String') {
switch (field.name.toLocaleLowerCase()) {
case 'email':
type = 'AWSEmail'
break
case 'url':
type = 'AWSURL'
break
case 'email':
type = 'AWSEmail'
break
case 'url':
type = 'AWSURL'
break
}
}
}
Expand Down
56 changes: 4 additions & 52 deletions packages/generator/templates/schema.gql.njk
Original file line number Diff line number Diff line change
Expand Up @@ -52,57 +52,9 @@ enum OrderByArg {
{% endif %}

input {{ model.name }}Filter {
some: {{ model.name }}ScalarWhereInput
every: {{ model.name }}ScalarWhereInput
none: {{ model.name }}ScalarWhereInput
}

input {{ model.name }}RelationFilter {
{% for field in model.fields -%}
{% if not field.relation -%}
{% if field.isList -%}
{% if field.isEnum -%}
{{ field.name }}: {{ field.scalar }}EnumListFilter
{% elseif field.scalar in ["Int", "Float", "AWSDateTime", "AWSJSON", "AWSEmail", "AWSURL", "Boolean"] -%}
{{ field.name }}: {{ field.scalar }}ListFilter
{% else -%}
{{ field.name }}: StringListFilter
{% endif -%}
{% else -%}
{% if field.isEnum -%}
{{ field.name }}: {{ field.scalar }}EnumFilter
{% elseif field.scalar in ["Int", "Float", "AWSDateTime", "AWSJSON", "AWSEmail", "AWSURL", "Boolean"] -%}
{{ field.name }}: {{ field.scalar }}Filter
{% else -%}
{{ field.name }}: StringFilter
{% endif -%}
{% endif -%}
{% endif -%}
{% endfor %}
}

input {{ model.name }}ScalarWhereInput {
{% for field in model.fields -%}
{% if not field.relation -%}
{% if field.isList -%}
{% if field.isEnum -%}
{{ field.name }}: {{ field.scalar }}EnumListFilter
{% elseif field.scalar in ["Int", "Float", "AWSDateTime", "AWSJSON", "AWSEmail", "AWSURL", "Boolean"] -%}
{{ field.name }}: {{ field.scalar }}ListFilter
{% else -%}
{{ field.name }}: StringListFilter
{% endif -%}
{% else -%}
{% if field.isEnum -%}
{{ field.name }}: {{ field.scalar }}EnumFilter
{% elseif field.scalar in ["Int", "Float", "AWSDateTime", "AWSJSON", "AWSEmail", "AWSURL", "Boolean"] -%}
{{ field.name }}: {{ field.scalar }}Filter
{% else -%}
{{ field.name }}: StringFilter
{% endif -%}
{% endif -%}
{% endif -%}
{% endfor %}
some: {{ model.name }}WhereInput
every: {{ model.name }}WhereInput
none: {{ model.name }}WhereInput
}

input {{ model.name }}WhereInput {
Expand Down Expand Up @@ -130,7 +82,7 @@ enum OrderByArg {
{% endif -%}
{% else -%}
{% if field.relation.kind === "one" -%}
{{ field.name }}: {{ field.relation.type }}RelationFilter
{{ field.name }}: {{ field.relation.type }}WhereInput
{% else -%}
{{ field.name }}: {{ field.relation.type }}Filter
{% endif -%}
Expand Down
90 changes: 88 additions & 2 deletions tests/generator/prisma-to-graphql.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ describe('GENERATOR #gql', () => {
`
tester.test(true, query)
})
test('expect "relation to one filter" query to be valid', async () => {
test('expect "relation to-one filter" query to be valid', async () => {
const query = `
query {
listComments(
Expand All @@ -136,7 +136,7 @@ describe('GENERATOR #gql', () => {
`
tester.test(true, query)
})
test('expect "relation to many filter" query to be valid', async () => {
test('expect "relation to-many filter" query to be valid', async () => {
const query = `
query {
listUsers(
Expand All @@ -158,6 +158,92 @@ describe('GENERATOR #gql', () => {
`
tester.test(true, query)
})
test('expect "deeply nested relation to-one-to-many filter" query to be valid', async () => {
const query = `
query {
listComments(
where: {
author: {
posts: {
every: {
published: { equals: true }
}
}
}
}
) {
message
}
}
`
tester.test(true, query)
})
test('expect "deeply nested relation to-many-to-many filter" query to be valid', async () => {
const query = `
query {
listUsers(
where: {
posts: {
every: {
comments: {
every: {
message: { startsWith: "hello" }
}
}
}
}
}
) {
username
email
role
}
}
`
tester.test(true, query)
})
test('expect "deeply nested relation to-one-to-one filter" query to be valid', async () => {
const query = `
query {
listComments(
where: {
author: {
profile: {
bio: { contains: "hello" }
}
}
}
) {
message
}
}
`
tester.test(true, query)
})
test('expect "deeply nested relation to-many-to-one filter" query to be valid', async () => {
const query = `
query {
listUsers(
where: {
posts: {
every: {
author: {
username: {
equals: "username"
}
}
}
}
}
) {
username
email
role
}
}
`
tester.test(true, query)
})
test('expect "custom resolver" query to be valid', async () => {
const query = `
mutation ($message: String!) {
Expand Down
8 changes: 8 additions & 0 deletions tests/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,17 @@ model User {
hiddenField String?
role Role? @default(USER)
posts Post[]
profile Profile?
comments Comment[]
}

model Profile {
uuid String @id @default(uuid()) @db.VarChar(200)
owner User? @relation(fields: [ownerUuid], references: [uuid])
ownerUuid String? @unique @db.VarChar(200)
bio String?
}

/// @gql(model: null)
model hiddenModel {
id Int @id @default(autoincrement())
Expand Down