From 42268ee72707e94a6197607d24534a438b748649 Mon Sep 17 00:00:00 2001 From: Mitchell Hamilton Date: Tue, 21 Sep 2021 09:34:27 +1000 Subject: [PATCH] Json field updates (#6607) --- .changeset/curly-apples-thank.md | 5 ++ docs/pages/docs/apis/fields.mdx | 5 +- examples/json/README.md | 13 +++-- examples/json/schema.ts | 2 +- .../keystone/src/fields/types/json/index.ts | 48 ++++++++++++------- .../fields/types/json/tests/test-fixtures.ts | 6 +-- .../src/fields/types/json/views/index.tsx | 9 +++- 7 files changed, 56 insertions(+), 32 deletions(-) create mode 100644 .changeset/curly-apples-thank.md diff --git a/.changeset/curly-apples-thank.md b/.changeset/curly-apples-thank.md new file mode 100644 index 00000000000..eea39c10443 --- /dev/null +++ b/.changeset/curly-apples-thank.md @@ -0,0 +1,5 @@ +--- +'@keystone-next/keystone': major +--- + +Removed `isRequired` and `defaultValue` can no longer be dynamic in the `json` field. If you were using `isRequired`, the same behaviour can be re-created with the `validateInput` hook. diff --git a/docs/pages/docs/apis/fields.mdx b/docs/pages/docs/apis/fields.mdx index c3d46eee475..e0f254d18a5 100644 --- a/docs/pages/docs/apis/fields.mdx +++ b/docs/pages/docs/apis/fields.mdx @@ -223,7 +223,8 @@ export default config({ A `json` field represents a JSON blob. Currently the `json` field is non-orderable and non-filterable. -- `isRequired` (default: `false`): If `true` then this field can never be set to `null`. +- `defaultValue` (default: `null`): Can be set to any JSON value. + This value will be used for the field when creating items if no explicit value is set. ```typescript import { config, list } from '@keystone-next/keystone'; @@ -234,7 +235,7 @@ export default config({ ListName: list({ fields: { fieldName: json({ - isRequired: true, + defaultValue: { something: true }, }), /* ... */ }, diff --git a/examples/json/README.md b/examples/json/README.md index 22feef57fb2..63e274a25b5 100644 --- a/examples/json/README.md +++ b/examples/json/README.md @@ -20,14 +20,13 @@ You can also access a Apollo Sandbox at [localhost:3000/api/graphql](http://loca ## Features -This example implements a `Packages` list. In this field we specify a `pkgjson` field which is a _required_ [`json` field type](https://keystonejs.com/docs/apis/fields#json). +This example implements a `Packages` list. In this field we specify a `pkgjson` field which is a [`json` field type](https://keystonejs.com/docs/apis/fields#json). This accepts any valid JSON including: -- string node -- number node -- array node -- object node - -As this is required, both inputting `null` in the Admin UI as well as leaving the input empty are not accepted. However try removing the `isRequired` config option from the field config. In doing so, you'll notice that both inputting the string `null` as well as an empty field will result in a `null` database value being stored. +- string +- number +- array +- object +- null The JSON field type stores its value in the `jsonb` format, as specified by Prisma. However if `sqlite` is specified as the database type instead, then the value is stored as a `string`. diff --git a/examples/json/schema.ts b/examples/json/schema.ts index 4febb3863af..381c55483c4 100644 --- a/examples/json/schema.ts +++ b/examples/json/schema.ts @@ -5,7 +5,7 @@ export const lists = { Package: list({ fields: { label: text({ isRequired: true }), - pkgjson: json({ isRequired: true }), + pkgjson: json(), isPrivate: checkbox(), ownedBy: relationship({ ref: 'Person.packages', many: false }), }, diff --git a/packages/keystone/src/fields/types/json/index.ts b/packages/keystone/src/fields/types/json/index.ts index 5956278e0d4..61b433351f4 100644 --- a/packages/keystone/src/fields/types/json/index.ts +++ b/packages/keystone/src/fields/types/json/index.ts @@ -1,6 +1,5 @@ import { BaseGeneratedListTypes, - FieldDefaultValue, JSONValue, FieldTypeFunc, CommonFieldConfig, @@ -11,14 +10,12 @@ import { resolveView } from '../../resolve-view'; export type JsonFieldConfig = CommonFieldConfig & { - defaultValue?: FieldDefaultValue; - isRequired?: boolean; + defaultValue?: JSONValue; }; export const json = ({ - isRequired, - defaultValue, + defaultValue = null, ...config }: JsonFieldConfig = {}): FieldTypeFunc => meta => { @@ -26,17 +23,34 @@ export const json = throw Error("isIndexed: 'unique' is not a supported option for field type json"); } - return jsonFieldTypePolyfilledForSQLite(meta.provider, { - ...config, - input: { - create: { arg: graphql.arg({ type: graphql.JSON }) }, - update: { arg: graphql.arg({ type: graphql.JSON }) }, - }, - output: graphql.field({ type: graphql.JSON }), - views: resolveView('json/views'), - __legacy: { - defaultValue, - isRequired, + const resolve = (val: JSONValue | undefined) => + val === null && meta.provider === 'postgresql' ? 'DbNull' : val; + + return jsonFieldTypePolyfilledForSQLite( + meta.provider, + { + ...config, + input: { + create: { + arg: graphql.arg({ type: graphql.JSON }), + resolve(val) { + return resolve(val === undefined ? defaultValue : val); + }, + }, + update: { arg: graphql.arg({ type: graphql.JSON }), resolve }, + }, + output: graphql.field({ type: graphql.JSON }), + views: resolveView('json/views'), + getAdminMeta: () => ({ defaultValue }), }, - }); + { + default: + defaultValue === null + ? undefined + : { + kind: 'literal', + value: JSON.stringify(defaultValue), + }, + } + ); }; diff --git a/packages/keystone/src/fields/types/json/tests/test-fixtures.ts b/packages/keystone/src/fields/types/json/tests/test-fixtures.ts index effc4d5dc96..5af49f1cff0 100644 --- a/packages/keystone/src/fields/types/json/tests/test-fixtures.ts +++ b/packages/keystone/src/fields/types/json/tests/test-fixtures.ts @@ -1,5 +1,4 @@ import { json } from '..'; -import { KeystoneContext } from '../../../../types'; export const name = 'Json'; export const typeFunction = json; @@ -10,18 +9,19 @@ export const exampleValue2 = () => ({ b: [], }); export const supportsUnique = false; +export const skipRequiredTest = true; export const fieldName = 'testField'; export const getTestFields = () => ({ testField: json() }); -export const initItems = (_: any, context: KeystoneContext) => { +export const initItems = () => { return [ { name: 'a', testField: { a: [] } }, { name: 'b', testField: { b: 'string' } }, { name: 'c', testField: { c: 42 } }, { name: 'd', testField: { d: { i: 25 } } }, { name: 'e', testField: { e: null } }, - { name: 'f', testField: context.prisma.DbNull }, + { name: 'f', testField: null }, { name: 'g' }, ]; }; diff --git a/packages/keystone/src/fields/types/json/views/index.tsx b/packages/keystone/src/fields/types/json/views/index.tsx index 00f8cc401e2..6bd639946fb 100644 --- a/packages/keystone/src/fields/types/json/views/index.tsx +++ b/packages/keystone/src/fields/types/json/views/index.tsx @@ -9,6 +9,7 @@ import { FieldController, FieldControllerConfig, FieldProps, + JSONValue, } from '../../../../types'; import { CellContainer, CellLink } from '../../../../admin-ui/components'; @@ -26,6 +27,7 @@ export const Field = ({ {onChange ? (