Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ body:json {
"OwnerUserId": "16377833-8e6f-41b4-944a-98a91815a4d5",
// "RootSectionId": "d04ca675-d4eb-4c6a-9820-6ba1cce3e625",
"DefaultSectionNumbering": true,
"IsFavourite": false,
"ItemsPerPage":"OneQuestion"
}
}
Expand Down
5 changes: 4 additions & 1 deletion bruno/form-service/Form template/create a form template.bru
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ body:json {
"OwnerUserId": "{{USER_ID}}",
// "RootSectionId": "{{SECTION_ID}}",
"DefaultSectionNumbering": false,
"IsFavourite": true,
"ItemsPerPage": "OneQuestion"
}
}
Expand Down Expand Up @@ -55,6 +56,8 @@ script:post-response {
expect(jsonRes.Data).to.have.property('DisplayCode');
expect(jsonRes.Data).to.have.property('OwnerUserId');
expect(jsonRes.Data).to.have.property('RootSectionId');
expect(jsonRes.Data).to.have.property('DefaultSectionNumbering');
expect(jsonRes.Data).to.have.property('DefaultSectionNumbering');
expect(jsonRes.Data).to.have.property('IsFavourite');
expect(jsonRes.Data.IsFavourite).to.equal(true);
});
}
431 changes: 197 additions & 234 deletions package-lock.json

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions src/api/form.template/form.template.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { FormTemplateValidator } from './form.template.validator';
import { ErrorHandler } from '../../common/error.handling/error.handler';
import { uuid } from '../../domain.types/miscellaneous/system.types';
import { FormTemplateService } from '../../database/services/form.template.service';
import { FavoriteTemplateService } from '../../database/services/favorite.template.service';
import {
FormTemplateCreateModel,
FormTemplateSearchFilters,
FormTemplateUpdateModel,
} from '../../domain.types/form.template.domain.types';
import { FavoriteTemplateCreateModel } from '../../domain.types/favorite.template.domain.types';
import { FormSectionService } from '../../database/services/form.section.service';
import { generateDisplayCode } from '../../domain.types/miscellaneous/display.code';
import { Helper } from '../../domain.types/miscellaneous/helper';
Expand All @@ -29,6 +31,9 @@ export class FormTemplateController {

_service: FormTemplateService =
Injector.Container.resolve(FormTemplateService);

_favoriteService: FavoriteTemplateService =
Injector.Container.resolve(FavoriteTemplateService);

_section: FormSectionService =
Injector.Container.resolve(FormSectionService);
Expand Down Expand Up @@ -373,5 +378,51 @@ export class FormTemplateController {
}
}

updateFavourite = async (request: express.Request, response: express.Response) => {
try {
const id = request.params.id;
const { IsFavourite, userId } = request.body; // Get userId from request body


if (!userId) {
return ResponseHandler.failure(request, response, 'User ID is required', 400);
}

// Update the IsFavourite flag in FormTemplate
const updateModel: FormTemplateUpdateModel = {
IsFavourite: IsFavourite
};

const result = await this._service.update(id, updateModel);

// Handle FavoriteTemplate table operations
if (IsFavourite === true) {
// Mark as favourite - create record in favorite_templates table
const existingFavorite = await this._favoriteService.findByUserAndTemplate(userId, id);

if (!existingFavorite) {
// Create new favorite record
const favoriteCreateModel: FavoriteTemplateCreateModel = {
UserId: userId,
TemplateId: id
};
await this._favoriteService.create(favoriteCreateModel);
}
} else {
// Mark as non-favourite - find and delete record from favorite_templates table
const existingFavorite = await this._favoriteService.findByUserAndTemplate(userId, id);

if (existingFavorite) {
// Delete the favorite record
await this._favoriteService.delete(existingFavorite.id);
}
}

return ResponseHandler.success(request, response, `Template ${IsFavourite ? 'marked as favourite' : 'removed from favourites'} successfully!`, 200, result);
} catch (error) {
ResponseHandler.handleError(request, response, error);
}
};

