diff --git a/apps/backend/src/datasources/TemplateDataSource.ts b/apps/backend/src/datasources/TemplateDataSource.ts index b72b11c60a..b45966aac4 100644 --- a/apps/backend/src/datasources/TemplateDataSource.ts +++ b/apps/backend/src/datasources/TemplateDataSource.ts @@ -17,7 +17,10 @@ import { DeleteQuestionTemplateRelationArgs } from '../resolvers/mutations/templ import { SetActiveTemplateArgs } from '../resolvers/mutations/template/SetActiveTemplateMutation'; import { UpdateQuestionTemplateRelationSettingsArgs } from '../resolvers/mutations/template/UpdateQuestionTemplateRelationSettingsMutation'; import { UpdateTemplateArgs } from '../resolvers/mutations/template/UpdateTemplateMutation'; -import { QuestionsFilter } from '../resolvers/queries/QuestionsQuery'; +import { + AllQuestionsFilterArgs, + QuestionsFilter, +} from '../resolvers/queries/QuestionsQuery'; import { TemplatesArgs } from '../resolvers/queries/TemplatesQuery'; import { ConflictResolution } from '../resolvers/types/ConflictResolution'; import { TemplateGroup } from '../resolvers/types/TemplateGroup'; @@ -64,6 +67,9 @@ export interface TemplateDataSource { deleteQuestion(questionId: string): Promise; getComplementaryQuestions(templateId: number): Promise; getQuestions(filter?: QuestionsFilter): Promise; + getAllQuestions( + args: AllQuestionsFilterArgs + ): Promise<{ totalCount: number; questions: Question[] }>; getQuestionsInTemplate(templateId: number): Promise; // TemplateField rel diff --git a/apps/backend/src/datasources/mockups/TemplateDataSource.ts b/apps/backend/src/datasources/mockups/TemplateDataSource.ts index da3461b0cd..4082cc70d9 100644 --- a/apps/backend/src/datasources/mockups/TemplateDataSource.ts +++ b/apps/backend/src/datasources/mockups/TemplateDataSource.ts @@ -443,6 +443,13 @@ export class TemplateDataSourceMock implements TemplateDataSource { return [dummyQuestionFactory()]; } + async getAllQuestions(): Promise<{ + totalCount: number; + questions: Question[]; + }> { + return { totalCount: 1, questions: [dummyQuestionFactory()] }; + } + async getQuestionsInTemplate(templateId: number): Promise { return dummyTemplateSteps.flatMap((step) => step.fields.map((field) => field.question) diff --git a/apps/backend/src/datasources/postgres/TemplateDataSource.ts b/apps/backend/src/datasources/postgres/TemplateDataSource.ts index a107084b29..425e106a07 100644 --- a/apps/backend/src/datasources/postgres/TemplateDataSource.ts +++ b/apps/backend/src/datasources/postgres/TemplateDataSource.ts @@ -29,7 +29,10 @@ import { DeleteQuestionTemplateRelationArgs } from '../../resolvers/mutations/te import { SetActiveTemplateArgs } from '../../resolvers/mutations/template/SetActiveTemplateMutation'; import { UpdateQuestionTemplateRelationSettingsArgs } from '../../resolvers/mutations/template/UpdateQuestionTemplateRelationSettingsMutation'; import { UpdateTemplateArgs } from '../../resolvers/mutations/template/UpdateTemplateMutation'; -import { QuestionsFilter } from '../../resolvers/queries/QuestionsQuery'; +import { + AllQuestionsFilterArgs, + QuestionsFilter, +} from '../../resolvers/queries/QuestionsQuery'; import { TemplatesArgs } from '../../resolvers/queries/TemplatesQuery'; import { ConflictResolution } from '../../resolvers/types/ConflictResolution'; import { @@ -125,6 +128,89 @@ export default class PostgresTemplateDataSource implements TemplateDataSource { }); } + fieldMap: { [key: string]: string } = { + question: 'questions.question', + naturalKey: 'questions.natural_key', + dataType: 'questions.data_type', + answers: 'answers_count', + templates: 'templates_count', + categoryId: 'template_categories.name', + }; + + async getAllQuestions(args: AllQuestionsFilterArgs): Promise<{ + totalCount: number; + questions: Question[]; + }> { + const { filter, first, offset, sortField, sortDirection, searchText } = + args; + + return database('questions') + .select([ + 'questions.*', + 'template_categories.*', + database.raw('count(distinct answers.question_id) as answers_count'), + database.raw('count(distinct tq.question_id) as templates_count'), + database.raw('count(*) OVER() AS full_count'), + ]) + .from('questions') + .leftJoin('answers', 'questions.question_id', 'answers.question_id') + .leftJoin( + 'templates_has_questions as tq', + 'questions.question_id', + 'tq.question_id' + ) + .leftJoin( + 'template_categories', + 'questions.category_id', + 'template_categories.template_category_id' + ) + .modify((query) => { + if (filter?.category !== undefined) { + query.where('questions.category_id', filter.category); + } + if (filter?.dataType !== undefined) { + query.whereIn('questions.data_type', filter.dataType); + } + + if (searchText) { + query.andWhere((qb) => + qb + .orWhere('questions.question', 'ilike', `%${searchText.trim()}%`) + .orWhere( + 'questions.question_id', + 'ilike', + `%${searchText.trim()}%` + ) + ); + } + + if (sortField && sortDirection) { + if (!this.fieldMap.hasOwnProperty(sortField)) { + throw new GraphQLError(`Bad sort field given: ${sortField}`); + } + + query.orderByRaw(`${this.fieldMap[sortField]} ${sortDirection}`); + } + + if (first) { + query.limit(first); + } + if (offset) { + query.offset(offset); + } + }) + .groupBy([ + 'questions.question_id', + 'template_categories.template_category_id', + ]) + .then((rows: QuestionRecord[]) => { + return { + totalCount: rows[0] ? rows[0].full_count : 0, + questions: rows.map((row) => createQuestionObject(row)), + }; + }); + } + async createTemplate(args: CreateTemplateArgs): Promise