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

Change isUnique: true to isIndexed: 'unique' in fields #6437

Merged
merged 2 commits into from
Aug 31, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 8 additions & 0 deletions .changeset/quiet-tables-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@keystone-next/keystone': major
'@keystone-next/auth': patch
'@keystone-next/cloudinary': patch
'@keystone-next/fields-document': patch
Comment on lines +3 to +5
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are patch because they're only changes to error messages

---

Changed `isUnique: true` config in fields to `isIndexed: 'unique'`
4 changes: 2 additions & 2 deletions docs/pages/docs/apis/auth.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default withAuth(
lists: createSchema({
User: list({
fields: {
email: text({ isUnique: true }),
email: text({ isIndexed: 'unique', isFilterable: true }),
password: password(),
isAdmin: checkbox(),
},
Expand All @@ -62,7 +62,7 @@ The `createAuth` function must be used in conjunction with a [session](./session
The core functionality of the authentication system provides a GraphQL mutation to authenticate a user and then start a session, and a sign in page in the Admin UI.

- `listKey`: The name of the list to authenticate against.
- `identityField`: The name of the field to use as an identity field. This field must have `{ isUnique: true }` set.
- `identityField`: The name of the field to use as an identity field. This field must have `{ isIndexed: 'unique', isFilterable: true }` set.
- `secretField`: The name of the field to use as a secret. This field must be a `password()` field type.

```typescript
Expand Down
40 changes: 26 additions & 14 deletions docs/pages/docs/apis/fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,9 @@ Options:
`context` is a [`KeystoneContext`](./context) object.
`originalInput` is an object containing the data passed in to the `create` mutation.
- `isRequired` (default: `false`): If `true` then this field can never be set to `null`.
- `isUnique` (default: `false`): If `true` then all values of this field must be unique. This will also allow you to uniquely filter by this field in the GraphQL API.
- `isIndexed` (default: `false`)
- If `true` then this field will be indexed by the database.
- If `'unique'` then all values of this field must be unique.

```typescript
import { config, createSchema, list } from '@keystone-next/keystone';
Expand All @@ -188,7 +190,7 @@ export default config({
fieldName: integer({
defaultValue: 0,
isRequired: true,
isUnique: true,
isIndexed: 'unique',
}),
/* ... */
},
Expand Down Expand Up @@ -237,7 +239,9 @@ Options:
`context` is a [`KeystoneContext`](./context) object.
`originalInput` is an object containing the data passed in to the `create` mutation.
- `isRequired` (default: `false`): If `true` then this field can never be set to `null`.
- `isUnique` (default: `false`): If `true` then all values of this field must be unique.
- `isIndexed` (default: `false`)
- If `true` then this field will be indexed by the database.
- If `'unique'` then all values of this field must be unique.

```typescript
import { config, createSchema, list } from '@keystone-next/keystone';
Expand All @@ -250,7 +254,7 @@ export default config({
fieldName: float({
defaultValue: 3.14159,
isRequired: true,
isUnique: true,
isIndexed: 'unique',
}),
/* ... */
},
Expand All @@ -274,7 +278,9 @@ Options:
- `precision` (default: `18`): Maximum number of digits that are present in the number.
- `scale` (default: `4`): Maximum number of decimal places.
- `isRequired` (default: `false`): If `true` then this field can never be set to `null`.
- `isUnique` (default: `false`): If `true` then all values of this field must be unique.
- `isIndexed` (default: `false`)
- If `true` then this field will be indexed by the database.
- If `'unique'` then all values of this field must be unique.

```typescript
import { config, createSchema, list } from '@keystone-next/keystone';
Expand All @@ -289,7 +295,7 @@ export default config({
precision: 12,
scale: 3,
isRequired: true,
isUnique: true,
isIndexed: 'unique',
}),
/* ... */
},
Expand Down Expand Up @@ -354,7 +360,9 @@ Options:
`context` is a [`KeystoneContext`](./context) object.
`originalInput` is an object containing the data passed in to the `create` mutation.
- `isRequired` (default: `false`): If `true` then this field can never be set to `null`.
- `isUnique` (default: `false`): If `true` then all values of this field must be unique.
- `isIndexed` (default: `false`)
- If `true` then this field will be indexed by the database.
- If `'unique'` then all values of this field must be unique.
- `ui` (default: `{ displayMode: 'select' }`): Configures the display mode of the field in the Admin UI.
Can be one of `['select', 'segmented-control']`.

Expand All @@ -374,7 +382,7 @@ export default config({
],
defaultValue: '...',
isRequired: true,
isUnique: true,
isIndexed: 'unique',
ui: { displayMode: 'select' },
}),
/* ... */
Expand All @@ -397,7 +405,9 @@ Options:
`context` is a [`KeystoneContext`](./context) object.
`originalInput` is an object containing the data passed in to the `create` mutation.
- `isRequired` (default: `false`): If `true` then this field can never be set to `null`.
- `isUnique` (default: `false`): If `true` then all values of this field must be unique. This will also allow you to uniquely filter by this field in the GraphQL API.
- `isIndexed` (default: `false`)
- If `true` then this field will be indexed by the database.
- If `'unique'` then all values of this field must be unique.
- `ui` (default: `{ displayMode: 'input' }`): Configures the display mode of the field in the Admin UI.
Can be one of `['input', 'textarea']`.

Expand All @@ -412,7 +422,7 @@ export default config({
fieldName: text({
defaultValue: '...',
isRequired: true,
isUnique: true,
isIndexed: 'unique',
ui: { displayMode: 'textarea' },
}),
/* ... */
Expand All @@ -436,7 +446,9 @@ Options:
`context` is a [`KeystoneContext`](./context) object.
`originalInput` is an object containing the data passed in to the `create` mutation.
- `isRequired` (default: `false`): If `true` then this field can never be set to `null`.
- `isUnique` (default: `false`): If `true` then all values of this field must be unique.
- `isIndexed` (default: `false`)
- If `true` then this field will be indexed by the database.
- If `'unique'` then all values of this field must be unique.

```typescript
import { config, createSchema, list } from '@keystone-next/keystone';
Expand All @@ -449,7 +461,7 @@ export default config({
fieldName: timestamp({
defaultValue: '1970-01-01T00:00:00.000Z',
isRequired: true,
isUnique: true,
isIndexed: 'unique',
}),
/* ... */
},
Expand Down Expand Up @@ -537,7 +549,7 @@ Options:

- `defaultValue`
- `isRequired`
- `isUnique`
- `isIndexed`

```typescript
import { config, createSchema, list } from '@keystone-next/keystone';
Expand All @@ -550,7 +562,7 @@ export default config({
fieldName: autoIncrement({
defaultValue: 0,
isRequired: true,
isUnique: true,
isIndexed: 'unique',
}),
/* ... */
},
Expand Down
6 changes: 2 additions & 4 deletions docs/pages/docs/guides/custom-fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,12 @@ export type MyIntFieldConfig<TGeneratedListTypes extends BaseGeneratedListTypes>
CommonFieldConfig<TGeneratedListTypes> & {
defaultValue?: FieldDefaultValue<number, TGeneratedListTypes>;
isRequired?: boolean;
isIndexed?: boolean;
isUnique?: boolean;
isIndexed?: boolean | 'unique';
};

export const myInt =
<TGeneratedListTypes extends BaseGeneratedListTypes>({
isIndexed,
isUnique,
isRequired,
defaultValue,
...config
Expand All @@ -56,7 +54,7 @@ export const myInt =
kind: 'scalar',
mode: 'optional',
scalar: 'Int',
index: isIndexed ? 'index' : isUnique ? 'unique' : undefined,
index: isIndexed === true ? 'index' : isIndexed || undefined,
})({
...config,
input: {
Expand Down
4 changes: 2 additions & 2 deletions docs/pages/docs/guides/virtual-fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ To achieve this, rather than passing in `graphql.field({ ... })` as the `field`
The argument `lists` contains the type information for all of the Keystone lists.
In our case, we want the output type of the `Post` list, so we specify `type: lists.Post.types.output`.

```
```ts
export const lists = createSchema({
Post: list({
fields: {
Expand All @@ -274,7 +274,7 @@ export const lists = createSchema({
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
posts: relationship({ ref: 'Post.author', many: true }),
latestPost: virtual({
field: lists =>
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ export const lists = createSchema({
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
password: password(),
posts: relationship({ ref: 'Post.author', many: true }),
},
Expand Down
2 changes: 1 addition & 1 deletion examples-staging/assets-cloud/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const lists = createSchema({
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
posts: relationship({ ref: 'Post.author', many: true }),
},
}),
Expand Down
2 changes: 1 addition & 1 deletion examples-staging/assets-local/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const lists = createSchema({
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
posts: relationship({ ref: 'Post.author', many: true }),
},
}),
Expand Down
2 changes: 1 addition & 1 deletion examples-staging/auth/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const lists = createSchema({
// The user's name
name: text({ isRequired: true }),
// The user's email address, used as the identity field for auth
email: text({ isRequired: true, isUnique: true, isFilterable: true }),
email: text({ isRequired: true, isIndexed: 'unique', isFilterable: true }),
// The user's password, used as the secret field for auth
password: password({
access: {
Expand Down
2 changes: 1 addition & 1 deletion examples-staging/basic/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const lists = createSchema({
/** The user's first and last name. */
name: text({ isRequired: true }),
/** Email is used to log into the system. */
email: text({ isRequired: true, isUnique: true, isFilterable: true }),
email: text({ isRequired: true, isIndexed: 'unique', isFilterable: true }),
/** Avatar upload for the users profile, stored locally */
avatar: image(),
attachment: file(),
Expand Down
2 changes: 1 addition & 1 deletion examples-staging/ecommerce/schemas/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const User = list({
},
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true, isFilterable: true }),
email: text({ isRequired: true, isIndexed: 'unique', isFilterable: true }),
password: password(),
cart: relationship({
ref: 'CartItem.user',
Expand Down
2 changes: 1 addition & 1 deletion examples-staging/graphql-api-endpoint/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const lists = createSchema({
},
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true, isFilterable: true }),
email: text({ isRequired: true, isIndexed: 'unique', isFilterable: true }),
password: password(),
posts: relationship({ ref: 'Post.author', many: true }),
},
Expand Down
2 changes: 1 addition & 1 deletion examples-staging/roles/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export const lists = createSchema({
/* The name of the user */
name: text({ isRequired: true }),
/* The email of the user, used to sign in */
email: text({ isRequired: true, isUnique: true, isFilterable: true }),
email: text({ isRequired: true, isIndexed: 'unique', isFilterable: true }),
/* The password of the user */
password: password({
isRequired: true,
Expand Down
2 changes: 1 addition & 1 deletion examples/blog/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const lists = createSchema({
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
posts: relationship({ ref: 'Post.author', many: true }),
noRead: text({ graphql: { omit: ['read'] } }),
noUpdate: text({ graphql: { omit: ['update'] } }),
Expand Down
2 changes: 1 addition & 1 deletion examples/custom-field/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const lists = createSchema({
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
posts: relationship({ ref: 'Post.author', many: true }),
},
}),
Expand Down
19 changes: 2 additions & 17 deletions examples/custom-field/stars-field/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ export type StarsFieldConfig<TGeneratedListTypes extends BaseGeneratedListTypes>
CommonFieldConfig<TGeneratedListTypes> & {
defaultValue?: FieldDefaultValue<number, TGeneratedListTypes>;
isRequired?: boolean;
isUnique?: boolean;
isIndexed?: boolean;
isIndexed?: boolean | 'unique';
maxStars?: number;
};

export const stars =
<TGeneratedListTypes extends BaseGeneratedListTypes>({
isIndexed,
isUnique,
isRequired,
defaultValue,
maxStars = 5,
Expand All @@ -38,7 +36,7 @@ export const stars =
kind: 'scalar',
mode: 'optional',
scalar: 'Int',
index: getIndexType({ isIndexed, isUnique }),
index: isIndexed === true ? 'index' : isIndexed || undefined,
})({
// this passes through all of the common configuration like access control and etc.
...config,
Expand Down Expand Up @@ -104,16 +102,3 @@ export const stars =
defaultValue,
},
});

function getIndexType({
isIndexed,
isUnique,
}: {
isIndexed?: boolean;
isUnique?: boolean;
}): undefined | 'index' | 'unique' {
if (isUnique && isIndexed) {
throw new Error('Only one of isUnique and isIndexed can be passed to field types');
}
return isIndexed ? 'index' : isUnique ? 'unique' : undefined;
}
4 changes: 2 additions & 2 deletions examples/document-field/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const lists = createSchema({
Post: list({
fields: {
title: text({ isRequired: true }),
slug: text({ isRequired: true, isUnique: true }),
slug: text({ isRequired: true, isIndexed: 'unique' }),
status: select({
dataType: 'enum',
options: [
Expand Down Expand Up @@ -43,7 +43,7 @@ export const lists = createSchema({
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
posts: relationship({ ref: 'Post.author', many: true }),
bio: document({
// We want to constrain the formatting in Author bios to a limited set of options.
Expand Down
2 changes: 1 addition & 1 deletion examples/extend-graphql-schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const lists = createSchema({
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
posts: relationship({ ref: 'Post.author', many: true }),
},
}),
Expand Down
2 changes: 1 addition & 1 deletion examples/testing/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const lists = createSchema({
Person: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
password: password({ isRequired: true }),
tasks: relationship({ ref: 'Task.assignedTo', many: true }),
},
Expand Down
2 changes: 1 addition & 1 deletion examples/virtual-field/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const lists = createSchema({
Author: list({
fields: {
name: text({ isRequired: true }),
email: text({ isRequired: true, isUnique: true }),
email: text({ isRequired: true, isIndexed: 'unique' }),
posts: relationship({ ref: 'Post.author', many: true }),
// A virtual field which returns a type derived from a Keystone list.
latestPost: virtual({
Expand Down
2 changes: 1 addition & 1 deletion examples/with-auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ We add two new fields, `email` and `password`, to the `Person` list.
These are used as our _identity_ and _secret_ fields for login.

```typescript
email: text({ isRequired: true, isUnique: true }),
email: text({ isRequired: true, isIndexed: 'unique', isFilterable: true }),
password: password({ isRequired: true }),
```

Expand Down
Loading