Complete question transition validation
This commit is contained in:
+171
@@ -0,0 +1,171 @@
|
||||
import { AppException } from '@/common/exceptions/app.exception';
|
||||
import { QuestionTransition } from '@/modules/questions/entities/question-transition.entity';
|
||||
import { QuestionTransitionType } from '@/modules/questions/enums/question-transition-type.enum';
|
||||
import { NumericCondition } from '@/modules/questions/interfaces/numeric-condition.interface';
|
||||
import { SingleChoiceCondition } from '@/modules/questions/interfaces/single-choice-condition.interface';
|
||||
import { TransitionValidateOptions } from '@/modules/questions/interfaces/transition-validate-options.interface';
|
||||
import { HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import type { Repository } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class CheckTransitionDBProvider {
|
||||
constructor(
|
||||
/**
|
||||
* Inject Transitions Repository
|
||||
*/
|
||||
@InjectRepository(QuestionTransition)
|
||||
private readonly transitionsRepository: Repository<QuestionTransition>,
|
||||
) {}
|
||||
|
||||
public async validate(
|
||||
options: TransitionValidateOptions,
|
||||
transitionId?: number,
|
||||
) {
|
||||
await this.validateSingleStart(options, transitionId);
|
||||
|
||||
await this.validateSingleEnd(options, transitionId);
|
||||
|
||||
await this.validateDefaultTransition(options, transitionId);
|
||||
|
||||
await this.validateDuplicateCondition(options, transitionId);
|
||||
}
|
||||
|
||||
private async validateSingleStart(
|
||||
options: TransitionValidateOptions,
|
||||
transitionId?: number,
|
||||
) {
|
||||
if (options.type !== QuestionTransitionType.START) return;
|
||||
|
||||
const qb = this.transitionsRepository
|
||||
.createQueryBuilder('t')
|
||||
.where('t.testId = :testId', {
|
||||
testId: options.test.id,
|
||||
})
|
||||
.andWhere('t.type = :type', {
|
||||
type: QuestionTransitionType.START,
|
||||
});
|
||||
|
||||
if (transitionId) {
|
||||
qb.andWhere('t.id != :transitionId', {
|
||||
transitionId,
|
||||
});
|
||||
}
|
||||
|
||||
if (await qb.getExists()) {
|
||||
throw new AppException(
|
||||
'questions.errors.questionTransition.startAlreadyExists',
|
||||
HttpStatus.CONFLICT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async validateSingleEnd(
|
||||
options: TransitionValidateOptions,
|
||||
transitionId?: number,
|
||||
) {
|
||||
if (options.type !== QuestionTransitionType.END) return;
|
||||
|
||||
const qb = this.transitionsRepository
|
||||
.createQueryBuilder('t')
|
||||
.where('t.fromQuestionId = :fromQuestionId', {
|
||||
fromQuestionId: options.fromQuestion?.id,
|
||||
})
|
||||
.andWhere('t.type = :type', {
|
||||
type: QuestionTransitionType.END,
|
||||
});
|
||||
|
||||
if (transitionId) {
|
||||
qb.andWhere('t.id != :transitionId', {
|
||||
transitionId,
|
||||
});
|
||||
}
|
||||
|
||||
if (await qb.getExists()) {
|
||||
throw new AppException(
|
||||
'questions.errors.questionTransition.endAlreadyExists',
|
||||
HttpStatus.CONFLICT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async validateDefaultTransition(
|
||||
options: TransitionValidateOptions,
|
||||
transitionId?: number,
|
||||
) {
|
||||
if (options.type !== QuestionTransitionType.DEFAULT) return;
|
||||
|
||||
const qb = this.transitionsRepository
|
||||
.createQueryBuilder('t')
|
||||
.where('t.fromQuestionId = :fromQuestionId', {
|
||||
fromQuestionId: options.fromQuestion?.id,
|
||||
})
|
||||
.andWhere('t.type = :type', {
|
||||
type: QuestionTransitionType.DEFAULT,
|
||||
});
|
||||
|
||||
if (transitionId) {
|
||||
qb.andWhere('t.id != :transitionId', {
|
||||
transitionId,
|
||||
});
|
||||
}
|
||||
|
||||
if (await qb.getExists()) {
|
||||
throw new AppException(
|
||||
'questions.errors.questionTransition.defaultAlreadyExists',
|
||||
HttpStatus.CONFLICT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async validateDuplicateCondition(
|
||||
options: TransitionValidateOptions,
|
||||
transitionId?: number,
|
||||
) {
|
||||
if (
|
||||
options.type === QuestionTransitionType.DEFAULT ||
|
||||
options.type === QuestionTransitionType.START ||
|
||||
options.type === QuestionTransitionType.END
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const qb = this.transitionsRepository
|
||||
.createQueryBuilder('t')
|
||||
.where('t.fromQuestionId = :fromQuestionId', {
|
||||
fromQuestionId: options.fromQuestion?.id,
|
||||
})
|
||||
.andWhere('t.type = :type', {
|
||||
type: options.type,
|
||||
});
|
||||
|
||||
if (options.type === QuestionTransitionType.CHOICE) {
|
||||
qb.andWhere(`t.conditionValue ->> 'choiceId' = :choiceId`, {
|
||||
choiceId: String(
|
||||
(options.conditionValue as SingleChoiceCondition).choiceId,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (options.type === QuestionTransitionType.NUMBER_RANGE) {
|
||||
qb.andWhere(`t.conditionValue ->> 'min' = :min`, {
|
||||
min: String((options.conditionValue as NumericCondition).min),
|
||||
}).andWhere(`t.conditionValue ->> 'max' = :max`, {
|
||||
max: String((options.conditionValue as NumericCondition).max),
|
||||
});
|
||||
}
|
||||
|
||||
if (transitionId) {
|
||||
qb.andWhere('t.id != :transitionId', {
|
||||
transitionId,
|
||||
});
|
||||
}
|
||||
|
||||
if (await qb.getExists()) {
|
||||
throw new AppException(
|
||||
'questions.errors.questionTransition.duplicateTransition',
|
||||
HttpStatus.CONFLICT,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user