feat: add question transition constraints and command provider

This commit is contained in:
2026-08-03 17:12:55 +03:30
parent a3b7c70999
commit a3400f66c5
9 changed files with 491 additions and 97 deletions
@@ -0,0 +1,79 @@
import {
EX_QUESTION_NUMBER_RANGE_OVERLAP,
UNIQUE_QUESTION_CHOICE,
UNIQUE_QUESTION_DEFAULT,
UNIQUE_QUESTION_END,
UNIQUE_QUESTION_NUMBER_RANGE,
UNIQUE_TEST_START,
} from '@/modules/questions/constants';
import { QuestionTransitionType } from '@/modules/questions/enums/question-transition-type.enum';
import { MigrationInterface, QueryRunner } from 'typeorm';
export class QuestionTransitionConstraints1785755373000 implements MigrationInterface {
name = 'QuestionTransitionConstraints1785755373000';
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`
CREATE UNIQUE INDEX ${UNIQUE_TEST_START}
ON question_transition("testId")
WHERE type = '${QuestionTransitionType.START}';
`);
await queryRunner.query(`
CREATE UNIQUE INDEX ${UNIQUE_QUESTION_END}
ON question_transition("fromQuestionId")
WHERE type = '${QuestionTransitionType.END}';
`);
await queryRunner.query(`
CREATE UNIQUE INDEX ${UNIQUE_QUESTION_DEFAULT}
ON question_transition("fromQuestionId")
WHERE type = '${QuestionTransitionType.DEFAULT}';
`);
await queryRunner.query(`
CREATE UNIQUE INDEX ${UNIQUE_QUESTION_CHOICE}
ON question_transition(
"fromQuestionId",
(condition ->> 'choiceId')
)
WHERE type = '${QuestionTransitionType.CHOICE}';
`);
await queryRunner.query(`
CREATE UNIQUE INDEX ${UNIQUE_QUESTION_NUMBER_RANGE}
ON question_transition(
"fromQuestionId",
(condition ->> 'min'),
(condition ->> 'max')
)
WHERE type = '${QuestionTransitionType.NUMBER_RANGE}';
`);
await queryRunner.query(`
CREATE EXTENSION IF NOT EXISTS btree_gist;
`);
await queryRunner.query(`
ALTER TABLE question_transition
ADD CONSTRAINT ${EX_QUESTION_NUMBER_RANGE_OVERLAP}
EXCLUDE USING gist (
"fromQuestionId" WITH =,
int4range(
((condition ->> 'min')::int),
((condition ->> 'max')::int),
'[]'
) WITH &&
)
WHERE (type = '${QuestionTransitionType.NUMBER_RANGE}');
`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`
ALTER TABLE question_transition
DROP CONSTRAINT ${EX_QUESTION_NUMBER_RANGE_OVERLAP};
`);
await queryRunner.query(
`DROP INDEX IF EXISTS ${UNIQUE_QUESTION_NUMBER_RANGE};`,
);
await queryRunner.query(`DROP INDEX IF EXISTS ${UNIQUE_QUESTION_CHOICE};`);
await queryRunner.query(`DROP INDEX IF EXISTS ${UNIQUE_QUESTION_DEFAULT};`);
await queryRunner.query(`DROP INDEX IF EXISTS ${UNIQUE_QUESTION_END};`);
await queryRunner.query(`DROP INDEX IF EXISTS ${UNIQUE_TEST_START};`);
}
}