Skip to content

Commit

Permalink
fix: improves validations API
Browse files Browse the repository at this point in the history
- Consolidate the validation function defintions to a single generic
- rename SlotValidationFunction -> StateValidationFunction

Miscellaneous
- add some missed exports to the "package-exports" index.ts

BREAKING CHANGE: validation function types altered.
  • Loading branch information
mliddell authored and Shreyas-vgr committed Nov 11, 2020
1 parent 6216dcd commit 1209f23
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 92 deletions.
22 changes: 22 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 10 additions & 18 deletions src/commonControls/DateControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Control, ControlInputHandlingProps, ControlProps, ControlState } from '
import { ControlInput } from '../controls/ControlInput';
import { ControlResultBuilder } from '../controls/ControlResult';
import { InteractionModelContributor } from '../controls/mixins/InteractionModelContributor';
import { ValidationResult } from '../controls/ValidationResult';
import { StateValidationFunction, ValidationFailure } from '../controls/ValidationResult';
import { AmazonBuiltInSlotType } from '../intents/AmazonBuiltInSlotType';
import { GeneralControlIntent, unpackGeneralControlIntent } from '../intents/GeneralControlIntent';
import {
Expand Down Expand Up @@ -71,7 +71,7 @@ export interface DateControlProps extends ControlProps {
* valid: DateControlValidations.FUTURE_DATE_ONLY,
* ```
*/
validation?: DateValidationFunction | DateValidationFunction[];
validation?: StateValidationFunction<DateControlState> | Array<StateValidationFunction<DateControlState>>;

/**
* Determines if the Control must obtain a value.
Expand Down Expand Up @@ -123,14 +123,6 @@ export interface DateControlProps extends ControlProps {
valueRenderer?: (value: string, input: ControlInput) => string;
}

/**
* ValueControl validation function
*/
export type DateValidationFunction = (
state: DateControlState,
input: ControlInput,
) => true | ValidationResult;

/**
* Mapping of action slot values to the behaviors that this control supports.
*
Expand Down Expand Up @@ -240,10 +232,10 @@ export namespace DateControlValidations {
* @param state - Control state
* @param input - Input
*/
export const PAST_DATE_ONLY: DateValidationFunction = (
export const PAST_DATE_ONLY: StateValidationFunction<DateControlState> = (
state: DateControlState,
input: ControlInput,
): true | ValidationResult => {
): true | ValidationFailure => {
const startDate = getStartDateOfRange(state.value!);
const startDateInUTC = getUTCDate(startDate);

Expand All @@ -262,10 +254,10 @@ export namespace DateControlValidations {
* @param state - Control state
* @param input - Input
*/
export const FUTURE_DATE_ONLY: DateValidationFunction = (
export const FUTURE_DATE_ONLY: StateValidationFunction<DateControlState> = (
state: DateControlState,
input: ControlInput,
): true | ValidationResult => {
): true | ValidationFailure => {
const endDate = getEndDateOfRange(state.value!);
const endDateInUTC = getUTCDate(endDate);

Expand Down Expand Up @@ -699,7 +691,7 @@ export class DateControl extends Control implements InteractionModelContributor
elicitationAction: string,
): void {
this.state.elicitationAction = elicitationAction;
const validationResult: true | ValidationResult = this.validate(input);
const validationResult: true | ValidationFailure = this.validate(input);
if (validationResult === true) {
if (elicitationAction === $.Action.Change) {
// if elicitationAction == 'change', then the previousValue must be defined.
Expand Down Expand Up @@ -774,11 +766,11 @@ export class DateControl extends Control implements InteractionModelContributor
this.validateAndAddActs(input, resultBuilder, $.Action.Change);
}

private validate(input: ControlInput): true | ValidationResult {
const listOfValidationFunc: DateValidationFunction[] =
private validate(input: ControlInput): true | ValidationFailure {
const listOfValidationFunc: Array<StateValidationFunction<DateControlState>> =
typeof this.props.validation === 'function' ? [this.props.validation] : this.props.validation;
for (const validationFunction of listOfValidationFunc) {
const validationResult: true | ValidationResult = validationFunction(this.state, input);
const validationResult: true | ValidationFailure = validationFunction(this.state, input);
if (validationResult !== true) {
log.debug(
`DateControl.validate(): validation failed. Reason: ${JSON.stringify(
Expand Down
24 changes: 9 additions & 15 deletions src/commonControls/NumberControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { Control, ControlInputHandlingProps, ControlProps, ControlState } from '
import { ControlInput } from '../controls/ControlInput';
import { ControlResultBuilder } from '../controls/ControlResult';
import { InteractionModelContributor } from '../controls/mixins/InteractionModelContributor';
import { ValidationResult } from '../controls/ValidationResult';
import { StateValidationFunction, ValidationFailure } from '../controls/ValidationResult';
import { GeneralControlIntent, unpackGeneralControlIntent } from '../intents/GeneralControlIntent';
import { ControlInteractionModelGenerator } from '../interactionModelGeneration/ControlInteractionModelGenerator';
import { ModelData, SharedSlotType } from '../interactionModelGeneration/ModelTypes';
Expand Down Expand Up @@ -68,7 +68,9 @@ export interface NumberControlProps extends ControlProps {
* - Validation functions return either `true` or a `ValidationResult` to
* describe what validation failed.
*/
validation?: NumberValidationFunction | NumberValidationFunction[];
validation?:
| StateValidationFunction<NumberControlState>
| Array<StateValidationFunction<NumberControlState>>;

/**
* Determines if the Control must obtain a value.
Expand Down Expand Up @@ -128,14 +130,6 @@ export interface NumberControlProps extends ControlProps {
valueRenderer?: (value: number, input: ControlInput) => string;
}

/**
* NumberControl validation function
*/
export type NumberValidationFunction = (
state: NumberControlState,
input: ControlInput,
) => true | ValidationResult;

/**
* NumberControl isRequired function
*/
Expand Down Expand Up @@ -1111,7 +1105,7 @@ export class NumberControl extends Control implements InteractionModelContributo
if (!this.evaluateBooleanProp(this.props.required, input) || this.state.value === undefined) {
return false;
}
const validationResult: true | ValidationResult = this.validateNumber(input);
const validationResult: true | ValidationFailure = this.validateNumber(input);
if (validationResult === true) {
return false;
}
Expand All @@ -1128,7 +1122,7 @@ export class NumberControl extends Control implements InteractionModelContributo
renderedValue:
this.state.value !== undefined ? this.props.valueRenderer(this.state.value, input) : '',
reasonCode: 'ValueInvalid',
renderedReason: (validationResult as ValidationResult).renderedReason,
renderedReason: (validationResult as ValidationFailure).renderedReason,
}),
);
resultBuilder.addAct(new RequestValueAct(this));
Expand Down Expand Up @@ -1157,11 +1151,11 @@ export class NumberControl extends Control implements InteractionModelContributo
);
}