//#endregion
}
1 change: 1 addition & 0 deletions src/api/form.template/form.template.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const register = (app: express.Application): void => {
router.get('/:id', context(`${contextBase}.GetById`), controller.getById);
router.delete('/:id', context(`${contextBase}.Delete`), controller.delete);
router.get('/:id/details', context(`${contextBase}.GetDetailsById`), controller.getDetailsById);
router.put('/:id/favourites', context(`${contextBase}.UpdateFavourite`), controller.updateFavourite);

app.use('/api/v1/form-templates', router);
};
10 changes: 10 additions & 0 deletions src/api/form.template/form.template.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class FormTemplateValidator extends BaseValidator {
OwnerUserId: joi.string().uuid(),
RootSectionId: joi.string().uuid(),
DefaultSectionNumbering: joi.boolean().optional(),
IsFavourite: joi.boolean().optional(),
});
await schema.validateAsync(request.body);
return {
Expand All @@ -43,6 +44,7 @@ export class FormTemplateValidator extends BaseValidator {
RootSectionId: request.body.RootSectionId,
DefaultSectionNumbering:
request.body.DefaultSectionNumbering ?? false,
IsFavourite: request.body.IsFavourite ?? false,
};
} catch (error) {
ErrorHandler.handleValidationError(error);
Expand All @@ -64,6 +66,7 @@ export class FormTemplateValidator extends BaseValidator {
OwnerUserId: joi.string().uuid().optional(),
RootSectionId: joi.string().uuid().optional(),
DefaultSectionNumbering: joi.boolean().optional(),
IsFavourite: joi.boolean().optional(),
});
await schema.validateAsync(request.body);
return {
Expand All @@ -78,6 +81,7 @@ export class FormTemplateValidator extends BaseValidator {
RootSectionId: request.body.RootSectionId ?? null,
DefaultSectionNumbering:
request.body.DefaultSectionNumbering ?? null,
IsFavourite: request.body.IsFavourite ?? null,
};
} catch (error) {
ErrorHandler.handleValidationError(error);
Expand All @@ -98,6 +102,7 @@ export class FormTemplateValidator extends BaseValidator {
ownerUserId: joi.string().optional(),
rootSectionId: joi.string().optional(),
defaultSectionNumbering: joi.boolean().optional(),
isFavourite: joi.boolean().optional(),
itemsPerPage: joi.number().optional(),
pageIndex: joi.number().optional(),
orderBy: joi.string().optional(),
Expand Down Expand Up @@ -166,6 +171,11 @@ export class FormTemplateValidator extends BaseValidator {
filters['DefaultSectionNumbering'] = defaultSectionNumbering;
}

var isFavourite = query.isFavourite ? query.isFavourite : null;
if (isFavourite != null) {
filters['IsFavourite'] = isFavourite;
}

return filters;
};
}
1 change: 1 addition & 0 deletions src/api/question.response/question.response.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export class QuestionResponseController {
const createModel: QuestionResponseCreateModel = {
FormSubmissionId: model.FormSubmissionId,
FormFieldId: model.FormFieldId,
FormTemplateId: model.FormTemplateId,
ResponseType: model.ResponseType,
IntegerValue: model.IntegerValue,
FloatValue: model.FloatValue,
Expand Down
8 changes: 8 additions & 0 deletions src/api/question.response/question.response.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class QuestionResponseValidator extends BaseValidator {
FormSubmissionId: joi.string().uuid().required(),
// QuestionId: joi.string().uuid().required(),
FormFieldId: joi.string().uuid().required(),
FormTemplateId: joi.string().uuid().required(),
ResponseType: joi.string(),
IntegerValue: joi.number().optional(),
FloatValue: joi.number().optional(),
Expand All @@ -43,6 +44,7 @@ export class QuestionResponseValidator extends BaseValidator {
FormSubmissionId: request.body.FormSubmissionId,
// QuestionId: request.body.QuestionId,
FormFieldId: request.body.FormFieldId,
FormTemplateId: request.body.FormTemplateId,
ResponseType: request.body.ResponseType,
IntegerValue: request.body.IntegerValue ?? null,
FloatValue: request.body.FloatValue ?? null,
Expand Down Expand Up @@ -118,6 +120,7 @@ export class QuestionResponseValidator extends BaseValidator {
formSubmissionId: joi.string().uuid().optional(),
// questionId: joi.string().uuid().optional(),
formFieldId: joi.string().uuid().optional(),
formTemplateId: joi.string().uuid().optional(),
responseType: joi.string().optional(),
integerValue: joi.number().optional(),
floatValue: joi.string().optional(),
Expand Down Expand Up @@ -167,6 +170,7 @@ export class QuestionResponseValidator extends BaseValidator {
FormSubmissionId: joi.string().uuid().required(),
// QuestionId: joi.string().uuid().required(),
FormFieldId: joi.string().uuid().required(),
FormTemplateId: joi.string().uuid().required(),
ResponseType: joi.string().required(),
IntegerValue: joi.number().optional().allow(null),
FloatValue: joi.number().optional().allow(null),
Expand Down Expand Up @@ -220,6 +224,10 @@ export class QuestionResponseValidator extends BaseValidator {
if (formFieldId != null) {
filters['FormFieldId'] = formFieldId;
}
var formTemplateId = query.formTemplateId ? query.formTemplateId : null;
if (formTemplateId != null) {
filters['FormTemplateId'] = formTemplateId;
}
var responseType = query.responseType ? query.responseType : null;
if (responseType != null) {
filters['ResponseType'] = responseType;
Expand Down
1 change: 1 addition & 0 deletions src/database/mappers/form.template.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class FormTemplateMapper {
OwnerUserId: record.OwnerUserId,
RootSectionId: record.RootSectionId,
DefaultSectionNumbering: record.DefaultSectionNumbering,
IsFavourite: record.IsFavourite,
CreatedAt: record.CreatedAt,
UpdatedAt: record.UpdatedAt,
};
Expand Down
17 changes: 14 additions & 3 deletions src/database/mappers/question.response.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,22 @@ export class ResponseMapper {
id: record.id,
FormSubmission: record.FormSubmission ? {
id: record.FormSubmission.id,
TemplateId: record.FormSubmission.TemplateId,
FormUrl: record.FormSubmission.FormUrl,
FormTemplateId: record.FormSubmission.FormTemplateId,
Title: record.FormSubmission.Title,
Type: record.FormSubmission.Type,
TenantId: record.FormSubmission.TenantId,
UserId: record.FormSubmission.UserId,
UserMetaData: record.FormSubmission.UserMetaData,
Encrypted: record.FormSubmission.Encrypted,
Unencrypted: record.FormSubmission.Unencrypted,
Link: record.FormSubmission.Link,
LinkQueryParams: record.FormSubmission.LinkQueryParams,
Status: record.FormSubmission.Status,
SubmissionTimestamp: record.FormSubmission.SubmissionTimestamp,
ValidTill: record.FormSubmission.ValidTill,
SubmittedAt: record.FormSubmission.SubmittedAt,
Score: record.FormSubmission.Score,
CreatedAt: record.FormSubmission.CreatedAt,
UpdatedAt: record.FormSubmission.UpdatedAt,
} : null,
Question: record.FormField ? {
id: record.FormField.id,
Expand All @@ -32,6 +42,7 @@ export class ResponseMapper {
UpdatedAt: record.FormField.UpdatedAt,
} : null,
FormFieldId: record.FormFieldId,
FormTemplateId: record.FormTemplateId,
ResponseType: record.ResponseType,
IntegerValue: record.IntegerValue,
FloatValue: record.FloatValue,
Expand Down
3 changes: 3 additions & 0 deletions src/database/models/form.template/form.template.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ export class FormTemplate extends BaseEntity {
@Column({ type: 'varchar', length: 512, nullable: true })
Tags?: string;

@Column({ type: 'boolean', nullable: false, default: false })
IsFavourite: boolean;

@OneToMany(() => FormSubmission, submission => submission.FormTemplate)
FormSubmissions: FormSubmission[];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export class QuestionResponse extends BaseEntity {
@Column({ type: 'uuid', nullable: true })
FormFieldId: string;

@Column({ type: 'uuid', nullable: false })
FormTemplateId: string;

@Column({
type: 'enum',
enum: QueryResponseType,
Expand Down
15 changes: 15 additions & 0 deletions src/database/services/favorite.template.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,21 @@ export class FavoriteTemplateService extends BaseService {
}
};

public findByUserAndTemplate = async (userId: string, templateId: string): Promise<FavoriteTemplateResponseDto | null> => {
try {
const favorite = await this._favoriteTemplateRepository.findOne({
where: {
UserId: userId,
TemplateId: templateId,
DeletedAt: null
}
});
return favorite ? FavoriteTemplateMapper.toDto(favorite) : null;
} catch (error) {
logger.error(`❌ Error finding favorite template by user and template: ${error.message}`);
ErrorHandler.throwInternalServerError(error.message, error);
}
};
//#region Privates

private getSearchModel = (filters: FavoriteTemplateSearchFilters) => {
Expand Down
14 changes: 13 additions & 1 deletion src/database/services/form.template.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export class FormTemplateService extends BaseService {
OwnerUserId: createModel.OwnerUserId,
RootSectionId: createModel.RootSectionId,
DefaultSectionNumbering: createModel.DefaultSectionNumbering,
IsFavourite: createModel.IsFavourite ?? false,
});
const record = await this._formTemplateRepository.save(template);

Expand Down Expand Up @@ -171,7 +172,12 @@ export class FormTemplateService extends BaseService {
// Populate operations for all form fields
await this.populateFormFieldsOperations(template.FormSections);

return template;
// Map the template to include IsFavourite field
const mappedTemplate = FormTemplateMapper.toDto(template);
return {
...template,
...mappedTemplate
};
} catch (error) {
logger.error(`❌ Error getting form template details by id: ${error.message}`);
ErrorHandler.throwInternalServerError(error.message, error);
Expand Down Expand Up @@ -281,6 +287,9 @@ export class FormTemplateService extends BaseService {
if (model.DefaultSectionNumbering != null) {
template.DefaultSectionNumbering = model.DefaultSectionNumbering;
}
if (model.IsFavourite != null) {
template.IsFavourite = model.IsFavourite;
}
var record = await this._formTemplateRepository.save(template);
return FormTemplateMapper.toDto(record);
} catch (error) {
Expand Down Expand Up @@ -375,6 +384,9 @@ export class FormTemplateService extends BaseService {
if (filters.OwnerUserId) {
search.where['OwnerUserId'] = filters.OwnerUserId;
}
if (filters.IsFavourite !== undefined) {
search.where['IsFavourite'] = filters.IsFavourite;
}

return search;
};
Expand Down
16 changes: 16 additions & 0 deletions src/database/services/question.response.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class ResponseService extends BaseService {
FormSubmissionId: createModel.FormSubmissionId,
// QuestionId: createModel.QuestionId,
FormFieldId: createModel.FormFieldId,
FormTemplateId: createModel.FormTemplateId,
ResponseType: createModel.ResponseType,
IntegerValue: createModel.IntegerValue,
FloatValue: createModel.FloatValue,
Expand Down Expand Up @@ -201,6 +202,9 @@ export class ResponseService extends BaseService {
if (filters.FormFieldId) {
search.where['FormFieldId'] = filters.FormFieldId;
}
if (filters.FormTemplateId) {
search.where['FormTemplateId'] = filters.FormTemplateId;
}
if (filters.ResponseType) {
search.where['ResponseType'] = filters.ResponseType;
}
Expand All @@ -219,6 +223,18 @@ export class ResponseService extends BaseService {
if (filters.Url) {
search.where['Url'] = filters.Url;
}
if (filters.FileResourceId) {
search.where['FileResourceId'] = filters.FileResourceId;
}
if (filters.TextValue) {
search.where['TextValue'] = filters.TextValue;
}
if (filters.SubmissionTimestamp) {
search.where['SubmissionTimestamp'] = filters.SubmissionTimestamp;
}
if (filters.LastSaveTimestamp) {
search.where['LastSaveTimestamp'] = filters.LastSaveTimestamp;
}

return search;
};
Expand Down
Loading