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

Validators #5

Merged
merged 3 commits into from
Oct 11, 2023
Merged

Validators #5

merged 3 commits into from
Oct 11, 2023

Conversation

sr480
Copy link
Contributor

@sr480 sr480 commented Oct 2, 2023

  • Валидация Meta и Features
  • Добавил форматы ошибок валидаторов,
  • Добавил в cli команду validate

@sr480 sr480 force-pushed the validators branch 2 times, most recently from 0ad9187 to e1fdbbd Compare October 2, 2023 18:59
@@ -9,6 +9,7 @@
"dist"
],
"scripts": {
"start": "NODE_TLS_REJECT_UNAUTHORIZED=0 ts-node ./src/cli.ts",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

добавил команду для запуска без аргументов, запускаю проверку вот так: npm start -- validate


const meta = await loadMeta(yml.metaPath, projectPath);

const metaValidation = validateMeta(meta);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Отдельный набор ошибок по валидации Meta, тут же строю мапу для атрибутов-значений

Может стоит останавливать валидацию, если есть ошибки в Meta?

);

console.log(metaValidation);
console.log(featureValidation);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Хочу получить ревью, до того как буду делать красивый вывод ошибок

const component = attributes?.component.join("-");
const subComponent = attributes?.["sub-component"].join("-");
code = `${component}_${subComponent}`;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Пришлось переписать, так как в моих yml нет таких атрибутов

Copy link
Contributor

@dima117 dima117 Oct 3, 2023

Choose a reason for hiding this comment

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

я планировал этот код выпилить сегодня и брать значение только из поля code (сделать его обязательным)
через пару часов пришлю PR

attributeValuesMap: AttributeValuesMap
): ValidationError[] => {
const errors = new Array<ValidationError>();
const featureCodesUnique = new Map<string, Feature>();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Строю мап фичей по кодам, чтобы проверять дубликаты и указывать на фичу первым встретившимся кодом

| FeatureMissingAttributeError
| FeatureMissingAttributeValueError
| CodeError;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Не добавлял filePath в ошибки, так как он содержится в Feature


if (feature.attributes) {
for (const attribute in feature.attributes) {
let valuesSet = this.metaAttributeValues.get(attribute);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Нужно ли проверять валидность кода атрибута? Все-равно выпадет ошибка, так как он не будет найден в мета, а там идет проверка формата кода атрибута.

Copy link
Contributor

Choose a reason for hiding this comment

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

в месте использования валидность кода атрибута проверять не нужно — только в месте объявления атрибута

код значения нужно проверять, т.к. значения можно не объявлять в мете

): Promise<YamlFile> => {
basePath: string = CWD, // TODO: перенести в resolvePath
validationContext?: Validator
): Promise<YamlFile | undefined> => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

я решил возвращать undefined, если отловил ошибку. из-за этого снаружи приходится фильтровать файлы перед последующей обработкой


const projectData = processYamlFiles(yamls, meta);
const projectData = processYamlFiles(successYamls, meta);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Пришлось добавить код выбора только успешно декодированных yaml

'config'
);
} else {
throw error;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

если не переда кнтекст валидации, то валимся с исключением, так как некому собирать ошибки

Copy link
Contributor

Choose a reason for hiding this comment

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

предлагаю сделать контекст валидации обязательным параметром, чтобы логика работы sync и validate отличалась только наличием в конце операции выгрузки данных

};
} catch (error) {
if (validationContext) {
validationContext.registerLoaderError(error, relativePath, 'feature');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

тут аналогично, либо собираем ошибки в конексте валидации либо валимся с исключением

};
errors.push(featureCodeError);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

предлагаю вынести валидацию формата кода во всех сущностях на уровень проверки схемы

feature,
firstFeature: existingFeature,
};
errors.push(featureCodeDuplicateError);
Copy link
Contributor

Choose a reason for hiding this comment

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

код может повторяться несколько раз
лучше бы выдавать ошибку вида:
повторяется код <код> в файлах: [ ... ]