private validateNumber(input: ControlInput): true | ValidationResult {
const listOfValidationFunc: NumberValidationFunction[] =
private validateNumber(input: ControlInput): true | ValidationFailure {
const listOfValidationFunc: Array<StateValidationFunction<NumberControlState>> =
typeof this.props.validation === 'function' ? [this.props.validation] : this.props.validation;
for (const validationFunction of listOfValidationFunc) {
const validationResult: boolean | ValidationResult = validationFunction(this.state, input);
const validationResult: boolean | ValidationFailure = validationFunction(this.state, input);
if (validationResult !== true) {
log.debug(
`NumberControl.validate(): validation failed. Reason: ${JSON.stringify(
Expand Down
22 changes: 8 additions & 14 deletions src/commonControls/ValueControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { ControlInput } from '../controls/ControlInput';
import { ControlResultBuilder } from '../controls/ControlResult';
import { ControlStateDiagramming } from '../controls/mixins/ControlStateDiagramming';
import { InteractionModelContributor } from '../controls/mixins/InteractionModelContributor';
import { ValidationResult } from '../controls/ValidationResult';
import { StateValidationFunction, ValidationFailure } from '../controls/ValidationResult';
import { AmazonBuiltInSlotType } from '../intents/AmazonBuiltInSlotType';
import { GeneralControlIntent, unpackGeneralControlIntent } from '../intents/GeneralControlIntent';
import {
Expand Down Expand Up @@ -77,7 +77,9 @@ export interface ValueControlProps extends ControlProps {
* - Validation functions return either `true` or a `ValidationResult` to
* describe what validation failed.
*/
validation?: SlotValidationFunction | SlotValidationFunction[];
validation?:
| StateValidationFunction<ValueControlState>
| Array<StateValidationFunction<ValueControlState>>;

/**
* Determines if the Control must obtain a value.
Expand Down Expand Up @@ -129,14 +131,6 @@ export interface ValueControlProps extends ControlProps {
valueRenderer?: (value: string, input: ControlInput) => string;
}

/**
* ValueControl validation function
*/
export type SlotValidationFunction = (
state: ValueControlState,
input: ControlInput,
) => true | ValidationResult;

/**
* Mapping of action slot values to the behaviors that this control supports.
*
Expand Down Expand Up @@ -758,7 +752,7 @@ export class ValueControl extends Control implements InteractionModelContributor
elicitationAction: string,
): void {
this.state.elicitationAction = elicitationAction;
const validationResult: true | ValidationResult = this.validate(input);
const validationResult: true | ValidationFailure = this.validate(input);
if (typeof validationResult === 'boolean') {
if (elicitationAction === $.Action.Change) {
// if elicitationAction == 'change', then the previousValue must be defined.
Expand Down Expand Up @@ -804,11 +798,11 @@ export class ValueControl extends Control implements InteractionModelContributor
*
* @param input - Input.
*/
private validate(input: ControlInput): true | ValidationResult {
const listOfValidationFunc: SlotValidationFunction[] =
private validate(input: ControlInput): true | ValidationFailure {
const listOfValidationFunc: Array<StateValidationFunction<ValueControlState>> =
typeof this.props.validation === 'function' ? [this.props.validation] : this.props.validation;
for (const validationFunction of listOfValidationFunc) {
const validationResult: true | ValidationResult = validationFunction(this.state, input);
const validationResult: true | ValidationFailure = validationFunction(this.state, input);
if (validationResult !== true) {
log.debug(
`ValueControl.validate(): validation failed. Reason: ${JSON.stringify(
Expand Down
38 changes: 18 additions & 20 deletions src/commonControls/dateRangeControl/DateRangeControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
import { ControlInput } from '../../controls/ControlInput';
import { ControlResultBuilder } from '../../controls/ControlResult';
import { InteractionModelContributor } from '../../controls/mixins/InteractionModelContributor';
import { ValidationResult } from '../../controls/ValidationResult';
import { StateValidationFunction, ValidationFailure } from '../../controls/ValidationResult';
import { AmazonBuiltInSlotType } from '../../intents/AmazonBuiltInSlotType';
import {
ActionAndTask,
Expand All @@ -51,7 +51,7 @@ import { SystemAct } from '../../systemActs/SystemAct';
import { evaluateCustomHandleFuncs, logIfBothTrue } from '../../utils/ControlUtils';
import { DeepRequired } from '../../utils/DeepRequired';
import { falseIfGuardFailed, okIf } from '../../utils/Predicates';
import { DateControl, DateControlPromptProps, DateValidationFunction } from '../DateControl';
import { DateControl, DateControlPromptProps, DateControlState } from '../DateControl';
import { alexaDateFormatToDate, findEdgeDateOfDateRange } from './DateHelper';
import { DateRangeControlIntentInput, generateDatesInputGroups } from './DateRangeNLUHelper';

Expand Down Expand Up @@ -85,7 +85,9 @@ export interface DateRangeControlProps extends ContainerControlProps {
* valid: DateControlValidations.FUTURE_DATE_ONLY,
* ```
*/
startDateValid?: DateValidationFunction | DateValidationFunction[];
startDateValid?:
| StateValidationFunction<DateControlState>
| Array<StateValidationFunction<DateControlState>>;

/**
* Function(s) that determine if the end date (in isolation) is valid.
Expand All @@ -102,7 +104,9 @@ export interface DateRangeControlProps extends ContainerControlProps {
* valid: DateControlValidations.FUTURE_DATE_ONLY,
* ```
*/
endDateValid?: DateValidationFunction | DateValidationFunction[];
endDateValid?:
| StateValidationFunction<DateControlState>
| Array<StateValidationFunction<DateControlState>>;

/**
* Function(s) that determine if the date-range is valid.
Expand All @@ -119,7 +123,9 @@ export interface DateRangeControlProps extends ContainerControlProps {
* valid: DateRangeControlValidations.START_BEFORE_END,
* ```
*/
rangeValid?: DateRangeValidationFunction | DateRangeValidationFunction[];
rangeValid?:
| StateValidationFunction<DateRangeControlState>
| Array<StateValidationFunction<DateRangeControlState>>;
};

/**
Expand Down Expand Up @@ -177,14 +183,6 @@ export type DateRange = {
endDate: string;
};

/**
* Date range validation function
*/
export type DateRangeValidationFunction = (
state: DateRangeControlState,
input: ControlInput,
) => true | ValidationResult;

/**
* Mapping of action slot values to the behaviors that this control supports.
*
Expand Down Expand Up @@ -362,10 +360,10 @@ export enum DateRangeValidationFailReasonCode {
* Built-in validation functions for use with DateControl
*/
export namespace DateRangeControlValidations {
export const START_BEFORE_END: DateRangeValidationFunction = (
export const START_BEFORE_END: StateValidationFunction<DateRangeControlState> = (
state: DateRangeControlState,
input: ControlInput,
): true | ValidationResult => {
): true | ValidationFailure => {
const startDate = alexaDateFormatToDate(state.startDate!);
const endDate = alexaDateFormatToDate(state.endDate!);
if (startDate > endDate) {
Expand Down Expand Up @@ -1214,13 +1212,13 @@ export class DateRangeControl extends ContainerControl implements InteractionMod
}
}

private validateDateRange(input: ControlInput): true | ValidationResult {
const listOfValidationFunc: DateRangeValidationFunction[] =
private validateDateRange(input: ControlInput): true | ValidationFailure {
const listOfValidationFunc: Array<StateValidationFunction<DateRangeControlState>> =
typeof this.props.validation.rangeValid === 'function'
? [this.props.validation.rangeValid]
: this.props.validation.rangeValid;
for (const validationFunction of listOfValidationFunc) {
const validationResult: true | ValidationResult = validationFunction(this.state, input);
const validationResult: true | ValidationFailure = validationFunction(this.state, input);
if (validationResult !== true) {
log.debug(
`DateRangeControl.validate(): validation failed. Reason: ${JSON.stringify(
Expand Down Expand Up @@ -1326,7 +1324,7 @@ export class DateRangeControl extends ContainerControl implements InteractionMod
try {
// Only fix range when range is set and there's no open question
okIf(!this.needsValue(input) && !this.state.isChangingRange);
const rangeValidationResult: true | ValidationResult = this.validateDateRange(input);
const rangeValidationResult: true | ValidationFailure = this.validateDateRange(input);
okIf(rangeValidationResult !== true);
this.takeInitiativeFunc = this.correctRange;
return true;
Expand All @@ -1336,7 +1334,7 @@ export class DateRangeControl extends ContainerControl implements InteractionMod
}

private correctRange(input: ControlInput, resultBuilder: ControlResultBuilder): void {
const rangeValidationResult: true | ValidationResult = this.validateDateRange(input);
const rangeValidationResult: true | ValidationFailure = this.validateDateRange(input);
this.state.onFocus = true;
if (rangeValidationResult !== true) {
const dateRange = {
Expand Down
Loading

0 comments on commit 1209f23

Please sign in to comment.