@kbn/io-ts-utils: strictKeys: validate objects in arrays (#191607)

Makes sure keys from plain objects in arrays are validated as well.

---------

Co-authored-by: Søren Louv-Jansen <soren.louv@elastic.co>
Co-authored-by: Carlos Crespo <crespocarlos@users.noreply.github.com>
This commit is contained in:
Dario Gieselaar 2024-08-30 08:00:42 +02:00 committed by GitHub
parent 1db47f0efb
commit 8ade3da14d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 67 additions and 6 deletions

View file

@ -206,7 +206,38 @@ describe('strictKeysRt', () => {
{
type: t.array(t.type({ foo: t.string })),
passes: [[{ foo: 'bar' }], [{ foo: 'baz' }, { foo: 'bar' }]],
fails: [],
fails: [{ foo: 'bar', bar: 'foo' }],
},
{
type: t.type({
nestedArray: t.array(
t.type({
bar: t.string,
})
),
}),
passes: [
{
nestedArray: [],
},
{
nestedArray: [
{
bar: 'foo',
},
],
},
],
fails: [
{
nestedArray: [
{
bar: 'foo',
foo: 'bar',
},
],
},
],
},
];

View file

@ -8,7 +8,7 @@
import * as t from 'io-ts';
import { either, isRight } from 'fp-ts/lib/Either';
import { difference, isPlainObject, forEach } from 'lodash';
import { difference, isPlainObject, forEach, isArray, castArray } from 'lodash';
import { MergeType } from '../merge_rt';
/*
@ -100,11 +100,31 @@ function getHandledKeys<T extends Record<string, unknown>>(
keys.handled.add(ownPrefix);
}
const processObject = (typeForObject: t.Mixed, objectToProcess: Record<string, unknown>) => {
const nextKeys = getHandledKeys(typeForObject, objectToProcess, ownPrefix);
nextKeys.all.forEach((k) => keys.all.add(k));
nextKeys.handled.forEach((k) => keys.handled.add(k));
};
if (isPlainObject(value)) {
handlingTypes.forEach((i) => {
const nextKeys = getHandledKeys(i, value as Record<string, unknown>, ownPrefix);
nextKeys.all.forEach((k) => keys.all.add(k));
nextKeys.handled.forEach((k) => keys.handled.add(k));
handlingTypes.forEach((typeAtIndex) => {
processObject(typeAtIndex, value as Record<string, unknown>);
});
}
if (isArray(value)) {
handlingTypes.forEach((typeAtIndex) => {
if (!isParsableType(typeAtIndex) || typeAtIndex._tag !== 'ArrayType') {
return;
}
const innerType = typeAtIndex.type;
castArray(value).forEach((valueAtIndex) => {
if (isPlainObject(valueAtIndex)) {
processObject(innerType, valueAtIndex as Record<string, unknown>);
}
});
});
}
});

View file

@ -115,6 +115,7 @@ export enum KnowledgeBaseType {
}
export interface ObservabilityAIAssistantScreenContextRequest {
starterPrompts?: StarterPrompt[];
screenDescription?: string;
data?: Array<{
name: string;

View file

@ -14,6 +14,7 @@ import {
type Message,
MessageRole,
type ObservabilityAIAssistantScreenContextRequest,
type StarterPrompt,
} from '../../common/types';
const serializeableRt = t.any;
@ -129,6 +130,12 @@ export const functionRt = t.intersection([
}),
]);
export const starterPromptRt: t.Type<StarterPrompt> = t.type({
title: t.string,
prompt: t.string,
icon: t.any,
});
export const screenContextRt: t.Type<ObservabilityAIAssistantScreenContextRequest> = t.partial({
description: t.string,
data: t.array(
@ -139,4 +146,6 @@ export const screenContextRt: t.Type<ObservabilityAIAssistantScreenContextReques
})
),
actions: t.array(functionRt),
screenDescription: t.string,
starterPrompts: t.array(starterPromptRt),
});