errors.push(missingAttribute);
continue;
}
for (const attributeValue of feature.attributes[attribute]) {
Copy link
Contributor

Choose a reason for hiding this comment

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

проверять наличие всех значений не нужно, т.к. частая ситуация, когда есть большое количество значений и они не описаны в конфиге — считаем, что это нормально и при выгрузке автоматом добавляем в базу недостающие занчения

с атрибутами так не прокатит, т.к. мы ссылаемся на атрибуты из других мест (например, из деревьев), поэтому проверяем наличие атрибутов

@@ -91,3 +94,5 @@ export const processYamlFiles = (

return { features, attributes, trees };
};

export * from './validators';
Copy link
Contributor

Choose a reason for hiding this comment

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

предлагаю все валидаторы вынести в отдельную папку validation на одном уровне с папками config и domain

@@ -21,8 +21,10 @@ export const cmdSync: CommandModule<{}, CommonOptions> = {
const yamls = await Promise.all(
files.map((path) => loadYaml(path, projectPath))
);
const successYamls = new Array<YamlFile>();
yamls.forEach(yaml => yaml && successYamls.push(yaml));
Copy link
Contributor

Choose a reason for hiding this comment

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

предлагаю написать

const successYamls = yamls.filter(Boolean)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

не получится, тогда нужно еще map делать, так как тип у массива останется (YamlFile | undefined)[]

:(

'config'
);
} else {
throw error;
Copy link
Contributor

Choose a reason for hiding this comment

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

предлагаю сделать контекст валидации обязательным параметром, чтобы логика работы sync и validate отличалась только наличием в конце операции выгрузки данных


if (feature.attributes) {
for (const attribute in feature.attributes) {
let valuesSet = this.metaAttributeValues.get(attribute);
Copy link
Contributor

Choose a reason for hiding this comment

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

в месте использования валидность кода атрибута проверять не нужно — только в месте объявления атрибута

код значения нужно проверять, т.к. значения можно не объявлять в мете


export class Validator {
private readonly featuresMap = new Map<string, Feature>();
private readonly metaAttributeValues = new Map<string, Set<string>>();
Copy link
Contributor

Choose a reason for hiding this comment

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

немного смущает, что featuresMap и metaAttributeValues находятся в полях класса, к ним есть обращения из методов, но в момент обращения эти значения могут быть не заполнены данными

предлагаю объявить их как локальные переменные методов и явно передавать во вложенные методы как параметры

Copy link
Contributor

Choose a reason for hiding this comment

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

т.к. validateMeta и validateFeatures используются ровно в одном месте и вызываются вместе, то предлагаю объединить их в один метод и сделать featuresMap и metaAttributeValues его локальными переменными

также этот метод можно вынести в родителя из processYamlFiles

также вместо параметров meta и fetures в него можно передавать один параметр типа ProjectData, который содержит всю ту же самую информацию (т.е. передать в него возвращаемое значение от метода processYamlFiles)

@sr480 sr480 force-pushed the validators branch 2 times, most recently from c49d2a8 to 18fcd04 Compare October 9, 2023 13:53
return content || {};
} catch (error) {
validationContext.registerLoaderError(error, filePath, 'config');
Copy link
Contributor

Choose a reason for hiding this comment

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

если не загрузилась мета, нужно падать с ошибкой, т.к. без этого точно не пройдет валидация остальных файлов

| 'description'
| 'title'
| 'group.title'
| 'group.description'
Copy link
Contributor

Choose a reason for hiding this comment

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

вроде бы у группы нельзя задать description

const links = LINK_LIKE.exec(value);
if (links) {
for (let link of links) {
link = link.substring(1);
Copy link
Contributor

Choose a reason for hiding this comment

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

если regexp изменится, то никак нельзя догадаться, что нужно пойти в это место и поправить логику
предлагаю сделать группу в regex и вместо substring выбирать группу + оставить комментарий об этом в месте, где описан regex

import { Assertion, AssertionGroup, Feature } from '../domain';
import { printError } from './renderer';

export type AttributeDuplicateError = {
Copy link
Contributor

Choose a reason for hiding this comment

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

предлагаю вынести валидатор в отдельный файл + вынести описание интерфейсов в файл model.ts (по аналогии с другими частям проекта), а в index.ts оставить только экспорт элементов, которые используются снаружи

@@ -50,14 +56,20 @@ export const applyJestReport = (
const parts = getKey(keyParts, assertionCtx, attributesCtx);
const fullName = getFullName(...parts);

console.log("process: \x1b[36m%s\x1b[0m", fullName);
console.log(`process: ${chalk.cyan(fullName)}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

предлагаю этот console.log убрать совсем — он был нужен, когда не было нормальной валидации, чтобы смотреть какие тесты не сматчились на фичи

const { yml, api, jest, projectPath } = await loadConfig(args.config);

const meta = await loadMeta(yml.metaPath, projectPath);
const meta = await loadMeta(validationContext, yml.metaPath, projectPath);
const files = await glob(yml.files, { cwd: projectPath });

const yamls = await Promise.all(
files.map((path) => loadYaml(path, projectPath))
Copy link
Contributor

Choose a reason for hiding this comment

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

кажется, нужно в loadYaml передать validationContext

@@ -1,50 +1,55 @@
import chalk from 'chalk';
import { ERROR_SEVERITY, ErrorSeverity, ValidationError, ValidationErrorTypes } from './models';
import { ERROR_SEVERITY, ErrorSeverity, ValidationError } from './models';

const renderError = (e: ValidationError): string => {
switch(e.type){
Copy link
Contributor

Choose a reason for hiding this comment

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

изменен формат вывода ошибок:

  • добавил разделение сообщений пустой строкой и яркий маркер начала нового сообщения
  • сделал начало сообщений на одном уровне (раньше начало сообщения зависело отдлины пути к файлу)
  • покрасил путь к файлу в серый (чтобы привлекал меньше внимания)
  • вместо нескольких цветов все значения вывожу одним цветом

}
}

const path = (p: string) => chalk.gray(p);
const strong = (p: string) => chalk.bold(p);
const val = (p: string) => chalk.cyan(p);
Copy link
Contributor

Choose a reason for hiding this comment

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

сделал семантические хелперы, чтобы при изменении формата он единообразно менялся во всех сообщениях

Copy link
Contributor Author

Choose a reason for hiding this comment

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

классно!

@@ -129,7 +128,8 @@ export class Validator {
): ValidationError[] {
const result = new Array<ValidationError>();
for (const attributeValue of attribute.values) {
const valueValidation = this.validateCode(attribute.code, filePath);
const valueValidation = this.validateCode(attributeValue.code, filePath);
Copy link
Contributor

Choose a reason for hiding this comment

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

была опечатка — вместо кода значения проверялся код атрибута

for (const tree of trees) {
const treeUnique = new Set<string>();
Copy link
Contributor

Choose a reason for hiding this comment

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

была опечатка — на каждом шаге цикла создавался новый set

};
errors.push(groupDuplicateError);
}
groupsUnique.add(assertionGroup.title);
Copy link
Contributor

Choose a reason for hiding this comment

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

выпилил эту проверку, т.к. список групп описывается как dictionary и если задублируются значения групп, то упадет проверка формата

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ага, я видел, что есть в yaml такое ограничение, но подумал оставить - вдруг формат yaml решим поменять, а для БД это критично

@@ -78,12 +78,7 @@ export type FeatureMissingLinkError = {
| 'assert.description';
link: string;
};
export type AssertionGroupDuplicateError = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Разве не стоит считать дубли групп ассертов ошибками?

Copy link
Contributor

Choose a reason for hiding this comment

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

по идее, это ошибка, но уже есть два места, которые её обрабатывают:

  • упадет разбор формата
  • на сервере при экспорте одинаковые группы склеятся в одну

кажется, если мы добавим еще одну проверку, то в реальной жизни она никогда не сработает (и если она сломается, мы об этом не узнаем)

Copy link
Contributor

Choose a reason for hiding this comment

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

т.е. суть предложения — не оставлять в проекте неиспользуемый код

}
}

const path = (p: string) => chalk.gray(p);
const strong = (p: string) => chalk.bold(p);
const val = (p: string) => chalk.cyan(p);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

классно!

};
errors.push(groupDuplicateError);
}
groupsUnique.add(assertionGroup.title);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ага, я видел, что есть в yaml такое ограничение, но подумал оставить - вдруг формат yaml решим поменять, а для БД это критично


try {
validationContext.setMetaFilePath(filePath);
Copy link
Contributor

Choose a reason for hiding this comment

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

выпилить setMetaFilePath?

return content || {};
} catch (error) {
printError(getLoaderError(error, filePath, 'config'));
throw 'Ошибка загрузки файла конфигурации';
Copy link
Contributor

Choose a reason for hiding this comment

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

давай, пожалуйста, в throw всегда будем передавать объект Error?

throw new Error('Ошибка загрузки файла конфигурации');

private metaFilePath = '';

get hasCriticalErrors(): boolean {
let hasError = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

предлагаю написать так:

let hasError = [
    ...loaderErrors,
    ...metaErrors,
    ...featureErrors,
    ...jestUnusedTests].some(e =>  ERROR_SEVERITY[e.type] === 'error');

@sr480 sr480 force-pushed the validators branch 2 times, most recently from 820a41f to 72eb499 Compare October 11, 2023 19:27
@dima117 dima117 merged commit 3defa72 into master Oct 11, 2023
@dima117 dima117 deleted the validators branch October 11, 2023 20:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants