Add object property validation in parser #369
This commit introduces stricter type validation across the application to reject objects with unexpected properties, enhancing the robustness and predictability of data handling. Changes include: - Implement a common utility to validate object types. - Refactor across various parsers and data handlers to utilize the new validations. - Update error messages for better clarity and troubleshooting.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/Common/ContextualError';
|
||||
import { Expression, type ExpressionEvaluator } from '../../Expression/Expression';
|
||||
import { createPositionFromRegexFullMatch, type ExpressionPositionFactory } from '../../Expression/ExpressionPositionFactory';
|
||||
import { createFunctionParameterCollection, type FunctionParameterCollectionFactory } from '../../../Function/Parameter/FunctionParameterCollectionFactory';
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { IExpressionsCompiler } from '@/application/Parser/Executable/Scrip
|
||||
import type { FunctionCall } from '@/application/Parser/Executable/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { FunctionCallCompilationContext } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
|
||||
import { ParsedFunctionCall } from '@/application/Parser/Executable/Script/Compiler/Function/Call/ParsedFunctionCall';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/Common/ContextualError';
|
||||
import type { ArgumentCompiler } from './ArgumentCompiler';
|
||||
|
||||
export class NestedFunctionArgumentCompiler implements ArgumentCompiler {
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
import type { FunctionCall } from '@/application/Parser/Executable/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { FunctionCallCompilationContext } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
|
||||
import type { CompiledCode } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Compiler/CompiledCode';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/Common/ContextualError';
|
||||
import { NestedFunctionArgumentCompiler } from './Argument/NestedFunctionArgumentCompiler';
|
||||
import type { SingleCallCompilerStrategy } from '../SingleCallCompilerStrategy';
|
||||
import type { ArgumentCompiler } from './Argument/ArgumentCompiler';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { NoEmptyLines } from '@/application/Parser/Executable/Script/Validation/
|
||||
import { NoDuplicatedLines } from '@/application/Parser/Executable/Script/Validation/Rules/NoDuplicatedLines';
|
||||
import type { ICodeValidator } from '@/application/Parser/Executable/Script/Validation/ICodeValidator';
|
||||
import { isArray, isNullOrUndefined, isPlainObject } from '@/TypeHelpers';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/Common/ContextualError';
|
||||
import { createFunctionWithInlineCode, createCallerFunction } from './SharedFunction';
|
||||
import { SharedFunctionCollection } from './SharedFunctionCollection';
|
||||
import { FunctionParameter } from './Parameter/FunctionParameter';
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { ILanguageSyntax } from '@/application/Parser/Executable/Script/Val
|
||||
import { CodeValidator } from '@/application/Parser/Executable/Script/Validation/CodeValidator';
|
||||
import { NoEmptyLines } from '@/application/Parser/Executable/Script/Validation/Rules/NoEmptyLines';
|
||||
import type { ICodeValidator } from '@/application/Parser/Executable/Script/Validation/ICodeValidator';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/Common/ContextualError';
|
||||
import { createScriptCode, type ScriptCodeFactory } from '@/domain/Executables/Script/Code/ScriptCodeFactory';
|
||||
import { SharedFunctionsParser } from './Function/SharedFunctionsParser';
|
||||
import { FunctionCallSequenceCompiler } from './Function/Call/Compiler/FunctionCallSequenceCompiler';
|
||||
|
||||
@@ -5,11 +5,11 @@ import { CollectionScript } from '@/domain/Executables/Script/CollectionScript';
|
||||
import { RecommendationLevel } from '@/domain/Executables/Script/RecommendationLevel';
|
||||
import type { ScriptCode } from '@/domain/Executables/Script/Code/ScriptCode';
|
||||
import type { ICodeValidator } from '@/application/Parser/Executable/Script/Validation/ICodeValidator';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/Common/ContextualError';
|
||||
import type { ScriptCodeFactory } from '@/domain/Executables/Script/Code/ScriptCodeFactory';
|
||||
import { createScriptCode } from '@/domain/Executables/Script/Code/ScriptCodeFactory';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import { createEnumParser, type IEnumParser } from '@/application/Common/Enum';
|
||||
import { createEnumParser, type EnumParser } from '@/application/Common/Enum';
|
||||
import { parseDocs, type DocsParser } from '../DocumentationParser';
|
||||
import { ExecutableType } from '../Validation/ExecutableType';
|
||||
import { createExecutableDataValidator, type ExecutableValidator, type ExecutableValidatorFactory } from '../Validation/ExecutableValidator';
|
||||
@@ -28,23 +28,28 @@ export interface ScriptParser {
|
||||
export const parseScript: ScriptParser = (
|
||||
data,
|
||||
collectionUtilities,
|
||||
utilities = DefaultScriptParserUtilities,
|
||||
scriptUtilities = DefaultUtilities,
|
||||
) => {
|
||||
const validator = utilities.createValidator({
|
||||
const validator = scriptUtilities.createValidator({
|
||||
type: ExecutableType.Script,
|
||||
self: data,
|
||||
});
|
||||
validateScript(data, validator);
|
||||
try {
|
||||
const script = utilities.createScript({
|
||||
const script = scriptUtilities.createScript({
|
||||
name: data.name,
|
||||
code: parseCode(data, collectionUtilities, utilities.codeValidator, utilities.createCode),
|
||||
docs: utilities.parseDocs(data),
|
||||
level: parseLevel(data.recommend, utilities.levelParser),
|
||||
code: parseCode(
|
||||
data,
|
||||
collectionUtilities,
|
||||
scriptUtilities.codeValidator,
|
||||
scriptUtilities.createCode,
|
||||
),
|
||||
docs: scriptUtilities.parseDocs(data),
|
||||
level: parseLevel(data.recommend, scriptUtilities.levelParser),
|
||||
});
|
||||
return script;
|
||||
} catch (error) {
|
||||
throw utilities.wrapError(
|
||||
throw scriptUtilities.wrapError(
|
||||
error,
|
||||
validator.createContextualErrorMessage('Failed to parse script.'),
|
||||
);
|
||||
@@ -53,7 +58,7 @@ export const parseScript: ScriptParser = (
|
||||
|
||||
function parseLevel(
|
||||
level: string | undefined,
|
||||
parser: IEnumParser<RecommendationLevel>,
|
||||
parser: EnumParser<RecommendationLevel>,
|
||||
): RecommendationLevel | undefined {
|
||||
if (!level) {
|
||||
return undefined;
|
||||
@@ -95,7 +100,13 @@ function validateScript(
|
||||
script: ScriptData,
|
||||
validator: ExecutableValidator,
|
||||
): asserts script is NonNullable<ScriptData> {
|
||||
validator.assertDefined(script);
|
||||
validator.assertType((v) => v.assertObject<CallScriptData & CodeScriptData>({
|
||||
value: script,
|
||||
valueName: script.name ?? 'script',
|
||||
allowedProperties: [
|
||||
'name', 'recommend', 'code', 'revertCode', 'call', 'docs',
|
||||
],
|
||||
}));
|
||||
validator.assertValidName(script.name);
|
||||
validator.assert(
|
||||
() => Boolean((script as CodeScriptData).code || (script as CallScriptData).call),
|
||||
@@ -112,7 +123,7 @@ function validateScript(
|
||||
}
|
||||
|
||||
interface ScriptParserUtilities {
|
||||
readonly levelParser: IEnumParser<RecommendationLevel>;
|
||||
readonly levelParser: EnumParser<RecommendationLevel>;
|
||||
readonly createScript: ScriptFactory;
|
||||
readonly codeValidator: ICodeValidator;
|
||||
readonly wrapError: ErrorWithContextWrapper;
|
||||
@@ -129,7 +140,7 @@ const createScript: ScriptFactory = (...parameters) => {
|
||||
return new CollectionScript(...parameters);
|
||||
};
|
||||
|
||||
const DefaultScriptParserUtilities: ScriptParserUtilities = {
|
||||
const DefaultUtilities: ScriptParserUtilities = {
|
||||
levelParser: createEnumParser(RecommendationLevel),
|
||||
createScript,
|
||||
codeValidator: CodeValidator.instance,
|
||||
|
||||
Reference in New Issue
Block a user