Skip to content

Commit

Permalink
fix: truthy hidden and readOnly fields should not be assistable
Browse files Browse the repository at this point in the history
  • Loading branch information
snorrees committed Jul 7, 2023
1 parent 3e0cd5d commit 1a33e01
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 8 deletions.
3 changes: 3 additions & 0 deletions plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ The following types are not supported, and behave as excluded types:
* [Image](https://www.sanity.io/docs/image-type) (supported when image has custom fields)
* [File](https://www.sanity.io/docs/file-type) (never supported, even when file has custom fields)

Types and fields with `hidden` or `readonly` with a truthy value (`true` or `function`) are not supported.
(Hidden and readOnly fields can be referenced in instructions still)

Fields with these types will not be changed by the assistant, do not have AI Assist actions, and cannot be referenced in instructions.

Objects where all fields are excluded or unsupported and arrays where all member types are excluded or unsupported
Expand Down
2 changes: 1 addition & 1 deletion plugin/src/assistInspector/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function getFieldRefs(
const fields =
field.type.jsonType === 'object' ? getFieldRefs(field.type, fieldRef, depth + 1) : []

if (!isAssistSupported(field.type)) {
if (!isAssistSupported(field.type, true)) {
return fields
}
return [fieldRef, ...fields]
Expand Down
6 changes: 4 additions & 2 deletions plugin/src/fieldActions/assistFieldActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ function node(node: DocumentFieldActionItem | DocumentFieldActionGroup) {
export const assistFieldActions: DocumentFieldAction = {
name: 'sanity-assist-actions',
useAction(props) {
const assistSupported = useAssistSupported(props.path, props.schemaType)
const {schemaType} = props
const assistSupported = useAssistSupported(props.path, schemaType)

const isDocumentLevel = props.path.length === 0

const {
Expand All @@ -47,7 +49,7 @@ export const assistFieldActions: DocumentFieldAction = {
// conditional hook _should_ be safe here since the logical path will be stable
isDocumentLevel
? // eslint-disable-next-line react-hooks/rules-of-hooks
useAssistDocumentContextValue(props.documentId, props.schemaType as ObjectSchemaType)
useAssistDocumentContextValue(props.documentId, schemaType as ObjectSchemaType)
: // eslint-disable-next-line react-hooks/rules-of-hooks
useAssistDocumentContext()

Expand Down
20 changes: 15 additions & 5 deletions plugin/src/helpers/assistSupported.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,40 @@ export function isSchemaAssistEnabled(type: SchemaType) {
return !(type.options as AssistOptions | undefined)?.aiWritingAssistance?.exclude
}

export function isAssistSupported(type: SchemaType) {
export function isAssistSupported(type: SchemaType, allowReadonlyHidden = false) {
if (!isSchemaAssistEnabled(type)) {
return false
}

if (isUnsupportedType(type)) {
if (isDisabled(type, allowReadonlyHidden)) {
return false
}

if (type.jsonType === 'array') {
const unsupportedArray = type.of.every((t) => isUnsupportedType(t))
const unsupportedArray = type.of.every((t) => isDisabled(t, allowReadonlyHidden))
return !unsupportedArray
}

if (type.jsonType === 'object') {
const unsupportedObject = type.fields.every((field) => isUnsupportedType(field.type))
const unsupportedObject = type.fields.every((field) =>
isDisabled(field.type, allowReadonlyHidden)
)
return !unsupportedObject
}
return true
}

function isUnsupportedType(type: SchemaType) {
function isDisabled(type: SchemaType, allowReadonlyHidden: boolean) {
const readonlyHidden = !!type.readOnly || !!type.hidden
return (
!isSchemaAssistEnabled(type) ||
isUnsupportedType(type) ||
(!allowReadonlyHidden && readonlyHidden)
)
}

function isUnsupportedType(type: SchemaType) {
return (
type.jsonType === 'number' ||
type.name === 'sanity.imageCrop' ||
type.name === 'sanity.imageHotspot' ||
Expand Down
26 changes: 26 additions & 0 deletions plugin/src/schemas/serialize/serializeSchema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,30 @@ describe('serializeSchema', () => {
{name: 'list', title: 'String', type: 'string', values: ['a', 'b']},
])
})

test('should exclude truthy hidden and readonly', () => {
const schema = Schema.compile({
name: 'test',
types: [
{
type: 'document',
name: 'article',
fields: [
{type: 'string', name: 'title', hidden: () => true},
{type: 'some', name: 'some'},
],
},
defineType({
type: 'object',
name: 'some',
readOnly: true,
fields: [{type: 'string', name: 'title'}],
}),
],
})

const serializedTypes = serializeSchema(schema, {leanFormat: true})

expect(serializedTypes).toEqual([])
})
})
2 changes: 2 additions & 0 deletions plugin/src/schemas/serialize/serializeSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function serializeSchema(schema: Schema, options?: Options): SerializedSc
.filter((t) => !(hiddenTypes.includes(t) || t.startsWith('sanity.')))
.map((t) => schema.get(t))
.filter((t): t is SchemaType => !!t)
.filter((t) => !t.hidden && !t.readOnly)
.map((t) => getSchemaStub(t, schema, options))
.filter((t) => {
if ('to' in t && t.to && !t.to.length) {
Expand Down Expand Up @@ -106,6 +107,7 @@ function serializeFields(
return schemaType.fields
.filter((f) => !['sanity.imageHotspot', 'sanity.imageCrop'].includes(f.type?.name ?? ''))
.filter((f) => isAssistSupported(f.type))
.filter((f) => !f.type.hidden && !f.type.readOnly)
.map((field) => serializeMember(schema, field.type, field.name, options))
}

Expand Down
36 changes: 36 additions & 0 deletions studio/schemas/mockArticle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -374,5 +374,41 @@ export const mockArticle = defineType({
}),
],
}),
defineField({
type: 'image',
name: 'inlineImage',
title: 'Image (inline type)',
fields: [
defineField({
type: 'string',
name: 'altText',
title: 'Alt text',
}),
],
options: {
imagePromptField: 'altText',
},
}),
defineField({
type: 'object',
name: 'inlineObject',
title: 'Inline object',
fields: [
defineField({
type: 'string',
name: 'readOnly',
readOnly: true,
}),
defineField({
type: 'string',
name: 'hidden',
hidden: true,
}),
defineField({
type: 'string',
name: 'Write',
}),
],
}),
],
})

0 comments on commit 1a33e01

Please sign in to comment.