Custom Admin UI Logo New
+
+ Custom Admin UI Pages New
+
Access Control
diff --git a/docs/pages/docs/guides/custom-admin-ui-pages.mdx b/docs/pages/docs/guides/custom-admin-ui-pages.mdx
new file mode 100644
index 00000000000..8357a5656d6
--- /dev/null
+++ b/docs/pages/docs/guides/custom-admin-ui-pages.mdx
@@ -0,0 +1,45 @@
+import { ComingSoon } from '../../../components/docs/ComingSoon';
+import { Markdown } from '../../../components/Markdown';
+import { Alert } from '../../../components/primitives/Alert';
+
+# Custom Admin UI Pages
+
+In this guide we'll show you how to add custom pages to the Keystone Admin UI.
+As the Admin UI is built on top of Next.js, it exposes the same pages directory for adding custom pages.
+
+To create a custom page, ensure that the `admin/pages` directory exists in the root of your Keystone Project.
+Much like with Next.js, all files in this directory will be added as routes to the Admin UI.
+The default export of every file in this directory is expected to be a valid React Component rendered out as the contents of the route.
+
+```tsx
+// admin/pages/MyCustomPage.tsx
+export default function () {
+ return (
+ This is a custom Admin UI Page
+ It can be accessed via the route '/MyCustomPage'
+ )
+}
+```
+
+If you have styling constraints, we recommend using the jsx export from the `@keystone-ui/core` package, as this will ensure that the version of emotion you're using conforms with the version of emotion used internally within Keystone.
+
+```tsx
+// admin/pages/MyCustomPage.tsx
+/** @jsxRuntime classic */
+/** @jsx jsx */
+import { jsx } from '@keystone-ui/core';
+export default function () {
+ return (
+ This is a custom Admin UI Page
+ It can be accessed via the route '/MyCustomPage'
+ )
+}
+```
+
+Of course this is purely a recommendation, if you would prefer to roll your own css-in-js solution in with your custom component please feel free to! Although this may require additional configuration outside of the scope of this guide.
+
+x> **Not all Next.js exports are available:** Keystone **only** supports the page component as a default export in the pages directory. This means that unlike with Next, auxillary exports such as `getStaticProps` and `getServerProps` are not supported.
+
+export default ({ children }) => {children};
diff --git a/examples-staging/assets-cloud/schema.graphql b/examples-staging/assets-cloud/schema.graphql
index db4ef456b16..7ef44fff5d7 100644
--- a/examples-staging/assets-cloud/schema.graphql
+++ b/examples-staging/assets-cloud/schema.graphql
@@ -255,14 +255,14 @@ type Mutation {
createPosts(data: [PostsCreateInput]): [Post]
updatePost(id: ID!, data: PostUpdateInput): Post
updatePosts(data: [PostUpdateArgs]): [Post]
- deletePost(id: ID!): Post
- deletePosts(ids: [ID!]): [Post]
+ deletePost(where: PostWhereUniqueInput!): Post
+ deletePosts(where: [PostWhereUniqueInput!]!): [Post]
createAuthor(data: AuthorCreateInput): Author
createAuthors(data: [AuthorsCreateInput]): [Author]
updateAuthor(id: ID!, data: AuthorUpdateInput): Author
updateAuthors(data: [AuthorUpdateArgs]): [Author]
- deleteAuthor(id: ID!): Author
- deleteAuthors(ids: [ID!]): [Author]
+ deleteAuthor(where: AuthorWhereUniqueInput!): Author
+ deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author]
}
type Query {
diff --git a/examples-staging/assets-local/schema.graphql b/examples-staging/assets-local/schema.graphql
index 1f17857bb60..0aca326a079 100644
--- a/examples-staging/assets-local/schema.graphql
+++ b/examples-staging/assets-local/schema.graphql
@@ -233,14 +233,14 @@ type Mutation {
createPosts(data: [PostsCreateInput]): [Post]
updatePost(id: ID!, data: PostUpdateInput): Post
updatePosts(data: [PostUpdateArgs]): [Post]
- deletePost(id: ID!): Post
- deletePosts(ids: [ID!]): [Post]
+ deletePost(where: PostWhereUniqueInput!): Post
+ deletePosts(where: [PostWhereUniqueInput!]!): [Post]
createAuthor(data: AuthorCreateInput): Author
createAuthors(data: [AuthorsCreateInput]): [Author]
updateAuthor(id: ID!, data: AuthorUpdateInput): Author
updateAuthors(data: [AuthorUpdateArgs]): [Author]
- deleteAuthor(id: ID!): Author
- deleteAuthors(ids: [ID!]): [Author]
+ deleteAuthor(where: AuthorWhereUniqueInput!): Author
+ deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author]
}
type Query {
diff --git a/examples-staging/auth/schema.graphql b/examples-staging/auth/schema.graphql
index 27a91b43f07..8737df31952 100644
--- a/examples-staging/auth/schema.graphql
+++ b/examples-staging/auth/schema.graphql
@@ -91,8 +91,8 @@ type Mutation {
createUsers(data: [UsersCreateInput]): [User]
updateUser(id: ID!, data: UserUpdateInput): User
updateUsers(data: [UserUpdateArgs]): [User]
- deleteUser(id: ID!): User
- deleteUsers(ids: [ID!]): [User]
+ deleteUser(where: UserWhereUniqueInput!): User
+ deleteUsers(where: [UserWhereUniqueInput!]!): [User]
authenticateUserWithPassword(
email: String!
password: String!
diff --git a/examples-staging/basic/schema.graphql b/examples-staging/basic/schema.graphql
index fd95da5072b..f10a7eaf32c 100644
--- a/examples-staging/basic/schema.graphql
+++ b/examples-staging/basic/schema.graphql
@@ -355,20 +355,20 @@ type Mutation {
createUsers(data: [UsersCreateInput]): [User]
updateUser(id: ID!, data: UserUpdateInput): User
updateUsers(data: [UserUpdateArgs]): [User]
- deleteUser(id: ID!): User
- deleteUsers(ids: [ID!]): [User]
+ deleteUser(where: UserWhereUniqueInput!): User
+ deleteUsers(where: [UserWhereUniqueInput!]!): [User]
createPhoneNumber(data: PhoneNumberCreateInput): PhoneNumber
createPhoneNumbers(data: [PhoneNumbersCreateInput]): [PhoneNumber]
updatePhoneNumber(id: ID!, data: PhoneNumberUpdateInput): PhoneNumber
updatePhoneNumbers(data: [PhoneNumberUpdateArgs]): [PhoneNumber]
- deletePhoneNumber(id: ID!): PhoneNumber
- deletePhoneNumbers(ids: [ID!]): [PhoneNumber]
+ deletePhoneNumber(where: PhoneNumberWhereUniqueInput!): PhoneNumber
+ deletePhoneNumbers(where: [PhoneNumberWhereUniqueInput!]!): [PhoneNumber]
createPost(data: PostCreateInput): Post
createPosts(data: [PostsCreateInput]): [Post]
updatePost(id: ID!, data: PostUpdateInput): Post
updatePosts(data: [PostUpdateArgs]): [Post]
- deletePost(id: ID!): Post
- deletePosts(ids: [ID!]): [Post]
+ deletePost(where: PostWhereUniqueInput!): Post
+ deletePosts(where: [PostWhereUniqueInput!]!): [Post]
authenticateUserWithPassword(
email: String!
password: String!
diff --git a/examples-staging/ecommerce/mutations/checkout.ts b/examples-staging/ecommerce/mutations/checkout.ts
index 09cd142d597..78645966d40 100644
--- a/examples-staging/ecommerce/mutations/checkout.ts
+++ b/examples-staging/ecommerce/mutations/checkout.ts
@@ -88,7 +88,7 @@ async function checkout(root: any, { token }: Arguments, context: KeystoneContex
const cartItemIds = user.cart.map((cartItem: any) => cartItem.id);
console.log('gonna create delete cartItems');
await context.lists.CartItem.deleteMany({
- ids: cartItemIds,
+ where: cartItemIds.map((id: string) => ({ id })),
});
return order;
}
diff --git a/examples-staging/ecommerce/schema.graphql b/examples-staging/ecommerce/schema.graphql
index f599403b371..384e512eb27 100644
--- a/examples-staging/ecommerce/schema.graphql
+++ b/examples-staging/ecommerce/schema.graphql
@@ -764,44 +764,44 @@ type Mutation {
createUsers(data: [UsersCreateInput]): [User]
updateUser(id: ID!, data: UserUpdateInput): User
updateUsers(data: [UserUpdateArgs]): [User]
- deleteUser(id: ID!): User
- deleteUsers(ids: [ID!]): [User]
+ deleteUser(where: UserWhereUniqueInput!): User
+ deleteUsers(where: [UserWhereUniqueInput!]!): [User]
createProduct(data: ProductCreateInput): Product
createProducts(data: [ProductsCreateInput]): [Product]
updateProduct(id: ID!, data: ProductUpdateInput): Product
updateProducts(data: [ProductUpdateArgs]): [Product]
- deleteProduct(id: ID!): Product
- deleteProducts(ids: [ID!]): [Product]
+ deleteProduct(where: ProductWhereUniqueInput!): Product
+ deleteProducts(where: [ProductWhereUniqueInput!]!): [Product]
createProductImage(data: ProductImageCreateInput): ProductImage
createProductImages(data: [ProductImagesCreateInput]): [ProductImage]
updateProductImage(id: ID!, data: ProductImageUpdateInput): ProductImage
updateProductImages(data: [ProductImageUpdateArgs]): [ProductImage]
- deleteProductImage(id: ID!): ProductImage
- deleteProductImages(ids: [ID!]): [ProductImage]
+ deleteProductImage(where: ProductImageWhereUniqueInput!): ProductImage
+ deleteProductImages(where: [ProductImageWhereUniqueInput!]!): [ProductImage]
createCartItem(data: CartItemCreateInput): CartItem
createCartItems(data: [CartItemsCreateInput]): [CartItem]
updateCartItem(id: ID!, data: CartItemUpdateInput): CartItem
updateCartItems(data: [CartItemUpdateArgs]): [CartItem]
- deleteCartItem(id: ID!): CartItem
- deleteCartItems(ids: [ID!]): [CartItem]
+ deleteCartItem(where: CartItemWhereUniqueInput!): CartItem
+ deleteCartItems(where: [CartItemWhereUniqueInput!]!): [CartItem]
createOrderItem(data: OrderItemCreateInput): OrderItem
createOrderItems(data: [OrderItemsCreateInput]): [OrderItem]
updateOrderItem(id: ID!, data: OrderItemUpdateInput): OrderItem
updateOrderItems(data: [OrderItemUpdateArgs]): [OrderItem]
- deleteOrderItem(id: ID!): OrderItem
- deleteOrderItems(ids: [ID!]): [OrderItem]
+ deleteOrderItem(where: OrderItemWhereUniqueInput!): OrderItem
+ deleteOrderItems(where: [OrderItemWhereUniqueInput!]!): [OrderItem]
createOrder(data: OrderCreateInput): Order
createOrders(data: [OrdersCreateInput]): [Order]
updateOrder(id: ID!, data: OrderUpdateInput): Order
updateOrders(data: [OrderUpdateArgs]): [Order]
- deleteOrder(id: ID!): Order
- deleteOrders(ids: [ID!]): [Order]
+ deleteOrder(where: OrderWhereUniqueInput!): Order
+ deleteOrders(where: [OrderWhereUniqueInput!]!): [Order]
createRole(data: RoleCreateInput): Role
createRoles(data: [RolesCreateInput]): [Role]
updateRole(id: ID!, data: RoleUpdateInput): Role
updateRoles(data: [RoleUpdateArgs]): [Role]
- deleteRole(id: ID!): Role
- deleteRoles(ids: [ID!]): [Role]
+ deleteRole(where: RoleWhereUniqueInput!): Role
+ deleteRoles(where: [RoleWhereUniqueInput!]!): [Role]
authenticateUserWithPassword(
email: String!
password: String!
diff --git a/examples-staging/embedded-nextjs/schema.graphql b/examples-staging/embedded-nextjs/schema.graphql
index 20a2063b387..3c4655fed16 100644
--- a/examples-staging/embedded-nextjs/schema.graphql
+++ b/examples-staging/embedded-nextjs/schema.graphql
@@ -86,8 +86,8 @@ type Mutation {
createPosts(data: [PostsCreateInput]): [Post]
updatePost(id: ID!, data: PostUpdateInput): Post
updatePosts(data: [PostUpdateArgs]): [Post]
- deletePost(id: ID!): Post
- deletePosts(ids: [ID!]): [Post]
+ deletePost(where: PostWhereUniqueInput!): Post
+ deletePosts(where: [PostWhereUniqueInput!]!): [Post]
}
type Query {
diff --git a/examples-staging/graphql-api-endpoint/schema.graphql b/examples-staging/graphql-api-endpoint/schema.graphql
index 095423692b2..13bc9dd812d 100644
--- a/examples-staging/graphql-api-endpoint/schema.graphql
+++ b/examples-staging/graphql-api-endpoint/schema.graphql
@@ -322,20 +322,20 @@ type Mutation {
createUsers(data: [UsersCreateInput]): [User]
updateUser(id: ID!, data: UserUpdateInput): User
updateUsers(data: [UserUpdateArgs]): [User]
- deleteUser(id: ID!): User
- deleteUsers(ids: [ID!]): [User]
+ deleteUser(where: UserWhereUniqueInput!): User
+ deleteUsers(where: [UserWhereUniqueInput!]!): [User]
createPost(data: PostCreateInput): Post
createPosts(data: [PostsCreateInput]): [Post]
updatePost(id: ID!, data: PostUpdateInput): Post
updatePosts(data: [PostUpdateArgs]): [Post]
- deletePost(id: ID!): Post
- deletePosts(ids: [ID!]): [Post]
+ deletePost(where: PostWhereUniqueInput!): Post
+ deletePosts(where: [PostWhereUniqueInput!]!): [Post]
createTag(data: TagCreateInput): Tag
createTags(data: [TagsCreateInput]): [Tag]
updateTag(id: ID!, data: TagUpdateInput): Tag
updateTags(data: [TagUpdateArgs]): [Tag]
- deleteTag(id: ID!): Tag
- deleteTags(ids: [ID!]): [Tag]
+ deleteTag(where: TagWhereUniqueInput!): Tag
+ deleteTags(where: [TagWhereUniqueInput!]!): [Tag]
authenticateUserWithPassword(
email: String!
password: String!
diff --git a/examples-staging/playground/schema.graphql b/examples-staging/playground/schema.graphql
index 987ce514026..2a19209f604 100644
--- a/examples-staging/playground/schema.graphql
+++ b/examples-staging/playground/schema.graphql
@@ -66,8 +66,8 @@ type Mutation {
createNotes(data: [NotesCreateInput]): [Note]
updateNote(id: ID!, data: NoteUpdateInput): Note
updateNotes(data: [NoteUpdateArgs]): [Note]
- deleteNote(id: ID!): Note
- deleteNotes(ids: [ID!]): [Note]
+ deleteNote(where: NoteWhereUniqueInput!): Note
+ deleteNotes(where: [NoteWhereUniqueInput!]!): [Note]
}
type Query {
diff --git a/examples-staging/roles/schema.graphql b/examples-staging/roles/schema.graphql
index b6fab947220..1ffe8df5daa 100644
--- a/examples-staging/roles/schema.graphql
+++ b/examples-staging/roles/schema.graphql
@@ -295,20 +295,20 @@ type Mutation {
createTodos(data: [TodosCreateInput]): [Todo]
updateTodo(id: ID!, data: TodoUpdateInput): Todo
updateTodos(data: [TodoUpdateArgs]): [Todo]
- deleteTodo(id: ID!): Todo
- deleteTodos(ids: [ID!]): [Todo]
+ deleteTodo(where: TodoWhereUniqueInput!): Todo
+ deleteTodos(where: [TodoWhereUniqueInput!]!): [Todo]
createPerson(data: PersonCreateInput): Person
createPeople(data: [PeopleCreateInput]): [Person]
updatePerson(id: ID!, data: PersonUpdateInput): Person
updatePeople(data: [PersonUpdateArgs]): [Person]
- deletePerson(id: ID!): Person
- deletePeople(ids: [ID!]): [Person]
+ deletePerson(where: PersonWhereUniqueInput!): Person
+ deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
createRole(data: RoleCreateInput): Role
createRoles(data: [RolesCreateInput]): [Role]
updateRole(id: ID!, data: RoleUpdateInput): Role
updateRoles(data: [RoleUpdateArgs]): [Role]
- deleteRole(id: ID!): Role
- deleteRoles(ids: [ID!]): [Role]
+ deleteRole(where: RoleWhereUniqueInput!): Role
+ deleteRoles(where: [RoleWhereUniqueInput!]!): [Role]
authenticatePersonWithPassword(
email: String!
password: String!
diff --git a/examples-staging/sandbox/schema.graphql b/examples-staging/sandbox/schema.graphql
index 2cfd8a8081c..f7f6030e250 100644
--- a/examples-staging/sandbox/schema.graphql
+++ b/examples-staging/sandbox/schema.graphql
@@ -223,14 +223,14 @@ type Mutation {
createTodos(data: [TodosCreateInput]): [Todo]
updateTodo(id: ID!, data: TodoUpdateInput): Todo
updateTodos(data: [TodoUpdateArgs]): [Todo]
- deleteTodo(id: ID!): Todo
- deleteTodos(ids: [ID!]): [Todo]
+ deleteTodo(where: TodoWhereUniqueInput!): Todo
+ deleteTodos(where: [TodoWhereUniqueInput!]!): [Todo]
createUser(data: UserCreateInput): User
createUsers(data: [UsersCreateInput]): [User]
updateUser(id: ID!, data: UserUpdateInput): User
updateUsers(data: [UserUpdateArgs]): [User]
- deleteUser(id: ID!): User
- deleteUsers(ids: [ID!]): [User]
+ deleteUser(where: UserWhereUniqueInput!): User
+ deleteUsers(where: [UserWhereUniqueInput!]!): [User]
}
type Query {
diff --git a/examples/README.md b/examples/README.md
index f705970cadf..e83361784cb 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -26,7 +26,8 @@ Each project below demonstrates a Keystone feature you can learn about and exper
- [Testing](./testing): Adds tests with `@keystone-next/testing` to the `withAuth()` example.
- [Custom field](./custom-field): Adds a custom `stars` field to the Blog base.
- [Custom field view](./custom-field-view): Adds a custom Admin UI view to a `json` field to the Task Manager base.
-- [Custom Admin UI components](./custom-admin-ui-logo): Adds a custom logo in the Admin UI to the Task Manager base.
+- [Custom Admin UI logo](./custom-admin-ui-logo): Adds a custom logo in the Admin UI to the Task Manager base.
+- [Custom Admin UI pages](./custom-admin-ui-pages): Adds a custom page in the Admin UI to the Task Manager base.
## Running examples
diff --git a/examples/blog/schema.graphql b/examples/blog/schema.graphql
index e89ebea716b..5ddc9da6ad0 100644
--- a/examples/blog/schema.graphql
+++ b/examples/blog/schema.graphql
@@ -193,14 +193,14 @@ type Mutation {
createPosts(data: [PostsCreateInput]): [Post]
updatePost(id: ID!, data: PostUpdateInput): Post
updatePosts(data: [PostUpdateArgs]): [Post]
- deletePost(id: ID!): Post
- deletePosts(ids: [ID!]): [Post]
+ deletePost(where: PostWhereUniqueInput!): Post
+ deletePosts(where: [PostWhereUniqueInput!]!): [Post]
createAuthor(data: AuthorCreateInput): Author
createAuthors(data: [AuthorsCreateInput]): [Author]
updateAuthor(id: ID!, data: AuthorUpdateInput): Author
updateAuthors(data: [AuthorUpdateArgs]): [Author]
- deleteAuthor(id: ID!): Author
- deleteAuthors(ids: [ID!]): [Author]
+ deleteAuthor(where: AuthorWhereUniqueInput!): Author
+ deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author]
}
type Query {
diff --git a/examples/custom-admin-ui-logo/schema.graphql b/examples/custom-admin-ui-logo/schema.graphql
index 55380b7c1d2..30beff9bc69 100644
--- a/examples/custom-admin-ui-logo/schema.graphql
+++ b/examples/custom-admin-ui-logo/schema.graphql
@@ -179,14 +179,14 @@ type Mutation {
createTasks(data: [TasksCreateInput]): [Task]
updateTask(id: ID!, data: TaskUpdateInput): Task
updateTasks(data: [TaskUpdateArgs]): [Task]
- deleteTask(id: ID!): Task
- deleteTasks(ids: [ID!]): [Task]
+ deleteTask(where: TaskWhereUniqueInput!): Task
+ deleteTasks(where: [TaskWhereUniqueInput!]!): [Task]
createPerson(data: PersonCreateInput): Person
createPeople(data: [PeopleCreateInput]): [Person]
updatePerson(id: ID!, data: PersonUpdateInput): Person
updatePeople(data: [PersonUpdateArgs]): [Person]
- deletePerson(id: ID!): Person
- deletePeople(ids: [ID!]): [Person]
+ deletePerson(where: PersonWhereUniqueInput!): Person
+ deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
}
type Query {
diff --git a/examples/custom-admin-ui-pages/CHANGELOG.md b/examples/custom-admin-ui-pages/CHANGELOG.md
new file mode 100644
index 00000000000..88b4c31f841
--- /dev/null
+++ b/examples/custom-admin-ui-pages/CHANGELOG.md
@@ -0,0 +1 @@
+# @keystone-next/example-custom-admin-ui-pages
diff --git a/examples/custom-admin-ui-pages/README.md b/examples/custom-admin-ui-pages/README.md
new file mode 100644
index 00000000000..37622aac6b1
--- /dev/null
+++ b/examples/custom-admin-ui-pages/README.md
@@ -0,0 +1,28 @@
+## Feature Example - Custom Components for the Admin UI
+
+This project demonstrates how to create a custom page in the Admin UI.
+It builds on the [Task Manager](../task-manager) starter project.
+
+## Instructions
+
+To run this project, clone the Keystone repository locally then navigate to this directory and run:
+
+```shell
+yarn dev
+```
+
+This will start the Admin UI at [localhost:3000](http://localhost:3000).
+
+You can use the Admin UI to create items in your database.
+
+You can also access a GraphQL Playground at [localhost:3000/api/graphql](http://localhost:3000/api/graphql), which allows you to directly run GraphQL queries and mutations.
+
+🚀 Congratulations, you're now up and running with Keystone!
+
+## admin/pages
+
+This project leverages the `/admin/pages` directory. As elaborated on in the [Custom Pages](https://keystonejs.com/docs/guides/custom-admin-ui-pages) guide, this directory is used to generate additional routes in the Admin UI, a behaviour inherited from `Next.js`. The default export of files in this directory are expected to be **React Components**.
+**All other exports are ignored**
+
+**NOTE** The Keystone monorepo leverages a babel config that means we use the old jsx transform (this doesn't have an impact on the code we ship to npm).
+This is why there are `import React from 'react'` statements in our examples, this is NOT necessary outside of the Keystone repo (unless you have a babel config with the old jsx transform which is currently the default with @babel/preset-react) as you'll be using Next's babel config which uses the new jsx transform.
diff --git a/examples/custom-admin-ui-pages/admin/pages/custom-page.tsx b/examples/custom-admin-ui-pages/admin/pages/custom-page.tsx
new file mode 100644
index 00000000000..f109f136375
--- /dev/null
+++ b/examples/custom-admin-ui-pages/admin/pages/custom-page.tsx
@@ -0,0 +1,34 @@
+/** @jsx jsx */
+import { Fragment } from 'react';
+import { jsx } from '@keystone-ui/core';
+// Please note that while this capability is driven by Next.js's pages directory
+// We do not currently support any of the auxillary methods that Next.js provides i.e. `getStaticProps`
+// Presently the only export from the directory that is supported is the page component itself.
+export default function CustomPage() {
+ return (
+
+
+
+ Hello this is a custom page
+
+
+ This is a custom page added to the Admin UI, leveraging @keystone-ui/core
+
+
+
+ );
+}
diff --git a/examples/custom-admin-ui-pages/keystone.ts b/examples/custom-admin-ui-pages/keystone.ts
new file mode 100644
index 00000000000..e961168f85e
--- /dev/null
+++ b/examples/custom-admin-ui-pages/keystone.ts
@@ -0,0 +1,10 @@
+import { config } from '@keystone-next/keystone/schema';
+import { lists } from './schema';
+
+export default config({
+ db: {
+ provider: 'sqlite',
+ url: process.env.DATABASE_URL || 'file:./keystone-example.db',
+ },
+ lists,
+});
diff --git a/examples/custom-admin-ui-pages/package.json b/examples/custom-admin-ui-pages/package.json
new file mode 100644
index 00000000000..574a7a771e8
--- /dev/null
+++ b/examples/custom-admin-ui-pages/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "@keystone-next/example-custom-admin-ui-pages",
+ "version": "0.0.1",
+ "private": true,
+ "license": "MIT",
+ "scripts": {
+ "dev": "keystone-next dev",
+ "start": "keystone-next start",
+ "build": "keystone-next build"
+ },
+ "dependencies": {
+ "@keystone-next/fields": "^13.0.0",
+ "@keystone-next/keystone": "^23.0.0",
+ "@keystone-next/types": "^23.0.0",
+ "@keystone-ui/core": "^3.1.0",
+ "next": "^10.2.3",
+ "react": "^17.0.2"
+ },
+ "devDependencies": {
+ "typescript": "^4.3.5"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ },
+ "repository": "https://github.com/keystonejs/keystone/tree/master/examples/custom-admin-ui-pages"
+}
diff --git a/examples/custom-admin-ui-pages/schema.graphql b/examples/custom-admin-ui-pages/schema.graphql
new file mode 100644
index 00000000000..30beff9bc69
--- /dev/null
+++ b/examples/custom-admin-ui-pages/schema.graphql
@@ -0,0 +1,296 @@
+type Task {
+ id: ID!
+ label: String
+ priority: TaskPriorityType
+ isComplete: Boolean
+ assignedTo: Person
+ finishBy: String
+}
+
+enum TaskPriorityType {
+ low
+ medium
+ high
+}
+
+input TaskWhereInput {
+ AND: [TaskWhereInput!]
+ OR: [TaskWhereInput!]
+ id: ID
+ id_not: ID
+ id_lt: ID
+ id_lte: ID
+ id_gt: ID
+ id_gte: ID
+ id_in: [ID!]
+ id_not_in: [ID!]
+ label: String
+ label_not: String
+ label_contains: String
+ label_not_contains: String
+ label_in: [String]
+ label_not_in: [String]
+ priority: TaskPriorityType
+ priority_not: TaskPriorityType
+ priority_in: [TaskPriorityType]
+ priority_not_in: [TaskPriorityType]
+ isComplete: Boolean
+ isComplete_not: Boolean
+ assignedTo: PersonWhereInput
+ assignedTo_is_null: Boolean
+ finishBy: String
+ finishBy_not: String
+ finishBy_lt: String
+ finishBy_lte: String
+ finishBy_gt: String
+ finishBy_gte: String
+ finishBy_in: [String]
+ finishBy_not_in: [String]
+}
+
+input TaskWhereUniqueInput {
+ id: ID
+}
+
+input TaskOrderByInput {
+ id: OrderDirection
+ label: OrderDirection
+ priority: OrderDirection
+ isComplete: OrderDirection
+ finishBy: OrderDirection
+}
+
+enum OrderDirection {
+ asc
+ desc
+}
+
+input TaskUpdateInput {
+ label: String
+ priority: TaskPriorityType
+ isComplete: Boolean
+ assignedTo: PersonRelateToOneInput
+ finishBy: String
+}
+
+input PersonRelateToOneInput {
+ create: PersonCreateInput
+ connect: PersonWhereUniqueInput
+ disconnect: PersonWhereUniqueInput
+ disconnectAll: Boolean
+}
+
+input TaskUpdateArgs {
+ id: ID!
+ data: TaskUpdateInput
+}
+
+input TaskCreateInput {
+ label: String
+ priority: TaskPriorityType
+ isComplete: Boolean
+ assignedTo: PersonRelateToOneInput
+ finishBy: String
+}
+
+input TasksCreateInput {
+ data: TaskCreateInput
+}
+
+type Person {
+ id: ID!
+ name: String
+ tasks(
+ where: TaskWhereInput! = {}
+ orderBy: [TaskOrderByInput!]! = []
+ first: Int
+ skip: Int! = 0
+ ): [Task!]
+ tasksCount(where: TaskWhereInput! = {}): Int
+}
+
+input PersonWhereInput {
+ AND: [PersonWhereInput!]
+ OR: [PersonWhereInput!]
+ id: ID
+ id_not: ID
+ id_lt: ID
+ id_lte: ID
+ id_gt: ID
+ id_gte: ID
+ id_in: [ID!]
+ id_not_in: [ID!]
+ name: String
+ name_not: String
+ name_contains: String
+ name_not_contains: String
+ name_in: [String]
+ name_not_in: [String]
+ tasks_every: TaskWhereInput
+ tasks_some: TaskWhereInput
+ tasks_none: TaskWhereInput
+}
+
+input PersonWhereUniqueInput {
+ id: ID
+}
+
+input PersonOrderByInput {
+ id: OrderDirection
+ name: OrderDirection
+}
+
+input PersonUpdateInput {
+ name: String
+ tasks: TaskRelateToManyInput
+}
+
+input TaskRelateToManyInput {
+ create: [TaskCreateInput]
+ connect: [TaskWhereUniqueInput]
+ disconnect: [TaskWhereUniqueInput]
+ disconnectAll: Boolean
+}
+
+input PersonUpdateArgs {
+ id: ID!
+ data: PersonUpdateInput
+}
+
+input PersonCreateInput {
+ name: String
+ tasks: TaskRelateToManyInput
+}
+
+input PeopleCreateInput {
+ data: PersonCreateInput
+}
+
+"""
+The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
+"""
+scalar JSON
+ @specifiedBy(
+ url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf"
+ )
+
+type Mutation {
+ createTask(data: TaskCreateInput): Task
+ createTasks(data: [TasksCreateInput]): [Task]
+ updateTask(id: ID!, data: TaskUpdateInput): Task
+ updateTasks(data: [TaskUpdateArgs]): [Task]
+ deleteTask(where: TaskWhereUniqueInput!): Task
+ deleteTasks(where: [TaskWhereUniqueInput!]!): [Task]
+ createPerson(data: PersonCreateInput): Person
+ createPeople(data: [PeopleCreateInput]): [Person]
+ updatePerson(id: ID!, data: PersonUpdateInput): Person
+ updatePeople(data: [PersonUpdateArgs]): [Person]
+ deletePerson(where: PersonWhereUniqueInput!): Person
+ deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
+}
+
+type Query {
+ tasks(
+ where: TaskWhereInput! = {}
+ orderBy: [TaskOrderByInput!]! = []
+ first: Int
+ skip: Int! = 0
+ ): [Task!]
+ task(where: TaskWhereUniqueInput!): Task
+ tasksCount(where: TaskWhereInput! = {}): Int
+ people(
+ where: PersonWhereInput! = {}
+ orderBy: [PersonOrderByInput!]! = []
+ first: Int
+ skip: Int! = 0
+ ): [Person!]
+ person(where: PersonWhereUniqueInput!): Person
+ peopleCount(where: PersonWhereInput! = {}): Int
+ keystone: KeystoneMeta!
+}
+
+type KeystoneMeta {
+ adminMeta: KeystoneAdminMeta!
+}
+
+type KeystoneAdminMeta {
+ enableSignout: Boolean!
+ enableSessionItem: Boolean!
+ lists: [KeystoneAdminUIListMeta!]!
+ list(key: String!): KeystoneAdminUIListMeta
+}
+
+type KeystoneAdminUIListMeta {
+ key: String!
+ itemQueryName: String!
+ listQueryName: String!
+ hideCreate: Boolean!
+ hideDelete: Boolean!
+ path: String!
+ label: String!
+ singular: String!
+ plural: String!
+ description: String
+ initialColumns: [String!]!
+ pageSize: Int!
+ labelField: String!
+ fields: [KeystoneAdminUIFieldMeta!]!
+ initialSort: KeystoneAdminUISort
+ isHidden: Boolean!
+}
+
+type KeystoneAdminUIFieldMeta {
+ path: String!
+ label: String!
+ isOrderable: Boolean!
+ fieldMeta: JSON
+ viewsIndex: Int!
+ customViewsIndex: Int
+ createView: KeystoneAdminUIFieldMetaCreateView!
+ listView: KeystoneAdminUIFieldMetaListView!
+ itemView(id: ID!): KeystoneAdminUIFieldMetaItemView
+ search: QueryMode
+}
+
+type KeystoneAdminUIFieldMetaCreateView {
+ fieldMode: KeystoneAdminUIFieldMetaCreateViewFieldMode!
+}
+
+enum KeystoneAdminUIFieldMetaCreateViewFieldMode {
+ edit
+ hidden
+}
+
+type KeystoneAdminUIFieldMetaListView {
+ fieldMode: KeystoneAdminUIFieldMetaListViewFieldMode!
+}
+
+enum KeystoneAdminUIFieldMetaListViewFieldMode {
+ read
+ hidden
+}
+
+type KeystoneAdminUIFieldMetaItemView {
+ fieldMode: KeystoneAdminUIFieldMetaItemViewFieldMode!
+}
+
+enum KeystoneAdminUIFieldMetaItemViewFieldMode {
+ edit
+ read
+ hidden
+}
+
+enum QueryMode {
+ default
+ insensitive
+}
+
+type KeystoneAdminUISort {
+ field: String!
+ direction: KeystoneAdminUISortDirection!
+}
+
+enum KeystoneAdminUISortDirection {
+ ASC
+ DESC
+}
diff --git a/examples/custom-admin-ui-pages/schema.prisma b/examples/custom-admin-ui-pages/schema.prisma
new file mode 100644
index 00000000000..a1efaced2f7
--- /dev/null
+++ b/examples/custom-admin-ui-pages/schema.prisma
@@ -0,0 +1,27 @@
+datasource sqlite {
+ url = env("DATABASE_URL")
+ provider = "sqlite"
+}
+
+generator client {
+ provider = "prisma-client-js"
+ output = "node_modules/.prisma/client"
+}
+
+model Task {
+ id String @id @default(cuid())
+ label String?
+ priority String?
+ isComplete Boolean?
+ assignedTo Person? @relation("Task_assignedTo", fields: [assignedToId], references: [id])
+ assignedToId String? @map("assignedTo")
+ finishBy DateTime?
+
+ @@index([assignedToId])
+}
+
+model Person {
+ id String @id @default(cuid())
+ name String?
+ tasks Task[] @relation("Task_assignedTo")
+}
\ No newline at end of file
diff --git a/examples/custom-admin-ui-pages/schema.ts b/examples/custom-admin-ui-pages/schema.ts
new file mode 100644
index 00000000000..9b539803be8
--- /dev/null
+++ b/examples/custom-admin-ui-pages/schema.ts
@@ -0,0 +1,28 @@
+import { createSchema, list } from '@keystone-next/keystone/schema';
+import { checkbox, relationship, text, timestamp } from '@keystone-next/fields';
+import { select } from '@keystone-next/fields';
+
+export const lists = createSchema({
+ Task: list({
+ fields: {
+ label: text({ isRequired: true }),
+ priority: select({
+ dataType: 'enum',
+ options: [
+ { label: 'Low', value: 'low' },
+ { label: 'Medium', value: 'medium' },
+ { label: 'High', value: 'high' },
+ ],
+ }),
+ isComplete: checkbox(),
+ assignedTo: relationship({ ref: 'Person.tasks', many: false }),
+ finishBy: timestamp(),
+ },
+ }),
+ Person: list({
+ fields: {
+ name: text({ isRequired: true }),
+ tasks: relationship({ ref: 'Task.assignedTo', many: true }),
+ },
+ }),
+});
diff --git a/examples/custom-field-view/schema.graphql b/examples/custom-field-view/schema.graphql
index 633316ca9e5..64ed5a1fcfd 100644
--- a/examples/custom-field-view/schema.graphql
+++ b/examples/custom-field-view/schema.graphql
@@ -182,14 +182,14 @@ type Mutation {
createTasks(data: [TasksCreateInput]): [Task]
updateTask(id: ID!, data: TaskUpdateInput): Task
updateTasks(data: [TaskUpdateArgs]): [Task]
- deleteTask(id: ID!): Task
- deleteTasks(ids: [ID!]): [Task]
+ deleteTask(where: TaskWhereUniqueInput!): Task
+ deleteTasks(where: [TaskWhereUniqueInput!]!): [Task]
createPerson(data: PersonCreateInput): Person
createPeople(data: [PeopleCreateInput]): [Person]
updatePerson(id: ID!, data: PersonUpdateInput): Person
updatePeople(data: [PersonUpdateArgs]): [Person]
- deletePerson(id: ID!): Person
- deletePeople(ids: [ID!]): [Person]
+ deletePerson(where: PersonWhereUniqueInput!): Person
+ deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
}
type Query {
diff --git a/examples/custom-field/schema.graphql b/examples/custom-field/schema.graphql
index 491ee9da0e4..0cdc565f96f 100644
--- a/examples/custom-field/schema.graphql
+++ b/examples/custom-field/schema.graphql
@@ -205,14 +205,14 @@ type Mutation {
createPosts(data: [PostsCreateInput]): [Post]
updatePost(id: ID!, data: PostUpdateInput): Post
updatePosts(data: [PostUpdateArgs]): [Post]
- deletePost(id: ID!): Post
- deletePosts(ids: [ID!]): [Post]
+ deletePost(where: PostWhereUniqueInput!): Post
+ deletePosts(where: [PostWhereUniqueInput!]!): [Post]
createAuthor(data: AuthorCreateInput): Author
createAuthors(data: [AuthorsCreateInput]): [Author]
updateAuthor(id: ID!, data: AuthorUpdateInput): Author
updateAuthors(data: [AuthorUpdateArgs]): [Author]
- deleteAuthor(id: ID!): Author
- deleteAuthors(ids: [ID!]): [Author]
+ deleteAuthor(where: AuthorWhereUniqueInput!): Author
+ deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author]
}
type Query {
diff --git a/examples/default-values/schema.graphql b/examples/default-values/schema.graphql
index 55380b7c1d2..30beff9bc69 100644
--- a/examples/default-values/schema.graphql
+++ b/examples/default-values/schema.graphql
@@ -179,14 +179,14 @@ type Mutation {
createTasks(data: [TasksCreateInput]): [Task]
updateTask(id: ID!, data: TaskUpdateInput): Task
updateTasks(data: [TaskUpdateArgs]): [Task]
- deleteTask(id: ID!): Task
- deleteTasks(ids: [ID!]): [Task]
+ deleteTask(where: TaskWhereUniqueInput!): Task
+ deleteTasks(where: [TaskWhereUniqueInput!]!): [Task]
createPerson(data: PersonCreateInput): Person
createPeople(data: [PeopleCreateInput]): [Person]
updatePerson(id: ID!, data: PersonUpdateInput): Person
updatePeople(data: [PersonUpdateArgs]): [Person]
- deletePerson(id: ID!): Person
- deletePeople(ids: [ID!]): [Person]
+ deletePerson(where: PersonWhereUniqueInput!): Person
+ deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
}
type Query {
diff --git a/examples/document-field/schema.graphql b/examples/document-field/schema.graphql
index f59e850e4c3..0ad49bbde1a 100644
--- a/examples/document-field/schema.graphql
+++ b/examples/document-field/schema.graphql
@@ -208,14 +208,14 @@ type Mutation {
createPosts(data: [PostsCreateInput]): [Post]
updatePost(id: ID!, data: PostUpdateInput): Post
updatePosts(data: [PostUpdateArgs]): [Post]
- deletePost(id: ID!): Post
- deletePosts(ids: [ID!]): [Post]
+ deletePost(where: PostWhereUniqueInput!): Post
+ deletePosts(where: [PostWhereUniqueInput!]!): [Post]
createAuthor(data: AuthorCreateInput): Author
createAuthors(data: [AuthorsCreateInput]): [Author]
updateAuthor(id: ID!, data: AuthorUpdateInput): Author
updateAuthors(data: [AuthorUpdateArgs]): [Author]
- deleteAuthor(id: ID!): Author
- deleteAuthors(ids: [ID!]): [Author]
+ deleteAuthor(where: AuthorWhereUniqueInput!): Author
+ deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author]
}
type Query {
diff --git a/examples/extend-graphql-schema/schema.graphql b/examples/extend-graphql-schema/schema.graphql
index ac8ce899eb7..bfba3284e41 100644
--- a/examples/extend-graphql-schema/schema.graphql
+++ b/examples/extend-graphql-schema/schema.graphql
@@ -193,14 +193,14 @@ type Mutation {
createPosts(data: [PostsCreateInput]): [Post]
updatePost(id: ID!, data: PostUpdateInput): Post
updatePosts(data: [PostUpdateArgs]): [Post]
- deletePost(id: ID!): Post
- deletePosts(ids: [ID!]): [Post]
+ deletePost(where: PostWhereUniqueInput!): Post
+ deletePosts(where: [PostWhereUniqueInput!]!): [Post]
createAuthor(data: AuthorCreateInput): Author
createAuthors(data: [AuthorsCreateInput]): [Author]
updateAuthor(id: ID!, data: AuthorUpdateInput): Author
updateAuthors(data: [AuthorUpdateArgs]): [Author]
- deleteAuthor(id: ID!): Author
- deleteAuthors(ids: [ID!]): [Author]
+ deleteAuthor(where: AuthorWhereUniqueInput!): Author
+ deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author]
"""
Publish a post
diff --git a/examples/json/schema.graphql b/examples/json/schema.graphql
index 414c056db57..9aa15bc7ad4 100644
--- a/examples/json/schema.graphql
+++ b/examples/json/schema.graphql
@@ -156,14 +156,14 @@ type Mutation {
createPackages(data: [PackagesCreateInput]): [Package]
updatePackage(id: ID!, data: PackageUpdateInput): Package
updatePackages(data: [PackageUpdateArgs]): [Package]
- deletePackage(id: ID!): Package
- deletePackages(ids: [ID!]): [Package]
+ deletePackage(where: PackageWhereUniqueInput!): Package
+ deletePackages(where: [PackageWhereUniqueInput!]!): [Package]
createPerson(data: PersonCreateInput): Person
createPeople(data: [PeopleCreateInput]): [Person]
updatePerson(id: ID!, data: PersonUpdateInput): Person
updatePeople(data: [PersonUpdateArgs]): [Person]
- deletePerson(id: ID!): Person
- deletePeople(ids: [ID!]): [Person]
+ deletePerson(where: PersonWhereUniqueInput!): Person
+ deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
}
type Query {
diff --git a/examples/task-manager/schema.graphql b/examples/task-manager/schema.graphql
index 55380b7c1d2..30beff9bc69 100644
--- a/examples/task-manager/schema.graphql
+++ b/examples/task-manager/schema.graphql
@@ -179,14 +179,14 @@ type Mutation {
createTasks(data: [TasksCreateInput]): [Task]
updateTask(id: ID!, data: TaskUpdateInput): Task
updateTasks(data: [TaskUpdateArgs]): [Task]
- deleteTask(id: ID!): Task
- deleteTasks(ids: [ID!]): [Task]
+ deleteTask(where: TaskWhereUniqueInput!): Task
+ deleteTasks(where: [TaskWhereUniqueInput!]!): [Task]
createPerson(data: PersonCreateInput): Person
createPeople(data: [PeopleCreateInput]): [Person]
updatePerson(id: ID!, data: PersonUpdateInput): Person
updatePeople(data: [PersonUpdateArgs]): [Person]
- deletePerson(id: ID!): Person
- deletePeople(ids: [ID!]): [Person]
+ deletePerson(where: PersonWhereUniqueInput!): Person
+ deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
}
type Query {
diff --git a/examples/testing/schema.graphql b/examples/testing/schema.graphql
index 488720aca4f..02d9950b51c 100644
--- a/examples/testing/schema.graphql
+++ b/examples/testing/schema.graphql
@@ -198,14 +198,14 @@ type Mutation {
createTasks(data: [TasksCreateInput]): [Task]
updateTask(id: ID!, data: TaskUpdateInput): Task
updateTasks(data: [TaskUpdateArgs]): [Task]
- deleteTask(id: ID!): Task
- deleteTasks(ids: [ID!]): [Task]
+ deleteTask(where: TaskWhereUniqueInput!): Task
+ deleteTasks(where: [TaskWhereUniqueInput!]!): [Task]
createPerson(data: PersonCreateInput): Person
createPeople(data: [PeopleCreateInput]): [Person]
updatePerson(id: ID!, data: PersonUpdateInput): Person
updatePeople(data: [PersonUpdateArgs]): [Person]
- deletePerson(id: ID!): Person
- deletePeople(ids: [ID!]): [Person]
+ deletePerson(where: PersonWhereUniqueInput!): Person
+ deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
authenticatePersonWithPassword(
email: String!
password: String!
diff --git a/examples/virtual-field/schema.graphql b/examples/virtual-field/schema.graphql
index 265e49a5433..e3a8d3b511c 100644
--- a/examples/virtual-field/schema.graphql
+++ b/examples/virtual-field/schema.graphql
@@ -204,14 +204,14 @@ type Mutation {
createPosts(data: [PostsCreateInput]): [Post]
updatePost(id: ID!, data: PostUpdateInput): Post
updatePosts(data: [PostUpdateArgs]): [Post]
- deletePost(id: ID!): Post
- deletePosts(ids: [ID!]): [Post]
+ deletePost(where: PostWhereUniqueInput!): Post
+ deletePosts(where: [PostWhereUniqueInput!]!): [Post]
createAuthor(data: AuthorCreateInput): Author
createAuthors(data: [AuthorsCreateInput]): [Author]
updateAuthor(id: ID!, data: AuthorUpdateInput): Author
updateAuthors(data: [AuthorUpdateArgs]): [Author]
- deleteAuthor(id: ID!): Author
- deleteAuthors(ids: [ID!]): [Author]
+ deleteAuthor(where: AuthorWhereUniqueInput!): Author
+ deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author]
}
type Query {
diff --git a/examples/with-auth/schema.graphql b/examples/with-auth/schema.graphql
index 488720aca4f..02d9950b51c 100644
--- a/examples/with-auth/schema.graphql
+++ b/examples/with-auth/schema.graphql
@@ -198,14 +198,14 @@ type Mutation {
createTasks(data: [TasksCreateInput]): [Task]
updateTask(id: ID!, data: TaskUpdateInput): Task
updateTasks(data: [TaskUpdateArgs]): [Task]
- deleteTask(id: ID!): Task
- deleteTasks(ids: [ID!]): [Task]
+ deleteTask(where: TaskWhereUniqueInput!): Task
+ deleteTasks(where: [TaskWhereUniqueInput!]!): [Task]
createPerson(data: PersonCreateInput): Person
createPeople(data: [PeopleCreateInput]): [Person]
updatePerson(id: ID!, data: PersonUpdateInput): Person
updatePeople(data: [PersonUpdateArgs]): [Person]
- deletePerson(id: ID!): Person
- deletePeople(ids: [ID!]): [Person]
+ deletePerson(where: PersonWhereUniqueInput!): Person
+ deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
authenticatePersonWithPassword(
email: String!
password: String!
diff --git a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx
index 3e8ad8caef1..ad78a20b81c 100644
--- a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx
+++ b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.tsx
@@ -225,7 +225,7 @@ function DeleteButton({
const toasts = useToasts();
const [deleteItem, { loading }] = useMutation(
gql`mutation ($id: ID!) {
- ${list.gqlNames.deleteMutationName}(id: $id) {
+ ${list.gqlNames.deleteMutationName}(where: { id: $id }) {
id
}
}`,
diff --git a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/index.tsx b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/index.tsx
index 177dff1c384..e3b977c8737 100644
--- a/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/index.tsx
+++ b/packages/keystone/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/index.tsx
@@ -436,8 +436,8 @@ function DeleteManyButton({
useMemo(
() =>
gql`
- mutation($ids: [ID!]!) {
- ${list.gqlNames.deleteManyMutationName}(ids: $ids) {
+ mutation($where: [${list.gqlNames.whereUniqueInputName}!]!) {
+ ${list.gqlNames.deleteManyMutationName}(where: $where) {
id
}
}
@@ -468,7 +468,7 @@ function DeleteManyButton({
label: 'Delete',
action: async () => {
await deleteItems({
- variables: { ids: [...selectedItems] },
+ variables: { where: [...selectedItems].map(id => ({ id })) },
}).catch(err => {
toasts.addToast({
title: 'Failed to delete items',
diff --git a/packages/keystone/src/lib/core/mutations/index.ts b/packages/keystone/src/lib/core/mutations/index.ts
index 4c9229393de..c88d92d22f4 100644
--- a/packages/keystone/src/lib/core/mutations/index.ts
+++ b/packages/keystone/src/lib/core/mutations/index.ts
@@ -88,23 +88,22 @@ export function getMutationsForList(list: InitialisedList, provider: DatabasePro
const deleteOne = schema.field({
type: list.types.output,
- args: { id: schema.arg({ type: schema.nonNull(schema.ID) }) },
- resolve(rootVal, { id }, context) {
- return deletes.deleteOne({ id }, list, context);
+ args: { where: schema.arg({ type: schema.nonNull(list.types.uniqueWhere) }) },
+ resolve(rootVal, { where }, context) {
+ return deletes.deleteOne(where, list, context);
},
});
const deleteMany = schema.field({
type: schema.list(list.types.output),
- args: { ids: schema.arg({ type: schema.list(schema.nonNull(schema.ID)) }) },
- resolve(rootVal, { ids }, context) {
+ args: {
+ where: schema.arg({
+ type: schema.nonNull(schema.list(schema.nonNull(list.types.uniqueWhere))),
+ }),
+ },
+ resolve(rootVal, { where }, context) {
return promisesButSettledWhenAllSettledAndInOrder(
- deletes.deleteMany(
- (ids || []).map(id => ({ id })),
- list,
- context,
- provider
- )
+ deletes.deleteMany(where, list, context, provider)
);
},
});
diff --git a/packages/keystone/src/scripts/tests/fixtures/basic-project/schema.graphql b/packages/keystone/src/scripts/tests/fixtures/basic-project/schema.graphql
index 52fcac3e65e..9b0505c4968 100644
--- a/packages/keystone/src/scripts/tests/fixtures/basic-project/schema.graphql
+++ b/packages/keystone/src/scripts/tests/fixtures/basic-project/schema.graphql
@@ -66,8 +66,8 @@ type Mutation {
createTodos(data: [TodosCreateInput]): [Todo]
updateTodo(id: ID!, data: TodoUpdateInput): Todo
updateTodos(data: [TodoUpdateArgs]): [Todo]
- deleteTodo(id: ID!): Todo
- deleteTodos(ids: [ID!]): [Todo]
+ deleteTodo(where: TodoWhereUniqueInput!): Todo
+ deleteTodos(where: [TodoWhereUniqueInput!]!): [Todo]
}
type Query {
diff --git a/packages/types/src/context.ts b/packages/types/src/context.ts
index 89c77a518d3..b19cd9cdf37 100644
--- a/packages/types/src/context.ts
+++ b/packages/types/src/context.ts
@@ -65,9 +65,15 @@ export type KeystoneListsAPI[]>;
- deleteOne(args: { readonly id: string } & ResolveFields): Promise | null>;
+ deleteOne(
+ args: {
+ readonly where: KeystoneListsTypeInfo[Key]['inputs']['uniqueWhere'];
+ } & ResolveFields
+ ): Promise | null>;
deleteMany(
- args: { readonly ids: readonly string[] } & ResolveFields
+ args: {
+ readonly where: readonly KeystoneListsTypeInfo[Key]['inputs']['uniqueWhere'][];
+ } & ResolveFields
): Promise[]>;
};
};
@@ -113,9 +119,11 @@ export type KeystoneDbAPI;
- deleteOne(args: { readonly id: string }): Promise;
+ deleteOne(args: {
+ readonly where: KeystoneListsTypeInfo[Key]['inputs']['uniqueWhere'];
+ }): Promise;
deleteMany(args: {
- readonly ids: readonly string[];
+ readonly where: readonly KeystoneListsTypeInfo[Key]['inputs']['uniqueWhere'][];
}): Promise;
};
};
diff --git a/tests/api-tests/access-control/authed.test.ts b/tests/api-tests/access-control/authed.test.ts
index 8ef4eb30b57..d8ebf3f782d 100644
--- a/tests/api-tests/access-control/authed.test.ts
+++ b/tests/api-tests/access-control/authed.test.ts
@@ -99,7 +99,7 @@ describe('Authed', () => {
const item = await context.lists[listKey].createOne({ data });
expect(item).not.toBe(null);
expect(item.id).not.toBe(null);
- await context.sudo().lists[listKey].deleteOne({ id: item.id });
+ await context.sudo().lists[listKey].deleteOne({ where: { id: item.id } });
});
});
});
@@ -124,7 +124,7 @@ describe('Authed', () => {
} else {
expect(item[fieldName]).toBe(undefined);
}
- await context.sudo().lists[listKey].deleteOne({ id: item.id });
+ await context.sudo().lists[listKey].deleteOne({ where: { id: item.id } });
});
});
});
@@ -143,7 +143,7 @@ describe('Authed', () => {
});
expect(item).not.toBe(null);
expect(item.id).not.toBe(null);
- await context.sudo().lists[listKey].deleteOne({ id: item.id });
+ await context.sudo().lists[listKey].deleteOne({ where: { id: item.id } });
});
});
});
@@ -395,7 +395,9 @@ describe('Authed', () => {
test(`single allowed: ${JSON.stringify(access)}`, async () => {
const { id } = await create({ name: 'Hello' });
- const deleted = await context.lists[nameFn[mode](access)].deleteOne({ id });
+ const deleted = await context.lists[nameFn[mode](access)].deleteOne({
+ where: { id },
+ });
expect(deleted).not.toBe(null);
expect(deleted!.id).toEqual(id);
});
@@ -403,7 +405,7 @@ describe('Authed', () => {
test(`single denies: ${JSON.stringify(access)}`, async () => {
const { id: invalidId } = await create({ name: 'hi' });
const deleteMutationName = `delete${nameFn[mode](access)}`;
- const query = `mutation { ${deleteMutationName}(id: "${invalidId}") { id } }`;
+ const query = `mutation { ${deleteMutationName}(where: { id: "${invalidId}" }) { id } }`;
const { data, errors } = await context.graphql.raw({ query });
if (mode === 'imperative') {
expect(errors).toBe(undefined);
@@ -416,7 +418,7 @@ describe('Authed', () => {
test(`single denies missing: ${JSON.stringify(access)}`, async () => {
const deleteMutationName = `delete${nameFn[mode](access)}`;
- const query = `mutation { ${deleteMutationName}(id: "${FAKE_ID[provider]}") { id } }`;
+ const query = `mutation { ${deleteMutationName}(where: { id: "${FAKE_ID[provider]}" }) { id } }`;
const { data, errors } = await context.graphql.raw({ query });
expectNoAccess(data, errors, deleteMutationName);
});
@@ -425,7 +427,7 @@ describe('Authed', () => {
const { id: validId1 } = await create({ name: 'Hello' });
const { id: validId2 } = await create({ name: 'Hello' });
const multiDeleteMutationName = `delete${nameFn[mode](access)}s`;
- const query = `mutation { ${multiDeleteMutationName}(ids: ["${validId1}", "${validId2}"]) { id } }`;
+ const query = `mutation { ${multiDeleteMutationName}(where: [{ id: "${validId1}" }, { id: "${validId2}" }]) { id } }`;
const { data, errors } = await context.graphql.raw({ query });
expectNamedArray(data, errors, multiDeleteMutationName, [validId1, validId2]);
});
@@ -434,7 +436,7 @@ describe('Authed', () => {
const { id: validId1 } = await create({ name: 'hi' });
const { id: validId2 } = await create({ name: 'hi' });
const multiDeleteMutationName = `delete${nameFn[mode](access)}s`;
- const query = `mutation { ${multiDeleteMutationName}(ids: ["${validId1}", "${validId2}"]) { id } }`;
+ const query = `mutation { ${multiDeleteMutationName}(where: [{ id: "${validId1}" }, { id: "${validId2}" }]) { id } }`;
const { data, errors } = await context.graphql.raw({ query });
if (mode === 'imperative') {
expectNamedArray(data, errors, multiDeleteMutationName, [validId1, validId2]);
@@ -451,7 +453,7 @@ describe('Authed', () => {
const { id: validId1 } = await create({ name: 'Hello' });
const { id: invalidId } = await create({ name: 'hi' });
const multiDeleteMutationName = `delete${nameFn[mode](access)}s`;
- const query = `mutation { ${multiDeleteMutationName}(ids: ["${validId1}", "${invalidId}"]) { id } }`;
+ const query = `mutation { ${multiDeleteMutationName}(where: [{ id: "${validId1}" }, { id: "${invalidId}" }]) { id } }`;
const { data, errors } = await context.graphql.raw({ query });
if (mode === 'imperative') {
expectNamedArray(data, errors, multiDeleteMutationName, [validId1, invalidId]);
@@ -463,7 +465,7 @@ describe('Authed', () => {
test(`multi denies missing: ${JSON.stringify(access)}`, async () => {
const multiDeleteMutationName = `delete${nameFn[mode](access)}s`;
- const query = `mutation { ${multiDeleteMutationName}(ids: ["${FAKE_ID[provider]}", "${FAKE_ID_2[provider]}"]) { id } }`;
+ const query = `mutation { ${multiDeleteMutationName}(where: [{ id: "${FAKE_ID[provider]}" }, { id: "${FAKE_ID_2[provider]}" }]) { id } }`;
const { data, errors } = await context.graphql.raw({ query });
expectAccessDenied(errors, [
{ path: [multiDeleteMutationName, 0] },
diff --git a/tests/api-tests/access-control/mutations-list-declarative.test.ts b/tests/api-tests/access-control/mutations-list-declarative.test.ts
index d191a1a54fd..7f7c43ed52e 100644
--- a/tests/api-tests/access-control/mutations-list-declarative.test.ts
+++ b/tests/api-tests/access-control/mutations-list-declarative.test.ts
@@ -55,11 +55,11 @@ describe('Access control - Imperative => declarative', () => {
// Valid names should pass
const user1 = await context.lists.User.createOne({ data: { name: 'good' } });
const user2 = await context.lists.User.createOne({ data: { name: 'no delete' } });
- await context.lists.User.deleteOne({ id: user1.id });
+ await context.lists.User.deleteOne({ where: { id: user1.id } });
// Invalid name
const { data, errors } = await context.graphql.raw({
- query: `mutation ($id: ID!) { deleteUser(id: $id) { id } }`,
+ query: `mutation ($id: ID!) { deleteUser(where: { id: $id }) { id } }`,
variables: { id: user2.id },
});
@@ -155,10 +155,14 @@ describe('Access control - Imperative => declarative', () => {
// Mix of good and bad names
const { data, errors } = await context.graphql.raw({
- query: `mutation ($ids: [ID!]) { deleteUsers(ids: $ids) { id name } }`,
- variables: { ids: [users[0].id, users[1].id, users[2].id, users[3].id] },
+ query: `mutation ($where: [UserWhereUniqueInput!]!) { deleteUsers(where: $where) { id name } }`,
+ variables: {
+ where: [users[0].id, users[1].id, users[2].id, users[3].id].map(id => ({ id })),
+ },
});
+ expectAccessDenied(errors, [{ path: ['deleteUsers', 1] }, { path: ['deleteUsers', 3] }]);
+
// Valid users are returned, invalid come back as null
// The invalid deletes should have errors which point to the nulls in their path
expect(data).toEqual({
@@ -169,7 +173,6 @@ describe('Access control - Imperative => declarative', () => {
null,
],
});
- expectAccessDenied(errors, [{ path: ['deleteUsers', 1] }, { path: ['deleteUsers', 3] }]);
// Three users should still exist in the database
const _users = await context.lists.User.findMany({
diff --git a/tests/api-tests/access-control/mutations-list-static.test.ts b/tests/api-tests/access-control/mutations-list-static.test.ts
index c6dd438bfbe..3ce9600cacc 100644
--- a/tests/api-tests/access-control/mutations-list-static.test.ts
+++ b/tests/api-tests/access-control/mutations-list-static.test.ts
@@ -85,11 +85,11 @@ describe('Access control - Imperative => static', () => {
// Valid names should pass
const user1 = await context.lists.User.createOne({ data: { name: 'good' } });
const user2 = await context.lists.User.createOne({ data: { name: 'no delete' } });
- await context.lists.User.deleteOne({ id: user1.id });
+ await context.lists.User.deleteOne({ where: { id: user1.id } });
// Invalid name
const { data, errors } = await context.graphql.raw({
- query: `mutation ($id: ID!) { deleteUser(id: $id) { id } }`,
+ query: `mutation ($id: ID!) { deleteUser(where: { id: $id }) { id } }`,
variables: { id: user2.id },
});
@@ -217,8 +217,10 @@ describe('Access control - Imperative => static', () => {
// Mix of good and bad names
const { data, errors } = await context.graphql.raw({
- query: `mutation ($ids: [ID!]) { deleteUsers(ids: $ids) { id name } }`,
- variables: { ids: [users[0].id, users[1].id, users[2].id, users[3].id] },
+ query: `mutation ($where: [UserWhereUniqueInput!]!) { deleteUsers(where: $where) { id name } }`,
+ variables: {
+ where: [users[0].id, users[1].id, users[2].id, users[3].id].map(id => ({ id })),
+ },
});
// Valid users are returned, invalid come back as null
diff --git a/tests/api-tests/access-control/not-authed.test.ts b/tests/api-tests/access-control/not-authed.test.ts
index b8dbe1c8898..752b0a93cf6 100644
--- a/tests/api-tests/access-control/not-authed.test.ts
+++ b/tests/api-tests/access-control/not-authed.test.ts
@@ -362,14 +362,14 @@ describe(`Not authed`, () => {
.forEach(access => {
test(`single denied: ${JSON.stringify(access)}`, async () => {
const deleteMutationName = `delete${nameFn[mode](access)}`;
- const query = `mutation { ${deleteMutationName}(id: "${FAKE_ID[provider]}") { id } }`;
+ const query = `mutation { ${deleteMutationName}(where: {id: "${FAKE_ID[provider]}" }) { id } }`;
const { data, errors } = await context.graphql.raw({ query });
expectNoAccess(data, errors, deleteMutationName);
});
test(`multi denied: ${JSON.stringify(access)}`, async () => {
const multiDeleteMutationName = `delete${nameFn[mode](access)}s`;
- const query = `mutation { ${multiDeleteMutationName}(ids: ["${FAKE_ID[provider]}"]) { id } }`;
+ const query = `mutation { ${multiDeleteMutationName}(where: [{ id: "${FAKE_ID[provider]}" }]) { id } }`;
const { data, errors } = await context.graphql.raw({ query });
expect(data).toEqual({ [multiDeleteMutationName]: [null] });
diff --git a/tests/api-tests/fields/crud.test.ts b/tests/api-tests/fields/crud.test.ts
index 3805287ba43..2d492c1cc89 100644
--- a/tests/api-tests/fields/crud.test.ts
+++ b/tests/api-tests/fields/crud.test.ts
@@ -101,7 +101,13 @@ testModules
? `id name ${readFieldName || fieldName} { ${subfieldName} }`
: `id name ${readFieldName || fieldName}`;
- const withHelpers = (wrappedFn: (args: any) => void | Promise) => {
+ const withHelpers = (
+ wrappedFn: (args: {
+ context: KeystoneContext;
+ listKey: string;
+ items: readonly Record[];
+ }) => void | Promise
+ ) => {
return async ({ context, listKey }: { context: KeystoneContext; listKey: string }) => {
const items = await context.lists[listKey].findMany({
orderBy: { name: 'asc' },
@@ -213,7 +219,7 @@ testModules
keystoneTestWrapper(
withHelpers(async ({ context, items, listKey }) => {
const data = await context.lists[listKey].deleteOne({
- id: items[0].id,
+ where: { id: items[0].id },
query,
});
expect(data).not.toBe(null);
diff --git a/tests/api-tests/fields/types/document.test.ts b/tests/api-tests/fields/types/document.test.ts
index 82f68d69e53..c3df3699ba4 100644
--- a/tests/api-tests/fields/types/document.test.ts
+++ b/tests/api-tests/fields/types/document.test.ts
@@ -215,7 +215,7 @@ describe('Document field type', () => {
'hydrateRelationships: true - dangling reference',
runner(async ({ context }) => {
const { alice, bob, charlie, post, content } = await initData({ context });
- await context.lists.Author.deleteOne({ id: bob.id });
+ await context.lists.Author.deleteOne({ where: { id: bob.id } });
const _post = await context.lists.Post.findOne({
where: { id: post.id },
query: 'content { document(hydrateRelationships: true) }',
diff --git a/tests/api-tests/hooks/validation.test.ts b/tests/api-tests/hooks/validation.test.ts
index 730b94cca99..23fa581706f 100644
--- a/tests/api-tests/hooks/validation.test.ts
+++ b/tests/api-tests/hooks/validation.test.ts
@@ -77,11 +77,11 @@ describe('List Hooks: #validateInput()', () => {
// Valid names should pass
const user1 = await context.lists.User.createOne({ data: { name: 'good' } });
const user2 = await context.lists.User.createOne({ data: { name: 'no delete' } });
- await context.lists.User.deleteOne({ id: user1.id });
+ await context.lists.User.deleteOne({ where: { id: user1.id } });
// Invalid name
const { data, errors } = await context.graphql.raw({
- query: `mutation ($id: ID!) { deleteUser(id: $id) { id } }`,
+ query: `mutation ($id: ID!) { deleteUser(where: { id: $id }) { id } }`,
variables: { id: user2.id },
});
@@ -206,8 +206,10 @@ describe('List Hooks: #validateInput()', () => {
// Mix of good and bad names
const { data, errors } = await context.graphql.raw({
- query: `mutation ($ids: [ID!]) { deleteUsers(ids: $ids) { id name } }`,
- variables: { ids: [users[0].id, users[1].id, users[2].id, users[3].id] },
+ query: `mutation ($where: [UserWhereUniqueInput!]!) { deleteUsers(where: $where) { id name } }`,
+ variables: {
+ where: [users[0].id, users[1].id, users[2].id, users[3].id].map(id => ({ id })),
+ },
});
// Valid users are returned, invalid come back as null
diff --git a/tests/api-tests/queries/cache-hints.test.ts b/tests/api-tests/queries/cache-hints.test.ts
index fe0e12d54bb..c9fe7518e6c 100644
--- a/tests/api-tests/queries/cache-hints.test.ts
+++ b/tests/api-tests/queries/cache-hints.test.ts
@@ -303,7 +303,7 @@ describe('cache hints', () => {
const { body } = await graphQLRequest({
query: `
mutation {
- deletePost(id: "${posts[0].id}") {
+ deletePost(where: { id: "${posts[0].id}" }) {
id
}
}
diff --git a/tests/api-tests/relationships/crud-self-ref/many-to-many-one-sided.test.ts b/tests/api-tests/relationships/crud-self-ref/many-to-many-one-sided.test.ts
index eb01e96b2d6..62956e1d6ba 100644
--- a/tests/api-tests/relationships/crud-self-ref/many-to-many-one-sided.test.ts
+++ b/tests/api-tests/relationships/crud-self-ref/many-to-many-one-sided.test.ts
@@ -324,7 +324,7 @@ describe(`Many-to-many relationships`, () => {
const { user, friend } = await createUserAndFriend(context);
// Run the query to disconnect the location from company
- const _user = await context.lists.User.deleteOne({ id: user.id });
+ const _user = await context.lists.User.deleteOne({ where: { id: user.id } });
expect(_user?.id).toBe(user.id);
// Check the link has been broken
diff --git a/tests/api-tests/relationships/crud-self-ref/many-to-many.test.ts b/tests/api-tests/relationships/crud-self-ref/many-to-many.test.ts
index a8d854a71a7..8a77b6401b3 100644
--- a/tests/api-tests/relationships/crud-self-ref/many-to-many.test.ts
+++ b/tests/api-tests/relationships/crud-self-ref/many-to-many.test.ts
@@ -410,7 +410,7 @@ describe(`Many-to-many relationships`, () => {
const { user, friend } = await createUserAndFriend(context);
// Run the query to disconnect the location from company
- const _user = await context.lists.User.deleteOne({ id: user.id });
+ const _user = await context.lists.User.deleteOne({ where: { id: user.id } });
expect(_user?.id).toBe(user.id);
// Check the link has been broken
diff --git a/tests/api-tests/relationships/crud-self-ref/one-to-many-one-sided.test.ts b/tests/api-tests/relationships/crud-self-ref/one-to-many-one-sided.test.ts
index 22378b6610e..9b18346a8f7 100644
--- a/tests/api-tests/relationships/crud-self-ref/one-to-many-one-sided.test.ts
+++ b/tests/api-tests/relationships/crud-self-ref/one-to-many-one-sided.test.ts
@@ -305,7 +305,7 @@ describe(`One-to-many relationships`, () => {
const { friend, user } = await createUserAndFriend(context);
// Run the query to disconnect the location from company
- const _user = await context.lists.User.deleteOne({ id: user.id });
+ const _user = await context.lists.User.deleteOne({ where: { id: user.id } });
expect(_user?.id).toBe(user.id);
// Check the link has been broken
@@ -323,7 +323,7 @@ describe(`One-to-many relationships`, () => {
// Delete company {name}
const id = users.find(company => company.name === name)?.id;
- const _user = await context.lists.User.deleteOne({ id });
+ const _user = await context.lists.User.deleteOne({ where: { id } });
expect(_user?.id).toBe(id);
// Check all the companies look how we expect
@@ -380,7 +380,7 @@ describe(`One-to-many relationships`, () => {
// Delete friend {name}
const id = users.find(user => user.name === name)?.id;
- const _user = await context.lists.User.deleteOne({ id });
+ const _user = await context.lists.User.deleteOne({ where: { id } });
expect(_user?.id).toBe(id);
// Check all the companies look how we expect
diff --git a/tests/api-tests/relationships/crud-self-ref/one-to-many.test.ts b/tests/api-tests/relationships/crud-self-ref/one-to-many.test.ts
index 3ae5b55b0b6..57239b8b6ed 100644
--- a/tests/api-tests/relationships/crud-self-ref/one-to-many.test.ts
+++ b/tests/api-tests/relationships/crud-self-ref/one-to-many.test.ts
@@ -443,7 +443,7 @@ describe(`One-to-many relationships`, () => {
const { user, friend } = await createUserAndFriend(context);
// Run the query to disconnect the location from company
- const _user = await context.lists.User.deleteOne({ id: user.id });
+ const _user = await context.lists.User.deleteOne({ where: { id: user.id } });
expect(_user?.id).toBe(user.id);
// Check the link has been broken
diff --git a/tests/api-tests/relationships/crud-self-ref/one-to-one.test.ts b/tests/api-tests/relationships/crud-self-ref/one-to-one.test.ts
index c5b189effb3..ac2b9d4da59 100644
--- a/tests/api-tests/relationships/crud-self-ref/one-to-one.test.ts
+++ b/tests/api-tests/relationships/crud-self-ref/one-to-one.test.ts
@@ -427,7 +427,7 @@ describe(`One-to-one relationships`, () => {
const { user, friend } = await createUserAndFriend(context);
// Run the query to disconnect the location from company
- const _user = await context.lists.User.deleteOne({ id: user.id });
+ const _user = await context.lists.User.deleteOne({ where: { id: user.id } });
expect(_user?.id).toBe(user.id);
// Check the link has been broken
diff --git a/tests/api-tests/relationships/crud/many-to-many-one-sided.test.ts b/tests/api-tests/relationships/crud/many-to-many-one-sided.test.ts
index 0e745c77018..1439f2ba07f 100644
--- a/tests/api-tests/relationships/crud/many-to-many-one-sided.test.ts
+++ b/tests/api-tests/relationships/crud/many-to-many-one-sided.test.ts
@@ -399,7 +399,7 @@ describe(`Many-to-many relationships`, () => {
const { location, company } = await createCompanyAndLocation(context);
// Run the query to disconnect the location from company
- const _company = await context.lists.Company.deleteOne({ id: company.id });
+ const _company = await context.lists.Company.deleteOne({ where: { id: company.id } });
expect(_company?.id).toBe(company.id);
// Check the link has been broken
diff --git a/tests/api-tests/relationships/crud/many-to-many.test.ts b/tests/api-tests/relationships/crud/many-to-many.test.ts
index dfeca71811e..23762204047 100644
--- a/tests/api-tests/relationships/crud/many-to-many.test.ts
+++ b/tests/api-tests/relationships/crud/many-to-many.test.ts
@@ -507,7 +507,7 @@ describe(`Many-to-many relationships`, () => {
const { location, company } = await createCompanyAndLocation(context);
// Run the query to disconnect the location from company
- const _company = await context.lists.Company.deleteOne({ id: company.id });
+ const _company = await context.lists.Company.deleteOne({ where: { id: company.id } });
expect(_company?.id).toBe(company.id);
// Check the link has been broken
diff --git a/tests/api-tests/relationships/crud/one-to-many-one-sided.test.ts b/tests/api-tests/relationships/crud/one-to-many-one-sided.test.ts
index 1a46a7a70ba..d4485f8b213 100644
--- a/tests/api-tests/relationships/crud/one-to-many-one-sided.test.ts
+++ b/tests/api-tests/relationships/crud/one-to-many-one-sided.test.ts
@@ -340,7 +340,7 @@ describe(`One-to-many relationships`, () => {
const { location, company } = await createCompanyAndLocation(context);
// Run the query to disconnect the location from company
- const _company = await context.lists.Company.deleteOne({ id: company.id });
+ const _company = await context.lists.Company.deleteOne({ where: { id: company.id } });
expect(_company?.id).toBe(company.id);
// Check the link has been broken
@@ -358,7 +358,7 @@ describe(`One-to-many relationships`, () => {
// Delete company {name}
const id = companies.find(company => company.name === name)?.id;
- const _company = await context.lists.Company.deleteOne({ id });
+ const _company = await context.lists.Company.deleteOne({ where: { id } });
expect(_company?.id).toBe(id);
// Check all the companies look how we expect
@@ -413,7 +413,7 @@ describe(`One-to-many relationships`, () => {
// Delete location {name}
const id = locations.find(location => location.name === name)?.id;
- const deleted = await context.lists.Location.deleteOne({ id });
+ const deleted = await context.lists.Location.deleteOne({ where: { id } });
expect(deleted).not.toBe(null);
expect(deleted!.id).toBe(id);
diff --git a/tests/api-tests/relationships/crud/one-to-many.test.ts b/tests/api-tests/relationships/crud/one-to-many.test.ts
index 434aa544188..2cd15ee43be 100644
--- a/tests/api-tests/relationships/crud/one-to-many.test.ts
+++ b/tests/api-tests/relationships/crud/one-to-many.test.ts
@@ -477,7 +477,7 @@ describe(`One-to-many relationships`, () => {
const { location, company } = await createCompanyAndLocation(context);
// Run the query to disconnect the location from company
- const _company = await context.lists.Company.deleteOne({ id: company.id });
+ const _company = await context.lists.Company.deleteOne({ where: { id: company.id } });
expect(_company?.id).toBe(company.id);
// Check the link has been broken
diff --git a/tests/api-tests/relationships/crud/one-to-one.test.ts b/tests/api-tests/relationships/crud/one-to-one.test.ts
index b3b42239d03..b5ca174b3b5 100644
--- a/tests/api-tests/relationships/crud/one-to-one.test.ts
+++ b/tests/api-tests/relationships/crud/one-to-one.test.ts
@@ -922,7 +922,7 @@ describe(`One-to-one relationships`, () => {
const { location, company } = await createCompanyAndLocation(context);
// Run the query to disconnect the location from company
- const _company = await context.lists.Company.deleteOne({ id: company.id });
+ const _company = await context.lists.Company.deleteOne({ where: { id: company.id } });
expect(_company?.id).toBe(company.id);
// Check the link has been broken
@@ -939,7 +939,7 @@ describe(`One-to-one relationships`, () => {
const { location, company } = await createLocationAndCompany(context);
// Run the query to disconnect the location from company
- const _location = await context.lists.Location.deleteOne({ id: location.id });
+ const _location = await context.lists.Location.deleteOne({ where: { id: location.id } });
expect(_location?.id).toBe(location.id);
// Check the link has been broken
diff --git a/tests/api-tests/relationships/nested-mutations/two-way-backreference/to-many.test.ts b/tests/api-tests/relationships/nested-mutations/two-way-backreference/to-many.test.ts
index aa4a0b9b164..5dd19d2855e 100644
--- a/tests/api-tests/relationships/nested-mutations/two-way-backreference/to-many.test.ts
+++ b/tests/api-tests/relationships/nested-mutations/two-way-backreference/to-many.test.ts
@@ -351,7 +351,7 @@ test(
compareIds(teacher2.students, [student1, student2]);
// Run the query to delete the student
- await context.lists.Student.deleteOne({ id: student1.id });
+ await context.lists.Student.deleteOne({ where: { id: student1.id } });
teacher1 = await getTeacher(context, teacher1.id);
teacher2 = await getTeacher(context, teacher2.id);
student1 = await getStudent(context, student1.id);
diff --git a/tests/examples-smoke-tests/custom-admin-ui-pages.test.ts b/tests/examples-smoke-tests/custom-admin-ui-pages.test.ts
new file mode 100644
index 00000000000..408baf40399
--- /dev/null
+++ b/tests/examples-smoke-tests/custom-admin-ui-pages.test.ts
@@ -0,0 +1,20 @@
+import { Browser, Page } from 'playwright';
+import { exampleProjectTests } from './utils';
+
+exampleProjectTests('custom-admin-ui-pages', browserType => {
+ let browser: Browser = undefined as any;
+ let page: Page = undefined as any;
+ beforeAll(async () => {
+ browser = await browserType.launch();
+ page = await browser.newPage();
+ await page.goto('http://localhost:3000');
+ });
+ test('Load list', async () => {
+ await page.goto('http://localhost:3000/custom-page');
+ const content = await page.textContent('body h1');
+ expect(content).toBe('Hello this is a custom page');
+ });
+ afterAll(async () => {
+ await browser.close();
+ });
+});
diff --git a/tests/test-projects/basic/schema.graphql b/tests/test-projects/basic/schema.graphql
index 55380b7c1d2..30beff9bc69 100644
--- a/tests/test-projects/basic/schema.graphql
+++ b/tests/test-projects/basic/schema.graphql
@@ -179,14 +179,14 @@ type Mutation {
createTasks(data: [TasksCreateInput]): [Task]
updateTask(id: ID!, data: TaskUpdateInput): Task
updateTasks(data: [TaskUpdateArgs]): [Task]
- deleteTask(id: ID!): Task
- deleteTasks(ids: [ID!]): [Task]
+ deleteTask(where: TaskWhereUniqueInput!): Task
+ deleteTasks(where: [TaskWhereUniqueInput!]!): [Task]
createPerson(data: PersonCreateInput): Person
createPeople(data: [PeopleCreateInput]): [Person]
updatePerson(id: ID!, data: PersonUpdateInput): Person
updatePeople(data: [PersonUpdateArgs]): [Person]
- deletePerson(id: ID!): Person
- deletePeople(ids: [ID!]): [Person]
+ deletePerson(where: PersonWhereUniqueInput!): Person
+ deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
}
type Query {
diff --git a/yarn.lock b/yarn.lock
index 00bacc3ebf6..79bf3cd837e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3881,7 +3881,7 @@ assert@2.0.0:
object-is "^1.0.1"
util "^0.12.0"
-assert@^1.1.1, assert@^1.4.1:
+assert@^1.1.1:
version "1.5.0"
resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==