-
Notifications
You must be signed in to change notification settings - Fork 4
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
#134 into [email protected] 👹 add 'oneOf' parameter into descriptor #181
base: [email protected]
Are you sure you want to change the base?
Conversation
describe('plainEntitySchema: checkMode exclude', () => { | ||
test('Should return correct error path for descriptor (top and property level) without required value', () => { | ||
const incorrectTopLevelDescriptorBodyEntities = { | ||
test('Should return correct error path with descriptor schema checked before common record schema', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
По сути прошлый тест как раз это и смотрел, но было прям неочевидно, что он проверял - сделал более явно, что тест проверяет, мол descriptor schema должна проходить валидацию раньше record schema
export const extendedDiscriminatedUnion = <Discriminator extends string>( | ||
discriminator: Discriminator, | ||
options: [ | ||
ExtendedDiscriminatedUnionOption, | ||
ExtendedDiscriminatedUnionOption, | ||
...ExtendedDiscriminatedUnionOption[] | ||
] | ||
) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
По сути эта функция это просто адаптация этой функции из zod https://zod.dev/?id=discriminated-unions.
Т.е. так же принимается первым параметром название свойства, которое должно быть у проверяемого объекта и далее схемы, которые должны соответствовать каждому из возможных значений вышеупомянутого свойства - схемы это массив котрежей из 2 элементов, где первый элемент проверяет совпадение с выбранным свойством (discriminator), а второй элемент проверяет уже весь остальной объект (в случае, если первая схема была успешно провалидирована).
На самом деле супер просто все, это просто расширение обычного discriminatedUnion из zod, т.к. он в силу принципов своей работы вторым аргументом позволял передавать только массив z.object
-ов, а это жесткое ограничение, которое по сути заставляло бы нас писать ту валидацию, от которой мы и хотели уйти с помощью zod.
value: { | ||
key1: 'value1', | ||
key2: { nestedKey1: 'nestedValue1' } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Из-за oneOf теперь массивы так не парсятся
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Подскажи подробней, а как раньше было. Было тип пройдись по всему массиву и посмотри каждый элемент и тут был был equal для 0 элемента ?
Но теперь у нас есть oneOf и если не указать его в явную, то он будет сверять массив типо
Но тут филосовский вопрос, oneOf по дефалту сейчас false, что наверное правильно
if ( | ||
topLevelConvertedDescriptor.checkMode === 'exists' || | ||
topLevelConvertedDescriptor.checkMode === 'notExists' | ||
) { | ||
return resolveEntityValues({ | ||
checkMode: topLevelConvertedDescriptor.checkMode, | ||
actualValue: graphQLInput.variables | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Нужно явно проверять checkMode-ы без descriptorValue для типизации
export const CHECK_MODES = [ | ||
...CHECK_ACTUAL_VALUE_CHECK_MODES, | ||
...COMPARE_WITH_DESCRIPTOR_VALUE_CHECK_MODES, | ||
...CALCULATE_BY_DESCRIPTOR_VALUE_CHECK_MODES | ||
] as const satisfies readonly CheckMode[]; | ||
|
||
export const PLAIN_ENTITY_CHECK_MODES = [ | ||
...CHECK_ACTUAL_VALUE_CHECK_MODES, | ||
...COMPARE_WITH_DESCRIPTOR_ANY_VALUE_CHECK_MODES, | ||
'function' | ||
] as const satisfies readonly CheckMode[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Они просто не использовались нигде
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
По сути x instanceof RegExp
выполняет ту же самую роль поэтому не вижу смысла в этой функции. Мб раньше TS автоматически не сужал тип, но сейчас все норм
export type EntityDescriptor< | ||
Check extends CheckMode = CheckMode, | ||
Value = any | ||
> = Check extends CheckActualValueCheckMode | ||
? { checkMode: Check } | ||
: | ||
| { checkMode: Check; value: Value[]; oneOf: true } | ||
| { checkMode: Check; value: Value; oneOf?: false }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Теперь для CheckMode без descriptorValue типизация стала более явной
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тут самые большие изменения, но основных 2:
- Раньше по сути из-за необычной логики массивов требовалось проверять 9 комбинаций: каждый из actual и descriptor value мог быть примитивом, массивом, объектом, таким образом 3х3 = 9. Теперь же для нас без разницы, массив это или объект, то есть комбинаций становится 4 = 2х2. Нам без разницы, т.к. определение логики, сравнивать ли массив целиком или же только один из элементов, забрал на себя oneOf.
- Теперь, когда функция 'resolveEntityValues' вызывается, то мы смотрим какое значение имеет oneOf. Если true, то просто идем по массиву descriptorValue и ищем хотя бы 1 совпадение, если же oneOf=false или не представлен, то сравниваем целиком
|
||
return resolveEntityValues({ | ||
checkMode: topLevelConvertedDescriptor.checkMode, | ||
actualValue, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
я бы поставил первым параметром в объекте
@@ -50,8 +50,7 @@ export const createRestRoutes = ({ | |||
|
|||
const entries = Object.entries(entities) as Entries<Required<RestEntitiesByEntityName>>; | |||
return entries.every(([entityName, entityDescriptorOrValue]) => { | |||
const { checkMode, value: descriptorValue } = | |||
convertToEntityDescriptor(entityDescriptorOrValue); | |||
const topLevelConvertedDescriptor = convertToEntityDescriptor(entityDescriptorOrValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const entityDescriptor = convertToEntityDescriptor(entityDescriptorOrValue);
а почему вот такой бы нейминг просто не поддержать, мне кажется что top level вообще не обязателен
const actualValue = Object.keys(request.body).length ? request.body : undefined; | ||
return resolveEntityValues({ | ||
checkMode: 'equals', | ||
actualValue, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same про поставить выше
const actualEntity = flatten<any, any>(request[entityName]); | ||
|
||
// ✅ important: transform header keys to lower case because browsers send headers in lowercase | ||
const actualValue = | ||
actualEntity[entityName === 'headers' ? entityKey.toLowerCase() : entityKey]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// ✅ important: transform header keys to lower case because browsers send headers in lowercase
cosnt actualKey = entityName === 'headers' ? entityKey.toLowerCase() : entityKey
const actualValue = actualEntity[actualKey]
| ResolveEntityParamsWithEnabledOneOf | ||
| ResolveEntityParamsWithDisabledOneOf; | ||
|
||
export const resolveEntityValues = (params: ResolveEntityParams) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ResolveEntityValuesParams
interface ResolveEntityParamsWithEnabledOneOf { | ||
checkMode: Exclude<CheckMode, CheckActualValueCheckMode>; | ||
actualValue: any; | ||
descriptorValue: any[]; | ||
oneOf: true; | ||
} | ||
|
||
interface ResolveEntityParamsWithDisabledOneOf { | ||
checkMode: Exclude<CheckMode, CheckActualValueCheckMode>; | ||
actualValue: any; | ||
descriptorValue: any; | ||
oneOf?: false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
а разве нам нужно тут union, разве нам не пофиг если сделать вот так
interface ResolveEntityValuesParams {
checkMode: Exclude<CheckMode, CheckActualValueCheckMode>;
actualValue: any;
descriptorValue: any | any[];
oneOf?: boolean;
}
а уже снизу тут в коде один раз захоркодить as any[]
а то приходиться выше костылить true | false
// ✅ important: | ||
// recreate RegExp because 'g' flag can be cause of unexpected result | ||
// this is about https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex#avoiding_side_effects |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
а точно ли стоит такое писать ? просто в коде уже есть такой кейс
@@ -40,141 +43,91 @@ const checkFunction: CheckFunction = (checkMode, actualValue, descriptorValue?): | |||
throw new Error('Wrong checkMode'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
мб еще добавим значение сюда check mode, ну тип wrong checmode ${checkmode}
value: { | ||
key1: 'value1', | ||
key2: { nestedKey1: 'nestedValue1' } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Подскажи подробней, а как раньше было. Было тип пройдись по всему массиву и посмотри каждый элемент и тут был был equal для 0 элемента ?
Но теперь у нас есть oneOf и если не указать его в явную, то он будет сверять массив типо
Но тут филосовский вопрос, oneOf по дефалту сейчас false, что наверное правильно
No description provided.