From 3753b16ce952dd5dac7cdae45392ef98a4711858 Mon Sep 17 00:00:00 2001 From: Shubham109879 Date: Sun, 15 Jun 2025 13:10:22 +0530 Subject: [PATCH 01/29] forms_typeorm_conversion_initial_commit --- .vscode/launch.json | 2 +- config.json | 167 + config.local.json | 151 + package-lock.json | 2984 ++++++++++++++++- package.json | 8 +- src/api/base.controller.ts | 2 +- src/api/base.validator.ts | 2 +- src/api/form.section/form.section.auth.ts | 0 .../form.section/form.section.controller.ts | 11 +- .../form.section/form.section.validator.ts | 2 +- src/api/form.submission/form.auth.ts | 0 src/api/form.submission/form.controller.ts | 14 +- src/api/form.submission/form.validator.ts | 12 +- src/api/form.template/form.template.auth.ts | 0 .../form.template/form.template.controller.ts | 67 +- src/api/form.template/form.template.router.ts | 3 + .../form.template/form.template.validator.ts | 25 +- .../question.response.auth.ts | 0 .../question.response.controller.ts | 18 +- .../question.response.validator.ts | 5 +- src/api/question/question.auth.ts | 0 src/api/question/question.controller.ts | 42 +- src/api/question/question.router.ts | 1 - src/api/question/question.validator.ts | 13 +- src/api/user/user.controller.ts | 9 +- src/api/user/user.validator.ts | 2 +- src/app.ts | 70 +- src/common/database.utils/database.config.ts | 92 + .../database.client.interface.ts | 11 + .../dialect.clients/database.client.ts | 36 + .../dialect.clients/mysql.client.ts | 96 + .../dialect.clients/postgresql.client.ts | 85 + .../dialect.clients/sqlite.client.ts | 57 + src/common/{ => handlers}/error.handler.ts | 2 +- src/common/{ => handlers}/response.handler.ts | 6 +- src/config/configuration.manager.ts | 221 ++ src/config/configuration.types.ts | 104 + src/database/database.connector.interface.ts | 9 + src/database/database.connector.ts | 44 + src/database/database.injector.ts | 22 + .../form.section.repo.interface.ts | 17 + .../form.submission.repo.interface.ts | 22 + .../form.template.repo.interface.ts | 25 + .../question.response.repo.interface.ts | 28 + .../question/question.repo.interface.ts | 18 + .../user/user.repo.interface.ts | 17 + src/database/sql/sql.injector.ts | 20 + .../sql/typeorm/database.connector.typeorm.ts | 272 ++ .../typeorm}/mappers/form.section.mapper.ts | 2 +- .../mappers/form.submission.mapper.ts | 2 +- .../typeorm}/mappers/form.template.mapper.ts | 6 +- .../sql/typeorm}/mappers/question.mapper.ts | 2 +- .../mappers/question.response.mapper.ts | 2 +- .../sql/typeorm}/mappers/user.login.mapper.ts | 2 +- .../sql/typeorm}/mappers/user.mapper.ts | 2 +- .../sql/typeorm/models/base.entity.ts | 15 + .../favorite.template.model.ts | 13 + .../models/form.section/form.section.model.ts | 54 + .../form.submission/form.submission.model.ts | 65 + .../form.template.approval.model.ts | 18 + .../form.template/form.template.model.ts | 70 + .../input.unit.list/input.unit.list.model.ts | 17 + .../question.response.model.ts | 59 + .../typeorm/models/question/question.model.ts | 91 + .../template.folder/template.folder.model.ts | 15 + .../sql/typeorm/models/user/user.model.ts | 31 + .../sql/typeorm/repositories/base.repo.ts | 36 + .../form.section/form.section.repo.ts | 200 ++ .../form.submission/form.submission.repo.ts | 219 ++ .../form.template/form.template.repo.ts | 568 ++++ .../question.response.repo.ts | 353 ++ .../repositories/question/question.repo.ts | 278 ++ .../typeorm/repositories/user/user.repo.ts | 207 ++ src/database/sql/typeorm/typeorm.injector.ts | 30 + .../forms/form.section.domain.types.ts | 4 +- .../forms/form.submission.domain.types.ts | 13 +- .../forms/form.template.domain.types.ts | 56 +- .../forms/question.domain.types.ts | 7 +- .../forms/response.domain.types.ts | 3 +- .../miscellaneous/system.types.ts | 2 + src/modules/module.injector.ts | 19 + src/modules/reports/pdf.generator.ts | 228 ++ src/modules/storage/file.storage.injector.ts | 22 + .../file.storage.service.interface.ts | 18 + .../providers/aws.s3.file.storage.service.ts | 198 ++ .../providers/custom.file.storage.service.ts | 121 + src/services/form.section.service.ts | 236 -- .../form.section/form.section.service.ts | 58 + src/services/form.submission.service.ts | 182 - .../form.submission.service.ts | 65 + src/services/form.template.service.ts | 268 -- .../form.template/form.template.service.ts | 331 ++ src/services/question.response.service.ts | 350 -- .../question.response.service.ts | 112 + src/services/question.service.ts | 412 --- src/services/question/question.service.ts | 186 + src/services/user.login.session.service.ts | 2 +- src/services/user.service.ts | 210 -- src/services/user/user.service.ts | 49 + src/startup/injector.ts | 23 + src/startup/loader.ts | 77 + src/startup/logger.ts | 20 + tsconfig.json | 143 +- 103 files changed, 8544 insertions(+), 2042 deletions(-) create mode 100644 config.json create mode 100644 config.local.json create mode 100644 src/api/form.section/form.section.auth.ts create mode 100644 src/api/form.submission/form.auth.ts create mode 100644 src/api/form.template/form.template.auth.ts create mode 100644 src/api/question.response/question.response.auth.ts create mode 100644 src/api/question/question.auth.ts create mode 100644 src/common/database.utils/database.config.ts create mode 100644 src/common/database.utils/dialect.clients/database.client.interface.ts create mode 100644 src/common/database.utils/dialect.clients/database.client.ts create mode 100644 src/common/database.utils/dialect.clients/mysql.client.ts create mode 100644 src/common/database.utils/dialect.clients/postgresql.client.ts create mode 100644 src/common/database.utils/dialect.clients/sqlite.client.ts rename src/common/{ => handlers}/error.handler.ts (97%) rename src/common/{ => handlers}/response.handler.ts (93%) create mode 100644 src/config/configuration.manager.ts create mode 100644 src/config/configuration.types.ts create mode 100644 src/database/database.connector.interface.ts create mode 100644 src/database/database.connector.ts create mode 100644 src/database/database.injector.ts create mode 100644 src/database/repository.interfaces/form.section/form.section.repo.interface.ts create mode 100644 src/database/repository.interfaces/form.submission/form.submission.repo.interface.ts create mode 100644 src/database/repository.interfaces/form.template/form.template.repo.interface.ts create mode 100644 src/database/repository.interfaces/question.response/question.response.repo.interface.ts create mode 100644 src/database/repository.interfaces/question/question.repo.interface.ts create mode 100644 src/database/repository.interfaces/user/user.repo.interface.ts create mode 100644 src/database/sql/sql.injector.ts create mode 100644 src/database/sql/typeorm/database.connector.typeorm.ts rename src/{ => database/sql/typeorm}/mappers/form.section.mapper.ts (93%) rename src/{ => database/sql/typeorm}/mappers/form.submission.mapper.ts (96%) rename src/{ => database/sql/typeorm}/mappers/form.template.mapper.ts (82%) rename src/{ => database/sql/typeorm}/mappers/question.mapper.ts (98%) rename src/{ => database/sql/typeorm}/mappers/question.response.mapper.ts (95%) rename src/{ => database/sql/typeorm}/mappers/user.login.mapper.ts (94%) rename src/{ => database/sql/typeorm}/mappers/user.mapper.ts (93%) create mode 100644 src/database/sql/typeorm/models/base.entity.ts create mode 100644 src/database/sql/typeorm/models/favorite.template/favorite.template.model.ts create mode 100644 src/database/sql/typeorm/models/form.section/form.section.model.ts create mode 100644 src/database/sql/typeorm/models/form.submission/form.submission.model.ts create mode 100644 src/database/sql/typeorm/models/form.template.approval/form.template.approval.model.ts create mode 100644 src/database/sql/typeorm/models/form.template/form.template.model.ts create mode 100644 src/database/sql/typeorm/models/input.unit.list/input.unit.list.model.ts create mode 100644 src/database/sql/typeorm/models/question.response/question.response.model.ts create mode 100644 src/database/sql/typeorm/models/question/question.model.ts create mode 100644 src/database/sql/typeorm/models/template.folder/template.folder.model.ts create mode 100644 src/database/sql/typeorm/models/user/user.model.ts create mode 100644 src/database/sql/typeorm/repositories/base.repo.ts create mode 100644 src/database/sql/typeorm/repositories/form.section/form.section.repo.ts create mode 100644 src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts create mode 100644 src/database/sql/typeorm/repositories/form.template/form.template.repo.ts create mode 100644 src/database/sql/typeorm/repositories/question.response/question.response.repo.ts create mode 100644 src/database/sql/typeorm/repositories/question/question.repo.ts create mode 100644 src/database/sql/typeorm/repositories/user/user.repo.ts create mode 100644 src/database/sql/typeorm/typeorm.injector.ts create mode 100644 src/modules/module.injector.ts create mode 100644 src/modules/reports/pdf.generator.ts create mode 100644 src/modules/storage/file.storage.injector.ts create mode 100644 src/modules/storage/interfaces/file.storage.service.interface.ts create mode 100644 src/modules/storage/providers/aws.s3.file.storage.service.ts create mode 100644 src/modules/storage/providers/custom.file.storage.service.ts delete mode 100644 src/services/form.section.service.ts create mode 100644 src/services/form.section/form.section.service.ts delete mode 100644 src/services/form.submission.service.ts create mode 100644 src/services/form.submission/form.submission.service.ts delete mode 100644 src/services/form.template.service.ts create mode 100644 src/services/form.template/form.template.service.ts delete mode 100644 src/services/question.response.service.ts create mode 100644 src/services/question.response/question.response.service.ts delete mode 100644 src/services/question.service.ts create mode 100644 src/services/question/question.service.ts delete mode 100644 src/services/user.service.ts create mode 100644 src/services/user/user.service.ts create mode 100644 src/startup/injector.ts create mode 100644 src/startup/loader.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 3a5eec0..219b6a2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,7 +12,7 @@ "skipFiles": [ "/**" ], - "program": "${workspaceFolder}//dist//index.js", + "program": "${workspaceFolder}/src/index.ts", "preLaunchTask": "tsc: build - tsconfig.json", diff --git a/config.json b/config.json new file mode 100644 index 0000000..b198729 --- /dev/null +++ b/config.json @@ -0,0 +1,167 @@ +{ + "SystemIdentifier": "REAN HealthGuru", + "Auth" : { + "Authentication": "Custom", + "Authorization": "Custom", + "UseRefreshToken": true, + "AccessTokenExpiresInSeconds": 2592000, + "RefreshTokenExpiresInSeconds": 2592000 + }, + "Database" : { + "Type": "SQL", + "ORM": "Sequelize" + }, + "Ehr" : { + "Enabled": false, + "Specification": "FHIR", + "Provider": "GCP-FHIR" + }, + "FileStorage" : { + "Provider": "AWS-S3" + }, + "Processor" : { + "Provider": "Custom" + }, + "FeatureFlags" : { + "Provider": "Firebase-Remote-Config" + }, + "Communication" : { + "SMS": { + "Provider": "Twilio" + }, + "Email": { + "Provider": "SendGrid" + }, + "InAppNotifications": { + "Provider": "Firebase" + } + }, + "TemporaryFolders": { + "Upload": "./tmp/resources/uploads/", + "Download": "./tmp/resources/downloads/", + "CleanupFolderBeforeMinutes": 10 + }, + "Careplans" : [ + { + "Enabled": true, + "Provider": "AHA", + "Service": "AhaCarePlanService", + "Plans": [ + { + "Provider" : "AHA", + "Name": "Heart Failure Motivator", + "Code": "HFMotivator", + "DisplayName": "Heart Failure Motivator", + "DefaultDurationDays": 84, + "Description": "Heart Failure is a chronic condition in which the heart doesn't pump blood as well as it should. Heart failure can occur if the heart cannot pump (systolic) or fill (diastolic) adequately. Symptoms include shortness of breath, fatigue, swollen legs and rapid heartbeat.\nHeart Failure care plan helps in managing the condition.\n\nThe American Heart Association's National HeartFailure Initiative, IMPLEMENT-HF, is made possible with funding by founding sponsor, Novartis, and national sponsor, Boehringer Ingelheim and Eli Lilly and Company." + }, + { + "Provider" : "AHA", + "Name": "Cholesterol", + "Code": "Cholesterol", + "DisplayName": "Cholesterol", + "DefaultDurationDays": 91, + "Description": "Start your health journey to better manage your high cholesterol and reduce your risk of heart disease and stroke.\nYou'll learn about healthy lifestyle habits, goal planning, shared decision-making with your care team, cholesterol medications, self-management tips, and health behavior maintenance.\nNovartis is a proud supporter of the American Heart Association’s Integrated ASCVD Management Initiative." + }, + { + "Provider" : "AHA", + "Name": "Stroke", + "Code": "Stroke", + "DisplayName": "Stroke", + "DefaultDurationDays": 91, + "Description": "A stroke occurs when a blood vessel that carries oxygen and nutrients to the brain is either blocked by a clot or bursts (or ruptures).\nWhen that happens, part of the brain cannot get the blood (and oxygen) it needs, so it and brain cells die.\nThe HCA Healthcare Foundation is proud to be a national supporter of the American Stroke Association’s Together to End Stroke™.\nFor more information, visit: https://www.stroke.org." + }, + { + "Provider" : "AHA", + "Name": "SMBP", + "Code": "SMBP", + "DisplayName": "SMBP", + "DefaultDurationDays": 84, + "Description": "SMBP plus clinical support can improve access to care and quality of care for people with hypertension while making blood pressure control more convenient and accessible across the population. Clinical support includes regular one-on-one counseling, web-based or telephonic support tools, and educational classes." + } + ] + }, + { + "Enabled": true, + "Provider": "REAN", + "Service": "ReanCarePlanService", + "Plans": [ + { + "Provider" : "REAN", + "Name": "Maternity Careplan", + "Code": "DMC-Maternity", + "DisplayName": "Maternity Careplan", + "DefaultDurationDays": 100, + "Description": "Personal care plans help you to explore, understand and record your individual choices for pregnancy, birth and early parenthood. Every woman and family is different, with individual needs, which have an influence on care and preferences." + }, + { + "Provider" : "REAN", + "Name": "High Risk Maternity Careplan", + "Code": "Kenya-Maternity-Phase-2", + "DisplayName": "Maternity Careplan", + "DefaultDurationDays": 100, + "Description": "Personal care plans help you to explore, understand and record your individual choices for pregnancy, birth and early parenthood. Every woman and family is different, with individual needs, which have an influence on care and preferences." + } + ] + }, + { + "Enabled": true, + "Provider": "REAN_BW", + "Service": "ReanBWService", + "Plans": [ + { + "Provider" : "REAN_BW", + "Name": "Patient messages", + "Code": "Patient-Reminders", + "DisplayName": "Blood Warior Patient", + "DefaultDurationDays": 30, + "Description": "Blood warrior patient messages." + }, + { + "Provider" : "REAN_BW", + "Name": "Patient donation confirmation message", + "Code": "Patient-Donation-Confirmation", + "DisplayName": "Blood Warior Patient Confirmation", + "DefaultDurationDays": 30, + "Description": "Blood warrior patient messages." + }, + { + "Provider" : "REAN_BW", + "Name": "Donor messages", + "Code": "Donor-Reminders", + "DisplayName": "Blood Warior Donor", + "DefaultDurationDays": 30, + "Description": "Blood warrior donor messages." + }, + { + "Provider" : "REAN_BW", + "Name": "Volunteer messages", + "Code": "Volunteer-Donation-Confirmation", + "DisplayName": "Blood Warior Volunteer", + "DefaultDurationDays": 30, + "Description": "Blood warrior volunteer messages." + } + ] + } + ], + "FormServiceProviders" : [ + { + "Provider": "KoboToolbox", + "Code": "KoboToolbox" + }, + { + "Provider": "GoogleForms", + "Code": "GoogleForms" + } + ], + "WebhookControllerProviders" : [ + { + "Provider": "Terra", + "Code": "Terra" + } + ], + "Gamification": true, + "EHRAnalytics": true, + "Logger" : "Custom", + "MaxUploadFileSize": 104857600 +} diff --git a/config.local.json b/config.local.json new file mode 100644 index 0000000..6d813c2 --- /dev/null +++ b/config.local.json @@ -0,0 +1,151 @@ +{ + "SystemIdentifier": "REAN HealthGuru", + "Auth" : { + "Authentication": "Custom", + "Authorization": "Custom", + "UseRefreshToken": true, + "AccessTokenExpiresInSeconds": 2592000, + "RefreshTokenExpiresInSeconds": 2592000 + }, + "Database" : { + "Type": "SQL", + "ORM": "Sequelize" + }, + "Ehr" : { + "Enabled": false, + "Specification": "FHIR", + "Provider": "GCP-FHIR" + }, + "FileStorage" : { + "Provider": "Custom" + }, + "Processor" : { + "Provider": "Custom" + }, + "FeatureFlags" : { + "Provider": "Custom" + }, + "Communication" : { + "SMS": { + "Provider": "Twilio" + }, + "Email": { + "Provider": "Mock" + }, + "InAppNotifications": { + "Provider": "Mock" + } + }, + "TemporaryFolders": { + "Upload": "./tmp/resources/uploads/", + "Download": "./tmp/resources/downloads/", + "CleanupFolderBeforeMinutes": 10 + }, + "Careplans" : [ + { + "Enabled" : true, + "Provider": "AHA", + "Service": "AhaCarePlanService", + "Plans": [ + { + "Provider" : "AHA", + "Name": "Heart Failure Motivator", + "Code": "HFMotivator", + "DisplayName": "Heart Failure Motivator", + "DefaultDurationDays": 84, + "Description": "Heart Failure is a chronic condition in which the heart doesn't pump blood as well as it should. Heart failure can occur if the heart cannot pump (systolic) or fill (diastolic) adequately. Symptoms include shortness of breath, fatigue, swollen legs and rapid heartbeat.\nHeart Failure care plan helps in managing the condition.\n\nThe American Heart Association's National HeartFailure Initiative, IMPLEMENT-HF, is made possible with funding by founding sponsor, Novartis, and national sponsor, Boehringer Ingelheim and Eli Lilly and Company." + }, + { + "Provider" : "AHA", + "Name": "Cholesterol", + "Code": "Cholesterol", + "DisplayName": "Cholesterol", + "DefaultDurationDays": 91, + "Description": "Start your health journey to better manage your high cholesterol and reduce your risk of heart disease and stroke.\nYou'll learn about healthy lifestyle habits, goal planning, shared decision-making with your care team, cholesterol medications, self-management tips, and health behavior maintenance.\nNovartis is a proud supporter of the American Heart Association’s Integrated ASCVD Management Initiative." + }, + { + "Provider" : "AHA", + "Name": "Stroke", + "Code": "Stroke", + "DisplayName": "Stroke", + "DefaultDurationDays": 91, + "Description": "A stroke occurs when a blood vessel that carries oxygen and nutrients to the brain is either blocked by a clot or bursts (or ruptures).\nWhen that happens, part of the brain cannot get the blood (and oxygen) it needs, so it and brain cells die.\nThe HCA Healthcare Foundation is proud to be a national supporter of the American Stroke Association’s Together to End Stroke™.\nFor more information, visit: https://www.stroke.org." + }, + { + "Provider" : "AHA", + "Name": "SMBP", + "Code": "SMBP", + "DisplayName": "SMBP", + "DefaultDurationDays": 84, + "Description": "SMBP plus clinical support can improve access to care and quality of care for people with hypertension while making blood pressure control more convenient and accessible across the population. Clinical support includes regular one-on-one counseling, web-based or telephonic support tools, and educational classes." + } + ] + }, + { + "Enabled": true, + "Provider": "REAN", + "Service": "ReanCarePlanService", + "Plans": [ + { + "Provider" : "REAN", + "Name": "Maternity Careplan", + "Code": "DMC-Maternity", + "DisplayName": "Maternity Careplan", + "DefaultDurationDays": 100, + "Description": "Personal care plans help you to explore, understand and record your individual choices for pregnancy, birth and early parenthood. Every woman and family is different, with individual needs, which have an influence on care and preferences." + }, + { + "Provider" : "REAN", + "Name": "High Risk Maternity Careplan", + "Code": "Maternity-High-Risk", + "DisplayName": "Maternity Careplan", + "DefaultDurationDays": 100, + "Description": "Personal care plans help you to explore, understand and record your individual choices for pregnancy, birth and early parenthood. Every woman and family is different, with individual needs, which have an influence on care and preferences." + } + ] + }, + { + "Enabled": true, + "Provider": "REAN_BW", + "Service": "ReanBWService", + "Plans": [ + { + "Provider" : "REAN_BW", + "Name": "Patient messages", + "Code": "Patient-Reminders", + "DisplayName": "Blood Warior Patient", + "DefaultDurationDays": 30, + "Description": "Blood warrior patient messages." + }, + { + "Provider" : "REAN_BW", + "Name": "Donor messages", + "Code": "Donor-Reminders", + "DisplayName": "Blood Warior Donor", + "DefaultDurationDays": 30, + "Description": "Blood warrior donor messages." + } + ] + } + ], + "FormServiceProviders" : [ + { + "Provider": "KoboToolbox", + "Code": "KoboToolbox" + }, + { + "Provider": "GoogleForms", + "Code": "GoogleForms" + } + ], + "WebhookControllerProviders" : [ + { + "Provider": "Terra", + "Code": "Terra" + } + ], + "Gamification": false, + "EHRAnalytics": true, + "Logger" : "Pino", + "MaxUploadFileSize": 104857600 +} diff --git a/package-lock.json b/package-lock.json index 9325ccb..6b668e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@aws-sdk/client-s3": "^3.828.0", + "@aws-sdk/s3-request-presigner": "^3.828.0", "@prisma/client": "^5.22.0", "body-parser": "^1.20.2", "cors": "^2.8.5", @@ -17,12 +19,15 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "express-async-errors": "^3.1.1", + "helmet": "^8.1.0", "joi": "^17.12.2", "moment-timezone": "^0.5.45", "mysql2": "^3.10.0", "pdfkit": "^0.15.0", + "pg": "^8.16.0", "reflect-metadata": "^0.2.2", - "tsyringe": "^4.8.0" + "tsyringe": "^4.8.0", + "typeorm": "^0.3.24" }, "devDependencies": { "@types/cors": "^2.8.17", @@ -30,140 +35,1795 @@ "@types/mime-types": "^2.1.4", "@types/node": "^20.3.1", "@types/pdfkit": "^0.13.4", + "@types/pg": "^8.15.4", "prisma": "^5.19.1", "ts-node": "^10.9.1", "typescript": "^5.1.3" } }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.828.0.tgz", + "integrity": "sha512-TvFyrEfJkf9NN3cq5mXCgFv/sPaA8Rm5tEPgV5emuLedeGsORlWmVpdSKqfZ4lSoED1tMfNM6LY4uA9D8/RS5g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/credential-provider-node": "3.828.0", + "@aws-sdk/middleware-bucket-endpoint": "3.821.0", + "@aws-sdk/middleware-expect-continue": "3.821.0", + "@aws-sdk/middleware-flexible-checksums": "3.826.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-location-constraint": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-sdk-s3": "3.826.0", + "@aws-sdk/middleware-ssec": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.828.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/signature-v4-multi-region": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.828.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/eventstream-serde-browser": "^4.0.4", + "@smithy/eventstream-serde-config-resolver": "^4.1.2", + "@smithy/eventstream-serde-node": "^4.0.4", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-blob-browser": "^4.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/hash-stream-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/md5-js": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.828.0.tgz", + "integrity": "sha512-qxw8JcPTaFaBwTBUr4YmLajaMh3En65SuBWAKEtjctbITRRekzR7tvr/TkwoyVOh+XoAtkwOn+BQeQbX+/wgHw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.828.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.828.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.826.0.tgz", + "integrity": "sha512-BGbQYzWj3ps+dblq33FY5tz/SsgJCcXX0zjQlSC07tYvU1jHTUvsefphyig+fY38xZ4wdKjbTop+KUmXUYrOXw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.5.3", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-utf8": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.826.0.tgz", + "integrity": "sha512-DK3pQY8+iKK3MGDdC3uOZQ2psU01obaKlTYhEwNu4VWzgwQL4Vi3sWj4xSWGEK41vqZxiRLq6fOq7ysRI+qEZA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.826.0.tgz", + "integrity": "sha512-N+IVZBh+yx/9GbMZTKO/gErBi/FYZQtcFRItoLbY+6WU+0cSWyZYfkoeOxHmQV3iX9k65oljERIWUmL9x6OSQg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.828.0.tgz", + "integrity": "sha512-T3DJMo2/j7gCPpFg2+xEHWgua05t8WP89ye7PaZxA2Fc6CgScHkZsJZTri1QQIU2h+eOZ75EZWkeFLIPgN0kRQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/credential-provider-env": "3.826.0", + "@aws-sdk/credential-provider-http": "3.826.0", + "@aws-sdk/credential-provider-process": "3.826.0", + "@aws-sdk/credential-provider-sso": "3.828.0", + "@aws-sdk/credential-provider-web-identity": "3.828.0", + "@aws-sdk/nested-clients": "3.828.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.828.0.tgz", + "integrity": "sha512-9z3iPwVYOQYNzVZj8qycZaS/BOSKRXWA+QVNQlfEnQ4sA4sOcKR4kmV2h+rJcuBsSFfmOF62ZDxyIBGvvM4t/w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.826.0", + "@aws-sdk/credential-provider-http": "3.826.0", + "@aws-sdk/credential-provider-ini": "3.828.0", + "@aws-sdk/credential-provider-process": "3.826.0", + "@aws-sdk/credential-provider-sso": "3.828.0", + "@aws-sdk/credential-provider-web-identity": "3.828.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.826.0.tgz", + "integrity": "sha512-kURrc4amu3NLtw1yZw7EoLNEVhmOMRUTs+chaNcmS+ERm3yK0nKjaJzmKahmwlTQTSl3wJ8jjK7x962VPo+zWw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.828.0.tgz", + "integrity": "sha512-9CEAXzUDSzOjOCb3XfM15TZhTaM+l07kumZyx2z8NC6T2U4qbCJqn4h8mFlRvYrs6cBj2SN40sD3r5Wp0Cq2Kw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.828.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/token-providers": "3.828.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.828.0.tgz", + "integrity": "sha512-MguDhGHlQBeK9CQ/P4NOY0whAJ4HJU4x+f1dphg3I1sGlccFqfB8Moor2vXNKu0Th2kvAwkn9pr7gGb/+NGR9g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/nested-clients": "3.828.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.821.0.tgz", + "integrity": "sha512-cebgeytKlWOgGczLo3BPvNY9XlzAzGZQANSysgJ2/8PSldmUpXRIF+GKPXDVhXeInWYHIfB8zZi3RqrPoXcNYQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.821.0.tgz", + "integrity": "sha512-zAOoSZKe1njOrtynvK6ZORU57YGv5I7KP4+rwOvUN3ZhJbQ7QPf8gKtFUCYAPRMegaXCKF/ADPtDZBAmM+zZ9g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.826.0.tgz", + "integrity": "sha512-Fz9w8CFYPfSlHEB6feSsi06hdS+s+FB8k5pO4L7IV0tUa78mlhxF/VNlAJaVWYyOkZXl4HPH2K48aapACSQOXw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", + "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.821.0.tgz", + "integrity": "sha512-sKrm80k0t3R0on8aA/WhWFoMaAl4yvdk+riotmMElLUpcMcRXAd1+600uFVrxJqZdbrKQ0mjX0PjT68DlkYXLg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", + "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", + "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.826.0.tgz", + "integrity": "sha512-8F0qWaYKfvD/de1AKccXuigM+gb/IZSncCqxdnFWqd+TFzo9qI9Hh+TpUhWOMYSgxsMsYQ8ipmLzlD/lDhjrmA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/core": "^3.5.3", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.821.0.tgz", + "integrity": "sha512-YYi1Hhr2AYiU/24cQc8HIB+SWbQo6FBkMYojVuz/zgrtkFmALxENGF/21OPg7f/QWd+eadZJRxCjmRwh5F2Cxg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.828.0.tgz", + "integrity": "sha512-nixvI/SETXRdmrVab4D9LvXT3lrXkwAWGWk2GVvQvzlqN1/M/RfClj+o37Sn4FqRkGH9o9g7Fqb1YqZ4mqDAtA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@smithy/core": "^3.5.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.828.0.tgz", + "integrity": "sha512-xmeOILiR9LvfC8MctgeRXXN8nQTwbOvO4wHvgE8tDRsjnBpyyO0j50R4+viHXdMUGtgGkHEXRv8fFNBq54RgnA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.828.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.828.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", + "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.828.0.tgz", + "integrity": "sha512-6817h11Xi6LqnmTnHIwZf4PQB0rIMaRFwkq8/mfR9oOn+hsahxBVDbpgu+q4xzP5q+W3m5Y/din0cJPVrnP6yQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-format-url": "3.821.0", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.826.0.tgz", + "integrity": "sha512-3fEi/zy6tpMzomYosksGtu7jZqGFcdBXoL7YRsG7OEeQzBbOW9B+fVaQZ4jnsViSjzA/yKydLahMrfPnt+iaxg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.828.0.tgz", + "integrity": "sha512-JdOjI/TxkfQpY/bWbdGMdCiePESXTbtl6MfnJxz35zZ3tfHvBnxAWCoYJirdmjzY/j/dFo5oEyS6mQuXAG9w2w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/nested-clients": "3.828.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", + "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.804.0.tgz", + "integrity": "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.828.0.tgz", + "integrity": "sha512-RvKch111SblqdkPzg3oCIdlGxlQs+k+P7Etory9FmxPHyPDvsP1j1c74PmgYqtzzMWmoXTjd+c9naUHh9xG8xg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.821.0.tgz", + "integrity": "sha512-h+xqmPToxDrZ0a7rxE1a8Oh4zpWfZe9oiQUphGtfiGFA6j75UiURH5J3MmGHa/G4t15I3iLLbYtUXxvb1i7evg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", + "integrity": "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", + "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.828.0.tgz", + "integrity": "sha512-LdN6fTBzTlQmc8O8f1wiZN0qF3yBWVGis7NwpWK7FUEzP9bEZRxYfIkV9oV9zpt6iNRze1SedK3JQVB/udxBoA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.828.0", + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", + "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "devOptional": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "devOptional": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@prisma/client": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", + "hasInstallScript": true, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", + "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", + "devOptional": true + }, + "node_modules/@prisma/engines": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", + "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/fetch-engine": "5.22.0", + "@prisma/get-platform": "5.22.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", + "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", + "devOptional": true + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", + "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/get-platform": "5.22.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", + "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.22.0" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, + "node_modules/@smithy/abort-controller": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", + "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", + "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", + "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", + "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.3.tgz", + "integrity": "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.8", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", + "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.4.tgz", + "integrity": "sha512-7XoWfZqWb/QoR/rAU4VSi0mWnO2vu9/ltS6JZ5ZSZv0eovLVfDfu0/AX4ub33RsJTOth3TiFWSHS5YdztvFnig==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.3.1", + "@smithy/util-hex-encoding": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.4.tgz", + "integrity": "sha512-3fb/9SYaYqbpy/z/H3yIi0bYKyAa89y6xPmIqwr2vQiUT2St+avRt8UKwsWt9fEdEasc5d/V+QjrviRaX1JRFA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.1.2.tgz", + "integrity": "sha512-JGtambizrWP50xHgbzZI04IWU7LdI0nh/wGbqH3sJesYToMi2j/DcoElqyOcqEIG/D4tNyxgRuaqBXWE3zOFhQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.4.tgz", + "integrity": "sha512-RD6UwNZ5zISpOWPuhVgRz60GkSIp0dy1fuZmj4RYmqLVRtejFqQ16WmfYDdoSoAjlp1LX+FnZo+/hkdmyyGZ1w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.4.tgz", + "integrity": "sha512-UeJpOmLGhq1SLox79QWw/0n2PFX+oPRE1ZyRMxPIaFEfCqWaqpB7BU9C8kpPOGEhLF7AwEqfFbtwNxGy4ReENA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", + "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.4.tgz", + "integrity": "sha512-WszRiACJiQV3QG6XMV44i5YWlkrlsM5Yxgz4jvsksuu7LDXA6wAtypfPajtNTadzpJy3KyJPoWehYpmZGKUFIQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.0.0", + "@smithy/chunked-blob-reader-native": "^4.0.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", + "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.4.tgz", + "integrity": "sha512-wHo0d8GXyVmpmMh/qOR0R7Y46/G1y6OR8U+bSTB4ppEzRxd1xVAQ9xOE9hOc0bSjhz0ujCPAbfNLkLrpa6cevg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", + "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.4.tgz", + "integrity": "sha512-uGLBVqcOwrLvGh/v/jw423yWHq/ofUGK1W31M2TNspLQbUV1Va0F5kTxtirkoHawODAZcjXTSGi7JwbnPcDPJg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", + "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.11.tgz", + "integrity": "sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "@smithy/core": "^3.5.3", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + "node_modules/@smithy/middleware-retry": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.12.tgz", + "integrity": "sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.5", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", + "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", + "license": "Apache-2.0", "dependencies": { - "@hapi/hoek": "^9.0.0" + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, + "node_modules/@smithy/middleware-stack": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", + "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "node_modules/@smithy/node-config-provider": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", + "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "node_modules/@smithy/node-http-handler": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", + "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@smithy/abort-controller": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@prisma/client": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", - "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", - "hasInstallScript": true, + "node_modules/@smithy/property-provider": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", + "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=16.13" + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", + "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "prisma": "*" + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", + "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" }, - "peerDependenciesMeta": { - "prisma": { - "optional": true - } + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@prisma/debug": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", - "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", - "devOptional": true + "node_modules/@smithy/querystring-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", + "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@prisma/engines": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", - "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", - "devOptional": true, - "hasInstallScript": true, + "node_modules/@smithy/service-error-classification": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.5.tgz", + "integrity": "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==", + "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.22.0", - "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", - "@prisma/fetch-engine": "5.22.0", - "@prisma/get-platform": "5.22.0" + "@smithy/types": "^4.3.1" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@prisma/engines-version": { - "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", - "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", - "devOptional": true + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", + "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@prisma/fetch-engine": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", - "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", - "devOptional": true, + "node_modules/@smithy/signature-v4": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", + "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", + "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.22.0", - "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", - "@prisma/get-platform": "5.22.0" + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@prisma/get-platform": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", - "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", - "devOptional": true, + "node_modules/@smithy/smithy-client": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.3.tgz", + "integrity": "sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA==", + "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.22.0" + "@smithy/core": "^3.5.3", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "node_modules/@smithy/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", + "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", + "license": "Apache-2.0", "dependencies": { - "@hapi/hoek": "^9.0.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + "node_modules/@smithy/url-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", + "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + "node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.19.tgz", + "integrity": "sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.19.tgz", + "integrity": "sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.1.4", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", + "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", + "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.5.tgz", + "integrity": "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.5", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", + "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.5.tgz", + "integrity": "sha512-4QvC49HTteI1gfemu0I1syWovJgPvGn7CVUoN9ZFkdvr/cCFkrEL7qNCdx/2eICqDWEGnnr68oMdSIPCLAriSQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==", + "license": "MIT" }, "node_modules/@swc/helpers": { "version": "0.3.17", @@ -177,25 +1837,25 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "devOptional": true }, "node_modules/@types/body-parser": { "version": "1.19.5", @@ -271,7 +1931,7 @@ "version": "20.17.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz", "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==", - "dev": true, + "devOptional": true, "dependencies": { "undici-types": "~6.19.2" } @@ -285,6 +1945,18 @@ "@types/node": "*" } }, + "node_modules/@types/pg": { + "version": "8.15.4", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.4.tgz", + "integrity": "sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, "node_modules/@types/qs": { "version": "6.9.17", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", @@ -334,7 +2006,7 @@ "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, + "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -346,19 +2018,61 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, + "devOptional": true, "dependencies": { "acorn": "^8.11.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=0.4.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" } }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", @@ -402,6 +2116,12 @@ "node": ">= 6.0.0" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -444,6 +2164,21 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/brotli": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", @@ -452,6 +2187,30 @@ "base64-js": "^1.1.2" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -478,6 +2237,93 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", @@ -486,6 +2332,24 @@ "node": ">=0.8" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -534,7 +2398,21 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } }, "node_modules/crypto-js": { "version": "4.2.0", @@ -560,6 +2438,20 @@ "ms": "2.0.0" } }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/deep-equal": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", @@ -657,7 +2549,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -673,11 +2565,23 @@ "url": "https://dotenvx.com" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -724,6 +2628,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -786,6 +2699,28 @@ "express": "^4.16.2" } }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/finalhandler": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", @@ -827,6 +2762,22 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -881,6 +2832,15 @@ "is-property": "^1.0.2" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -899,6 +2859,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/gopd": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.1.0.tgz", @@ -982,6 +2962,15 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -1008,6 +2997,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1118,6 +3127,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -1253,6 +3271,27 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/joi": { "version": "17.13.3", "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", @@ -1318,7 +3357,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/media-typer": { "version": "0.3.0", @@ -1374,6 +3413,30 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -1517,6 +3580,12 @@ "node": ">= 0.8" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/pako": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", @@ -1530,6 +3599,37 @@ "node": ">= 0.8" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/path-to-regexp": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", @@ -1547,17 +3647,145 @@ "png-js": "^1.0.0" } }, - "node_modules/png-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", - "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "node_modules/pg": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.0.tgz", + "integrity": "sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.9.0", + "pg-pool": "^3.10.0", + "pg-protocol": "^1.10.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.5" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz", + "integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.0.tgz", + "integrity": "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.0.tgz", + "integrity": "sha512-DzZ26On4sQ0KmqnO34muPcmKbhrjmyiO4lCCR0VwEd7MjmiKf5NTg/6+apUEu0NF7ESa37CGzFxH513CoUmWnA==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.0.tgz", + "integrity": "sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/png-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", + "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, "node_modules/prisma": { @@ -1649,6 +3877,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/restructure": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/restructure/-/restructure-2.0.1.tgz", @@ -1784,6 +4021,40 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -1801,6 +4072,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sql-highlight": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", + "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", + "funding": [ + "https://github.com/scriptcoded/sql-highlight?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/scriptcoded" + } + ], + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/sqlstring": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", @@ -1828,6 +4136,114 @@ "node": ">= 0.4" } }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/tiny-inflate": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", @@ -1845,7 +4261,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -1917,11 +4333,140 @@ "node": ">= 0.6" } }, + "node_modules/typeorm": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.24.tgz", + "integrity": "sha512-4IrHG7A0tY8l5gEGXfW56VOMfUVWEkWlH/h5wmcyZ+V8oCiLj7iTPp0lEjMEZVrxEkGSdP9ErgTKHKXQApl/oA==", + "license": "MIT", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "ansis": "^3.17.0", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "dayjs": "^1.11.13", + "debug": "^4.4.0", + "dedent": "^1.6.0", + "dotenv": "^16.4.7", + "glob": "^10.4.5", + "sha.js": "^2.4.11", + "sql-highlight": "^6.0.0", + "tslib": "^2.8.1", + "uuid": "^11.1.0", + "yargs": "^17.7.2" + }, + "bin": { + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, + "engines": { + "node": ">=16.13.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0", + "@sap/hana-client": "^2.12.25", + "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "hdb-pool": "^0.1.6", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0 || ^6.0.0", + "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0", + "reflect-metadata": "^0.1.14 || ^0.2.0", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "hdb-pool": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/typescript": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1934,7 +4479,7 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "devOptional": true }, "node_modules/unicode-properties": { "version": "1.4.1", @@ -1970,11 +4515,24 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "node_modules/vary": { "version": "1.1.2", @@ -1984,6 +4542,21 @@ "node": ">= 0.8" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/which-boxed-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.0.tgz", @@ -2037,11 +4610,188 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } diff --git a/package.json b/package.json index 3057f93..e2f2147 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,8 @@ }, "homepage": "https://github.com/prashantkharade/project#readme", "dependencies": { + "@aws-sdk/client-s3": "^3.828.0", + "@aws-sdk/s3-request-presigner": "^3.828.0", "@prisma/client": "^5.22.0", "body-parser": "^1.20.2", "cors": "^2.8.5", @@ -26,12 +28,15 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "express-async-errors": "^3.1.1", + "helmet": "^8.1.0", "joi": "^17.12.2", "moment-timezone": "^0.5.45", "mysql2": "^3.10.0", "pdfkit": "^0.15.0", + "pg": "^8.16.0", "reflect-metadata": "^0.2.2", - "tsyringe": "^4.8.0" + "tsyringe": "^4.8.0", + "typeorm": "^0.3.24" }, "devDependencies": { "@types/cors": "^2.8.17", @@ -39,6 +44,7 @@ "@types/mime-types": "^2.1.4", "@types/node": "^20.3.1", "@types/pdfkit": "^0.13.4", + "@types/pg": "^8.15.4", "prisma": "^5.19.1", "ts-node": "^10.9.1", "typescript": "^5.1.3" diff --git a/src/api/base.controller.ts b/src/api/base.controller.ts index 20c92c5..1f07582 100644 --- a/src/api/base.controller.ts +++ b/src/api/base.controller.ts @@ -1,6 +1,6 @@ import express from 'express'; // import { Authorizer } from '../auth/authorizer'; -import { ErrorHandler } from '../common/error.handler'; +import { ErrorHandler } from '../common/handlers/error.handler'; import { error } from 'console'; // import { Loader } from '../startup/loader'; diff --git a/src/api/base.validator.ts b/src/api/base.validator.ts index af19fb0..5e3c99a 100644 --- a/src/api/base.validator.ts +++ b/src/api/base.validator.ts @@ -2,7 +2,7 @@ import joi from 'joi'; import express from 'express'; import { ErrorHandler -} from '../common/error.handler'; +} from '../common/handlers/error.handler'; import { uuid } from '../domain.types/miscellaneous/system.types'; ////////////////////////////////////////////////////////////////// diff --git a/src/api/form.section/form.section.auth.ts b/src/api/form.section/form.section.auth.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/api/form.section/form.section.controller.ts b/src/api/form.section/form.section.controller.ts index 24eaa52..5edfde5 100644 --- a/src/api/form.section/form.section.controller.ts +++ b/src/api/form.section/form.section.controller.ts @@ -1,12 +1,13 @@ import express from 'express'; -import { ResponseHandler } from '../../common/response.handler'; +import { ResponseHandler } from '../../common/handlers/response.handler'; import { BaseController } from '../base.controller'; -import { ErrorHandler } from '../../common/error.handler'; +import { ErrorHandler } from '../../common/handlers/error.handler'; import { uuid } from '../../domain.types/miscellaneous/system.types'; import { error } from 'console'; import { FormSectionValidator } from './form.section.validator'; -import { FormSectionService } from '../../services/form.section.service'; +import { FormSectionService } from '../../services/form.section/form.section.service'; import { FormSectionCreateModel, FormSectionSearchFilters, FormSectionUpdateModel } from '../../domain.types/forms/form.section.domain.types'; +import { Injector } from '../../startup/injector'; /////////////////////////////////////////////////////////////////////////////////////// @@ -14,7 +15,9 @@ export class FormSectionController extends BaseController { //#region member variables and constructors - _service: FormSectionService = new FormSectionService(); + // _service: FormSectionService = new FormSectionService(); + + _service: FormSectionService = Injector.Container.resolve(FormSectionService); _validator: FormSectionValidator = new FormSectionValidator(); diff --git a/src/api/form.section/form.section.validator.ts b/src/api/form.section/form.section.validator.ts index c49e8cd..dd57a4c 100644 --- a/src/api/form.section/form.section.validator.ts +++ b/src/api/form.section/form.section.validator.ts @@ -2,7 +2,7 @@ import joi, { optional } from 'joi'; import express from 'express'; import { ErrorHandler -} from '../../common/error.handler'; +} from '../../common/handlers/error.handler'; import BaseValidator from '../base.validator'; import { FormSectionCreateModel, FormSectionSearchFilters, FormSectionUpdateModel } from '../../domain.types/forms/form.section.domain.types'; import { generateDisplayCode } from '../../domain.types/miscellaneous/display.code'; diff --git a/src/api/form.submission/form.auth.ts b/src/api/form.submission/form.auth.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/api/form.submission/form.controller.ts b/src/api/form.submission/form.controller.ts index ef9779a..95d9b92 100644 --- a/src/api/form.submission/form.controller.ts +++ b/src/api/form.submission/form.controller.ts @@ -1,15 +1,15 @@ import express from 'express'; -import { ResponseHandler } from '../../common/response.handler'; +import { ResponseHandler } from '../../common/handlers/response.handler'; import { FormValidator } from './form.validator'; import { BaseController } from '../base.controller'; -import { ErrorHandler } from '../../common/error.handler'; +import { ErrorHandler } from '../../common/handlers/error.handler'; import { uuid } from '../../domain.types/miscellaneous/system.types'; -import { FormService } from '../../services/form.submission.service'; +import { FormService } from '../../services/form.submission/form.submission.service'; import { FormSubmissionCreateModel, FormSubmissionSearchFilters, FormSubmissionUpdateModel } from '../../domain.types/forms/form.submission.domain.types'; import { error } from 'console'; -import { FormTemplateService } from '../../services/form.template.service'; +import { FormTemplateService } from '../../services/form.template/form.template.service'; import * as crypto from "crypto"; -import { container } from 'tsyringe'; +import { Injector } from '../../startup/injector'; /////////////////////////////////////////////////////////////////////////////////////// @@ -17,9 +17,9 @@ export class FormController extends BaseController { //#region member variables and constructors - _service: FormService = container.resolve(FormService); + _service: FormService = Injector.Container.resolve(FormService); - _formTemplateService = container.resolve(FormTemplateService); + _formTemplateService = Injector.Container.resolve(FormTemplateService); _validator: FormValidator = new FormValidator(); diff --git a/src/api/form.submission/form.validator.ts b/src/api/form.submission/form.validator.ts index 9931b8f..8913f50 100644 --- a/src/api/form.submission/form.validator.ts +++ b/src/api/form.submission/form.validator.ts @@ -2,9 +2,11 @@ import joi from 'joi'; import express from 'express'; import { ErrorHandler -} from '../../common/error.handler'; +} from '../../common/handlers/error.handler'; import BaseValidator from '../base.validator'; -import { FormStatus, FormSubmissionCreateModel, FormSubmissionDto, FormSubmissionSearchFilters, FormSubmissionUpdateModel, FormType } from '../../domain.types/forms/form.submission.domain.types'; +import { FormSubmissionCreateModel, FormSubmissionDto, FormSubmissionSearchFilters, FormSubmissionUpdateModel } from '../../domain.types/forms/form.submission.domain.types'; +import { FormStatus } from '../../database/sql/typeorm/models/form.submission/form.submission.model'; +import { FormType } from '../../database/sql/typeorm/models/form.template/form.template.model'; import { ParsedQs } from 'qs'; import { TimeHelper } from '../../common/time.helper'; import { DurationType } from '../../miscellaneous/time.types'; @@ -100,9 +102,9 @@ export class FormValidator extends BaseValidator { throw new ApiError('Form link is expired!', 400); } - if (submission.Status !== FormStatus.InProgress) { - throw new ApiError('Please save the form first!', 400); - } + // if (submission.Status !== FormStatus.InProgress) { + // throw new ApiError('Please save the form first!', 400); + // } } private getSearchFilters = (query: ParsedQs): FormSubmissionSearchFilters => { diff --git a/src/api/form.template/form.template.auth.ts b/src/api/form.template/form.template.auth.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/api/form.template/form.template.controller.ts b/src/api/form.template/form.template.controller.ts index cf3615d..26c9a77 100644 --- a/src/api/form.template/form.template.controller.ts +++ b/src/api/form.template/form.template.controller.ts @@ -1,26 +1,27 @@ import express from 'express'; -import { ResponseHandler } from '../../common/response.handler'; +import { ResponseHandler } from '../../common/handlers/response.handler'; import { FormTemplateValidator } from './form.template.validator'; import { BaseController } from '../base.controller'; -import { ErrorHandler } from '../../common/error.handler'; +import { ErrorHandler } from '../../common/handlers/error.handler'; import { uuid } from '../../domain.types/miscellaneous/system.types'; import { error } from 'console'; -import { FormTemplateService } from '../../services/form.template.service'; +import { FormTemplateService } from '../../services/form.template/form.template.service'; import { FormTemplateCreateModel, FormTemplateSearchFilters, FormTemplateUpdateModel } from '../../domain.types/forms/form.template.domain.types'; -import { FormSectionService } from '../../services/form.section.service'; +import { FormSectionService } from '../../services/form.section/form.section.service'; import { generateDisplayCode } from '../../domain.types/miscellaneous/display.code'; import { ApiError } from '../../common/api.error'; import { Helper } from '../../domain.types/miscellaneous/helper'; import fs from 'fs'; import { container } from 'tsyringe'; +import { Injector } from '../../startup/injector'; /////////////////////////////////////////////////////////////////////////////////////// export class FormTemplateController extends BaseController { - _service: FormTemplateService = container.resolve(FormTemplateService); + _service: FormTemplateService = Injector.Container.resolve(FormTemplateService); - _section: FormSectionService = container.resolve(FormSectionService); + _section: FormSectionService = Injector.Container.resolve(FormSectionService); _validator: FormTemplateValidator = new FormTemplateValidator(); @@ -28,6 +29,21 @@ export class FormTemplateController extends BaseController { super(); } + //#endregion + + // getAll = async (request: express.Request, response: express.Response) => { + // try { + // const record = await this._service.allFormTemplates(); + // if (record === null) { + // ErrorHandler.throwInternalServerError('Unable to add Form!', error); + // } + // const message = 'All Form templates retrived successfully!'; + // return ResponseHandler.success(request, response, message, 201, record); + // } catch (error) { + // ResponseHandler.handleError(request, response, error); + // } + // } + create = async (request: express.Request, response: express.Response) => { try { let model: FormTemplateCreateModel = await this._validator.validateCreateRequest(request); @@ -83,7 +99,46 @@ export class FormTemplateController extends BaseController { } }; + exportTemplate = async (request: express.Request, response: express.Response): Promise => { + try { + const id: string = await this._validator.validateParamAsUUID(request, 'id'); + + const assessmentTemplate = await this._service.getById(id); + if (!assessmentTemplate) { + throw new ApiError('Cannot find assessment template!', 404); + } + + const templateObj = await this._service.readTemplateObjToExport(assessmentTemplate.id); + + const { dateFolder, filename, sourceFileLocation } = await Helper.storeTemplateToFileLocally(templateObj); + const mimeType = Helper.getMimeType(sourceFileLocation); + response.setHeader('Content-Type', mimeType); + response.setHeader('Content-Disposition', `attachment; filename=${filename}`); + + const filestream = fs.createReadStream(sourceFileLocation); + filestream.pipe(response); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + previewTemplate = async (request: express.Request, response: express.Response): Promise => { + try { + const id: string = await this._validator.validateParamAsUUID(request, 'id'); + + const templateObj = await this._service.previewTemplate(id); + + if (!templateObj) { + throw new ApiError('Cannot find assessment template!', 404); + } + + const message = 'Form templated retrived successfully!'; + ResponseHandler.success(request, response, message, 200, templateObj); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; update = async (request: express.Request, response: express.Response) => { try { diff --git a/src/api/form.template/form.template.router.ts b/src/api/form.template/form.template.router.ts index 97699cf..46d2864 100644 --- a/src/api/form.template/form.template.router.ts +++ b/src/api/form.template/form.template.router.ts @@ -9,12 +9,15 @@ export const register = (app: express.Application): void => { const controller = new FormTemplateController(); router.get('/search', controller.search); + // router.get('/all', controller.getAll); router.post('/', controller.create); router.put('/:id', controller.update); router.get('/:id/details', controller.getDetailsById); router.get('/:id', controller.getById); router.delete('/:id', controller.delete); router.get('/:id/submissions', controller.submissions) + router.get('/:id/export', controller.exportTemplate) + router.get('/:id/preview', controller.previewTemplate) app.use('/api/v1/form-templates', router); }; diff --git a/src/api/form.template/form.template.validator.ts b/src/api/form.template/form.template.validator.ts index 598bd39..b0ef893 100644 --- a/src/api/form.template/form.template.validator.ts +++ b/src/api/form.template/form.template.validator.ts @@ -2,7 +2,7 @@ import joi from 'joi'; import express from 'express'; import { ErrorHandler -} from '../../common/error.handler'; +} from '../../common/handlers/error.handler'; import BaseValidator from '../base.validator'; import { FormTemplateCreateModel, FormTemplateSearchFilters, FormTemplateUpdateModel } from '../../domain.types/forms/form.template.domain.types'; import { generateDisplayCode } from '../../domain.types/miscellaneous/display.code'; @@ -17,7 +17,7 @@ export class FormTemplateValidator extends BaseValidator { Title: joi.string().required(), Description: joi.string().max(512).optional(), CurrentVersion: joi.number().optional(), - TenantId: joi.string().optional(), + TenantCode: joi.string().optional(), Type: joi.string().required(), ItemsPerPage: joi.string().required(), DisplayCode: joi.string().optional(), @@ -30,9 +30,9 @@ export class FormTemplateValidator extends BaseValidator { Title: request.body.Title, Description: request.body.Description ?? null, CurrentVersion: request.body.CurrentVersion ?? 1, - TenantId: request.body.TenantId, + TenantCode: request.body.TenantCode, Type: request.body.Type, - ItemsPerPage: request.body.ItemsPerPage, + // ItemsPerPage: request.body.ItemsPerPage, DisplayCode: request.body.DisplayCode ?? generateDisplayCode(30, 'ASSESS_TEMP_#'), OwnerUserId: request.body.OwnerUserId, RootSectionId: request.body.RootSectionId, @@ -49,18 +49,26 @@ export class FormTemplateValidator extends BaseValidator { Title: joi.string().optional(), Description: joi.string().max(512).optional(), CurrentVersion: joi.number().optional(), - TenantId: joi.string().optional(), + TenantCode: joi.string().optional(), Type: joi.string().optional(), ItemsPerPage: joi.string().optional(), + DisplayCode: joi.string().max(64).optional(), + OwnerUserId: joi.string().uuid().optional(), + RootSectionId: joi.string().uuid().optional(), + DefaultSectionNumbering: joi.boolean().optional() }); await schema.validateAsync(request.body); return { Title: request.body.Title ?? null, Description: request.body.Description ?? null, CurrentVersion: request.body.CurrentVersion ?? null, - TenantId: request.body.TenantId ?? null, + TenantCode: request.body.TenantCode ?? null, Type: request.body.Type ?? null, - ItemsPerPage: request.body.ItemsPerPage ?? null + // ItemsPerPage: request.body.ItemsPerPage ?? null, + DisplayCode: request.body.DisplayCode ?? null, + OwnerUserId: request.body.OwnerUserId ?? null, + RootSectionId: request.body.RootSectionId ?? null, + DefaultSectionNumbering: request.body.DefaultSectionNumbering ?? null }; } catch (error) { ErrorHandler.handleValidationError(error); @@ -76,7 +84,6 @@ export class FormTemplateValidator extends BaseValidator { currentVersion: joi.number().optional(), type: joi.string().optional(), displayCode: joi.string().optional(), - tenantId: joi.string().uuid().optional(), ownerUserId: joi.string().uuid().optional(), rootSectionId: joi.string().uuid().optional(), defaultSectionNumbering: joi.boolean().optional(), @@ -109,7 +116,7 @@ export class FormTemplateValidator extends BaseValidator { var tenantCode = query.tenantCode ? query.tenantCode : null; if (tenantCode != null) { - filters['TenantId'] = tenantCode; + filters['TenantCode'] = tenantCode; } var description = query.description ? query.description : null; diff --git a/src/api/question.response/question.response.auth.ts b/src/api/question.response/question.response.auth.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/api/question.response/question.response.controller.ts b/src/api/question.response/question.response.controller.ts index c295623..c9ac20a 100644 --- a/src/api/question.response/question.response.controller.ts +++ b/src/api/question.response/question.response.controller.ts @@ -1,18 +1,20 @@ import express from 'express'; -import { ResponseHandler } from '../../common/response.handler'; +import { ResponseHandler } from '../../common/handlers/response.handler'; import { BaseController } from '../base.controller'; -import { ErrorHandler } from '../../common/error.handler'; +import { ErrorHandler } from '../../common/handlers/error.handler'; import { uuid } from '../../domain.types/miscellaneous/system.types'; import { error } from 'console'; import { QuestionResponseValidator } from './question.response.validator'; -import { ResponseService } from '../../services/question.response.service'; +import { ResponseService } from '../../services/question.response/question.response.service'; import { QuestionResponseCreateModel, QuestionResponseSaveModel, QuestionResponseSearchFilters, QuestionResponseUpdateModel } from '../../domain.types/forms/response.domain.types'; -import { QueryResponseType } from '@prisma/client'; +// import { QueryResponseType } from '@prisma/client'; +import { QueryResponseType } from '../../database/sql/typeorm/models/question/question.model'; import * as path from 'path'; import * as fs from 'fs'; import { container } from 'tsyringe'; -import { FormService } from '../../services/form.submission.service'; -import { FormStatus } from '../../domain.types/forms/form.submission.domain.types'; +import { FormService } from '../../services/form.submission/form.submission.service'; +import { FormStatus } from '../../database/sql/typeorm/models/form.submission/form.submission.model'; +import { Injector } from '../../startup/injector'; /////////////////////////////////////////////////////////////////////////////////////// @@ -20,9 +22,9 @@ export class QuestionResponseController extends BaseController { //#region member variables and constructors - _service: ResponseService = new ResponseService(); + _service: ResponseService = Injector.Container.resolve(ResponseService); - _formService: FormService = container.resolve(FormService); + _formService: FormService = Injector.Container.resolve(FormService); _validator: QuestionResponseValidator = new QuestionResponseValidator(); diff --git a/src/api/question.response/question.response.validator.ts b/src/api/question.response/question.response.validator.ts index 6012e0c..cc2cd9d 100644 --- a/src/api/question.response/question.response.validator.ts +++ b/src/api/question.response/question.response.validator.ts @@ -1,6 +1,6 @@ import joi from "joi"; import express from "express"; -import { ErrorHandler } from "../../common/error.handler"; +import { ErrorHandler } from "../../common/handlers/error.handler"; import BaseValidator from "../base.validator"; import { QuestionResponseCreateModel, @@ -9,7 +9,8 @@ import { QuestionResponseUpdateModel, } from "../../domain.types/forms/response.domain.types"; import { ParsedQs } from 'qs'; -import { FormStatus, FormSubmissionDto } from "../../domain.types/forms/form.submission.domain.types"; +import { FormSubmissionDto } from "../../domain.types/forms/form.submission.domain.types"; +import { FormStatus } from "../../database/sql/typeorm/models/form.submission/form.submission.model"; import { ApiError } from "../../common/api.error"; /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/api/question/question.auth.ts b/src/api/question/question.auth.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/api/question/question.controller.ts b/src/api/question/question.controller.ts index 72fda1e..49c59b9 100644 --- a/src/api/question/question.controller.ts +++ b/src/api/question/question.controller.ts @@ -1,12 +1,13 @@ import express from 'express'; -import { ResponseHandler } from '../../common/response.handler'; +import { ResponseHandler } from '../../common/handlers/response.handler'; import { BaseController } from '../base.controller'; -import { ErrorHandler } from '../../common/error.handler'; +import { ErrorHandler } from '../../common/handlers/error.handler'; import { uuid } from '../../domain.types/miscellaneous/system.types'; import { error } from 'console'; -import { QuestionService } from '../../services/question.service'; +import { QuestionService } from '../../services/question/question.service'; import { QuestionValidator } from './question.validator'; import { QuestionCreateModel, QuestionSearchFilters, QuestionUpdateModel } from '../../domain.types/forms/question.domain.types'; +import { Injector } from '../../startup/injector'; /////////////////////////////////////////////////////////////////////////////////////// @@ -14,7 +15,7 @@ export class QuestionController extends BaseController { //#region member variables and constructors - _service: QuestionService = new QuestionService(); + _service: QuestionService = Injector.Container.resolve(QuestionService); _validator: QuestionValidator = new QuestionValidator(); @@ -98,39 +99,6 @@ export class QuestionController extends BaseController { } }; - updateSequence = async (request: express.Request, response: express.Response) => { - try { - const id = await this._validator.validateParamAsUUID(request, 'id'); - const model = await this._validator.validateUpdateRequest(request); - - const questionRecord = await this._service.getById(id); - const oldSeq = Number(questionRecord.Sequence); - const newSeq = Number(model.Sequence); - const parentSectionId = model.ParentSectionId; - - if (oldSeq < newSeq) { - await this._service.decrementSequenceInRange(oldSeq + 1, newSeq, parentSectionId); - } else if (oldSeq > newSeq) { - await this._service.incrementSequenceInRange(newSeq, oldSeq - 1, parentSectionId); - } - - const updatedRecord = await this._service.updateSequence(id, { - ...model, - Sequence: newSeq - }); - - ResponseHandler.success(request, response, 'Sequence updated', 200, updatedRecord); - } catch (error) { - ResponseHandler.handleError(request, response, error); - } - }; - - - - - - - delete = async (request: express.Request, response: express.Response): Promise => { try { // await this.authorize('Form.Delete', request, response); diff --git a/src/api/question/question.router.ts b/src/api/question/question.router.ts index 120954b..26bbbeb 100644 --- a/src/api/question/question.router.ts +++ b/src/api/question/question.router.ts @@ -13,7 +13,6 @@ export const register = (app: express.Application): void => { // router.get('/all', controller.getAll); router.post('/', controller.create); router.put('/:id', controller.update); - router.put('/sequence/:id', controller.updateSequence); router.get('/:id', controller.getById); router.get('/by-template-id/:templateId', controller.getByTemplateId); router.delete('/:id', controller.delete); diff --git a/src/api/question/question.validator.ts b/src/api/question/question.validator.ts index b01ed32..2a694b1 100644 --- a/src/api/question/question.validator.ts +++ b/src/api/question/question.validator.ts @@ -2,7 +2,7 @@ import joi from 'joi'; import express from 'express'; import { ErrorHandler -} from '../../common/error.handler'; +} from '../../common/handlers/error.handler'; import BaseValidator from '../base.validator'; import { QuestionCreateModel, QuestionSearchFilters, QuestionUpdateModel } from '../../domain.types/forms/question.domain.types'; import { generateDisplayCode } from '../../domain.types/miscellaneous/display.code'; @@ -73,7 +73,7 @@ export class QuestionValidator extends BaseValidator { CorrectAnswer: joi.string().optional(), IsRequired: joi.boolean().optional(), Hint: joi.string().optional(), - Sequence: joi.number().optional(), + Sequence: joi.string().optional(), Options: joi.array().items(optionSchema).optional(), // Validate Options as an array of objects // FileResourceId: joi.string().uuid(), QuestionImageUrl: joi.string().optional(), @@ -121,8 +121,6 @@ export class QuestionValidator extends BaseValidator { CorrectAnswer: joi.string().optional(), IsRequired: joi.boolean().optional(), Hint: joi.string().optional(), - Sequence: joi.number().optional(), - ParentSectionId: joi.string().uuid().optional(), // Options: joi.array().items(joi.string().optional()).optional(), Options: joi.array().items(optionSchema).optional(), // Validate Options as an array of objects // FileResourceId : joi.string().uuid().optional(), @@ -141,8 +139,6 @@ export class QuestionValidator extends BaseValidator { IsRequired: request.body.IsRequired ?? null, Hint: request.body.Hint ?? null, Options: request.body.Options ?? null, - Sequence: request.body.Sequence ?? null, - ParentSectionId: request.body.ParentSectionId ?? null, // FileResourceId : request.body.FileResourceId ?? null, QuestionImageUrl: request.body.QuestionImageUrl ?? null, RangeMin: request.body.RangeMin ?? null, @@ -167,7 +163,6 @@ export class QuestionValidator extends BaseValidator { correctAnswer: joi.string().optional(), isRequired: joi.boolean().optional(), hint: joi.string().optional(), - sequence: joi.number().optional(), options: joi.array().items(joi.string().optional()).optional(), // FileResourceId : joi.string().uuid().optional(), questionImageUrl: joi.string().optional(), @@ -231,10 +226,6 @@ export class QuestionValidator extends BaseValidator { if (hint != null) { filters['hint'] = hint; } - var sequence = query.sequence ? query.sequence : null; - if (sequence != null) { - filters['sequence'] = sequence; - } var options = query.options ? query.options : null; if (options != null) { filters['options'] = options; diff --git a/src/api/user/user.controller.ts b/src/api/user/user.controller.ts index 8c67169..256c9ee 100644 --- a/src/api/user/user.controller.ts +++ b/src/api/user/user.controller.ts @@ -1,12 +1,13 @@ import express from 'express'; -import { ResponseHandler } from '../../common/response.handler'; +import { ResponseHandler } from '../../common/handlers/response.handler'; import { UserValidator } from './user.validator'; import { BaseController } from '../base.controller'; -import { ErrorHandler } from '../../common/error.handler'; +import { ErrorHandler } from '../../common/handlers/error.handler'; import { uuid } from '../../domain.types/miscellaneous/system.types'; import { error } from 'console'; import { UserCreateModel, UserSearchFilters, UserUpdateModel } from '../../domain.types/forms/user.domain.types'; -import { UserService } from '../../services/user.service'; +import { UserService } from '../../services/user/user.service'; +import { Injector } from '../../startup/injector'; /////////////////////////////////////////////////////////////////////////////////////// @@ -14,7 +15,7 @@ export class UserController extends BaseController { //#region member variables and constructors - _service: UserService = new UserService(); + _service: UserService = Injector.Container.resolve(UserService); _validator: UserValidator = new UserValidator(); diff --git a/src/api/user/user.validator.ts b/src/api/user/user.validator.ts index aa7043e..e946aa0 100644 --- a/src/api/user/user.validator.ts +++ b/src/api/user/user.validator.ts @@ -2,7 +2,7 @@ import joi from 'joi'; import express from 'express'; import { ErrorHandler -} from '../../common/error.handler'; +} from '../../common/handlers/error.handler'; import BaseValidator from '../base.validator'; import { UserCreateModel, UserSearchFilters, UserUpdateModel } from '../../domain.types/forms/user.domain.types'; import { ParsedQs } from 'qs'; diff --git a/src/app.ts b/src/app.ts index e6a9f6d..f3a26a8 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,10 +1,18 @@ import express from 'express'; import "reflect-metadata"; +import cors from 'cors'; import { Router } from './startup/router'; import { execSync } from 'child_process'; // import { Logger } from './startup/logger'; import mysql from 'mysql2/promise'; import { Logger } from './common/logger'; +import helmet from 'helmet'; +import { ConfigurationManager } from './config/configuration.manager'; +import { Loader } from './startup/loader'; +import { Injector } from './startup/injector'; +import { DatabaseClient } from './common/database.utils/dialect.clients/database.client'; +import { DatabaseSchemaType } from './common/database.utils/database.config'; +import { PrimaryDatabaseConnector } from './database/database.connector'; // import ErrsoleMySQL from 'errsole-mysql'; // import errsole from 'errsole'; @@ -33,10 +41,8 @@ export default class Application { } - start = async () => { + start = async (): Promise => { try { - this._app.use(express.json()); - this._app.use(express.urlencoded()); // errsole.initialize({ // storage: new ErrsoleMySQL({ @@ -47,14 +53,57 @@ export default class Application { // }) // }); - this.migrate(); - this._router.init(); - this.listen(); + //Load configurations + ConfigurationManager.loadConfigurations(); + + //Load the modules + await Loader.init(); + + //Set-up middlewares + await this.setupMiddlewares(); + + //Connect databases + await connectDatabase_Primary(); + + + + // this.migrate(); + + + //Set the routes + await this._router.init(); + + //Seed the service + // await Loader.seeder.init(); + + //Start listening + await this.listen(); } catch (error) { + Logger.instance().log('An error occurred while starting reancare-api service.' + error.message); } } + private setupMiddlewares = async (): Promise => { + + return new Promise((resolve, reject) => { + try { + this._app.use(express.urlencoded({ limit: '50mb', extended: true })); + this._app.use(express.json( { limit: '50mb' })); + this._app.use(helmet()); + this._app.use(cors()); + + //TODO: Move this to upload specific routes. Use router.use() method + // this.useFileUploadMiddleware(); + + resolve(true); + } + catch (error) { + reject(error); + } + }); + }; + private listen = async () => { return new Promise((resolve, reject) => { try { @@ -131,3 +180,12 @@ export default class Application { } + +async function connectDatabase_Primary() { + if (process.env.NODE_ENV === 'test') { + const databaseClient = Injector.Container.resolve(DatabaseClient); + await databaseClient.dropDb(DatabaseSchemaType.Primary); + } + const primaryDatabaseConnector = Injector.Container.resolve(PrimaryDatabaseConnector); + await primaryDatabaseConnector.init(); +} diff --git a/src/common/database.utils/database.config.ts b/src/common/database.utils/database.config.ts new file mode 100644 index 0000000..e72602d --- /dev/null +++ b/src/common/database.utils/database.config.ts @@ -0,0 +1,92 @@ +import { DatabaseDialect } from "../../domain.types/miscellaneous/system.types"; +import * as dotenv from 'dotenv'; +import { Logger } from "../../common/logger"; +import { ConfigurationManager } from "../../config/configuration.manager"; + +///////////////////////////////////////////////////////////////////////////// + +export enum DatabaseSchemaType { + Primary = 'Primary', + EHRInsights = 'EHRInsights', + AwardsFacts = 'AwardsFacts' +} + +export interface DatabaseConfiguration { + Dialect : DatabaseDialect; + DatabaseName : string; + Host : string; + Port : number; + Username : string; + Password : string; + ConnectionString: string; + Pool : { + Max : number, + Min : number, + Acquire: number, + Idle : number, + }, + Cache : boolean; + Synchronize: boolean; + Logging : boolean; +} + +export const databaseConfig = (schemaType: DatabaseSchemaType) + : DatabaseConfiguration => { + + const dialect = process.env.DB_DIALECT as DatabaseDialect; + const config: DatabaseConfiguration = { + Dialect : dialect, + DatabaseName : process.env.DB_NAME, + Host : process.env.DB_HOST, + Port : parseInt(process.env.DB_PORT), + Username : process.env.DB_USER_NAME, + Password : process.env.DB_USER_PASSWORD, + ConnectionString : process.env.CONNECTION_STRING ?? null, + Pool : { + Max : 20, + Min : 0, + Acquire : 30000, + Idle : 10000, + }, + Cache : true, + Logging : true, + Synchronize : true + }; + + if (schemaType === DatabaseSchemaType.EHRInsights && + ConfigurationManager.EHRAnalyticsEnabled()) { + config.DatabaseName = process.env.DB_NAME_EHR_INSIGHTS; + } + else if (schemaType === DatabaseSchemaType.AwardsFacts && + ConfigurationManager.GamificationEnabled()) { + config.DatabaseName = process.env.DB_NAME_AWARDS_FACTS; + } + + return config; +}; + +////////////////////////////////////////////////////////////////////////////////// + +if (typeof process.env.NODE_ENV === 'undefined') { + dotenv.config(); +} + +Logger.instance().log('================================================'); +Logger.instance().log('Environment : ' + process.env.NODE_ENV); +Logger.instance().log('Database dialect : ' + process.env.DB_DIALECT); +Logger.instance().log('Database host : ' + process.env.DB_HOST); +Logger.instance().log('Database port : ' + process.env.DB_PORT); +Logger.instance().log('Database user-name : ' + process.env.DB_USER_NAME); +Logger.instance().log('Primary database name : ' + process.env.DB_NAME); + +if (ConfigurationManager.EHRAnalyticsEnabled()) { + Logger.instance().log('EHR insights database name : ' + process.env.DB_NAME_EHR_INSIGHTS); +} +if (ConfigurationManager.GamificationEnabled()) { + Logger.instance().log('Awards facts database name : ' + process.env.DB_NAME_AWARDS_FACTS); +} + +Logger.instance().log('================================================'); + +////////////////////////////////////////////////////////////////////////////////// + diff --git a/src/common/database.utils/dialect.clients/database.client.interface.ts b/src/common/database.utils/dialect.clients/database.client.interface.ts new file mode 100644 index 0000000..cdcd5cf --- /dev/null +++ b/src/common/database.utils/dialect.clients/database.client.interface.ts @@ -0,0 +1,11 @@ +import { DatabaseSchemaType } from "../database.config"; + +export interface IDatabaseClient { + + createDb(schemaType: DatabaseSchemaType): Promise; + + dropDb(schemaType: DatabaseSchemaType): Promise; + + execute(schemaType: DatabaseSchemaType, query: string): Promise; + +} diff --git a/src/common/database.utils/dialect.clients/database.client.ts b/src/common/database.utils/dialect.clients/database.client.ts new file mode 100644 index 0000000..69b0484 --- /dev/null +++ b/src/common/database.utils/dialect.clients/database.client.ts @@ -0,0 +1,36 @@ +import { DatabaseSchemaType } from "../database.config"; +import { DatabaseDialect } from '../../../domain.types/miscellaneous/system.types'; +import { MysqlClient } from "./mysql.client"; +import { PostgresqlClient } from "./postgresql.client"; + +////////////////////////////////////////////////////////////////////////////// + +export class DatabaseClient { + + _client = null; + + constructor() { + const dialect = process.env.DB_DIALECT as DatabaseDialect; + if (dialect === 'mysql') { + this._client = MysqlClient.getInstance(); + } else if (dialect === 'postgres') { + this._client = PostgresqlClient.getInstance(); + } else { + // this._client = SQLiteClient.getInstance(); + } + + } + + public createDb = async (schemaType: DatabaseSchemaType): Promise => { + return await this._client.createDb(schemaType); + }; + + public dropDb = async (schemaType: DatabaseSchemaType): Promise => { + return await this._client.dropDb(schemaType); + }; + + public executeQuery = async (schemaType: DatabaseSchemaType, query: string): Promise => { + return await this._client.executeQuery(schemaType, query); + }; + +} diff --git a/src/common/database.utils/dialect.clients/mysql.client.ts b/src/common/database.utils/dialect.clients/mysql.client.ts new file mode 100644 index 0000000..c137ce0 --- /dev/null +++ b/src/common/database.utils/dialect.clients/mysql.client.ts @@ -0,0 +1,96 @@ + +// eslint-disable-next-line @typescript-eslint/no-var-requires +import mysql, { Connection } from 'mysql2/promise'; +import { Logger } from '../../logger'; +import { DatabaseSchemaType, databaseConfig } from '../database.config'; +import { IDatabaseClient } from './database.client.interface'; + +////////////////////////////////////////////////////////////////////////////// + +export class MysqlClient implements IDatabaseClient { + + private connection: Connection = null; + + private static instance: MysqlClient = null; + + private constructor() {} + + public static getInstance() { + return this.instance || (this.instance = new MysqlClient()); + } + + public connect = async (schemaType: DatabaseSchemaType): Promise => { + try { + const config = databaseConfig(schemaType); + this.connection = await mysql.createConnection({ + database : config.DatabaseName, + host : config.Host, + user : config.Username, + password : config.Password, + }); + + } catch (error) { + Logger.instance().log(error.message); + Logger.instance().log(`Error trace: ${error.stack}`); + } + }; + + public createDb = async (schemaType: DatabaseSchemaType): Promise => { + try { + const config = databaseConfig(schemaType); + //var query = `CREATE DATABASE ${config.database} CHARACTER SET utf8 COLLATE utf8_general_ci;`; + const query = `CREATE DATABASE ${config.DatabaseName}`; + return await this.execute(schemaType, query); + } catch (error) { + Logger.instance().log(error.message); + } + }; + + public dropDb = async (schemaType: DatabaseSchemaType): Promise => { + try { + const config = databaseConfig(schemaType); + const query = `DROP DATABASE IF EXISTS ${config.DatabaseName}`; + return await this.execute(schemaType, query); + } catch (error) { + Logger.instance().log(error.message); + } + }; + + public executeQuery = async (query: string): Promise => { + try { + const result = await this.connection.query(query); + return result; + } catch (error) { + Logger.instance().log(error.message); + Logger.instance().log(`Error trace: ${error.stack}`); + } + return null; + }; + + public execute = async (schemaType: DatabaseSchemaType, query: string): Promise => { + + try { + const config = databaseConfig(schemaType); + + const connection = await mysql.createConnection({ + host : config.Host, + user : config.Username, + password : config.Password, + }); + + await connection.query(query); + return true; + } catch (error) { + Logger.instance().log(error.message); + } + }; + + public closeDbConnection = async () => { + try { + await this.connection.end(); + } catch (error) { + Logger.instance().log(error.message); + } + }; + +} diff --git a/src/common/database.utils/dialect.clients/postgresql.client.ts b/src/common/database.utils/dialect.clients/postgresql.client.ts new file mode 100644 index 0000000..0a3fa67 --- /dev/null +++ b/src/common/database.utils/dialect.clients/postgresql.client.ts @@ -0,0 +1,85 @@ +import { Client } from 'pg'; +import { Logger } from '../../logger'; +import { IDatabaseClient } from './database.client.interface'; +import { DatabaseSchemaType, databaseConfig } from '../database.config'; + +//////////////////////////////////////////////////////////////// + +export class PostgresqlClient implements IDatabaseClient { + + private connection: Client = null; + + private static instance: PostgresqlClient = null; + + private constructor() {} + + public static getInstance() { + return this.instance || (this.instance = new PostgresqlClient()); + } + + public connect = async (schemaType: DatabaseSchemaType): Promise => { + try { + const config = databaseConfig(schemaType); + this.connection = new Client({ + database : config.DatabaseName, + host : config.Host, + user : config.Username, + password : config.Password, + }); + await this.connection.connect(); + } catch (error) { + Logger.instance().log(error.message); + Logger.instance().log(`Error trace: ${error.stack}`); + } + }; + + public createDb = async (schemaType: DatabaseSchemaType): Promise => { + try { + const config = databaseConfig(schemaType); + const query = `CREATE DATABASE ${config.DatabaseName}`; + return await this.execute(schemaType, query); + } catch (error) { + Logger.instance().log(error.message); + } + }; + + public executeQuery = async (query: string): Promise => { + try { + const result = await this.connection.query(query); + return result; + } catch (error) { + Logger.instance().log(error.message); + Logger.instance().log(`Error trace: ${error.stack}`); + } + return null; + }; + + public dropDb = async (schemaType: DatabaseSchemaType): Promise => { + try { + const config = databaseConfig(schemaType); + const query = `DROP DATABASE IF EXISTS ${config.DatabaseName}`; + return await this.execute(schemaType, query); + } catch (error) { + Logger.instance().log(error.message); + } + }; + + public execute = async (schemaType: DatabaseSchemaType, query: string): Promise => { + try { + const config = databaseConfig(schemaType); + const client = new Client({ + user : config.Username, + host : config.Host, + password : config.Password, + port : 5432, + }); + await client.connect(); + await client.query(query); + await client.end(); + return true; + } catch (error) { + Logger.instance().log(error.message); + } + }; + +} diff --git a/src/common/database.utils/dialect.clients/sqlite.client.ts b/src/common/database.utils/dialect.clients/sqlite.client.ts new file mode 100644 index 0000000..03c2b63 --- /dev/null +++ b/src/common/database.utils/dialect.clients/sqlite.client.ts @@ -0,0 +1,57 @@ +/* +import sqlite3 from 'sqlite3'; +import * as fs from 'fs'; +import { Logger } from '../../logger'; +import { IDatabaseClient } from './database.client.interface'; +import { DatabaseSchemaType, databaseConfig } from '../database.config'; + +//////////////////////////////////////////////////////////////// + +export class SQLiteClient implements IDatabaseClient { + + public createDb = async (schemaType: DatabaseSchemaType): Promise => { + try { + const config = databaseConfig(schemaType); + const db = new sqlite3.Database(config.DatabaseName, (err) => { + if (err) { + // eslint-disable-next-line no-console + console.log('Error connecting to the database:', err.message); + } else { + // eslint-disable-next-line no-console + console.log('Connected to the SQLite database.'); + } + }); + return db != null; + } catch (error) { + Logger.instance().log(error.message); + } + }; + + public dropDb = async (schemaType: DatabaseSchemaType): Promise => { + try { + const config = databaseConfig(schemaType); + const databaseName = config.DatabaseName; + fs.unlinkSync(databaseName); + return true; + } catch (error) { + Logger.instance().log(error.message); + } + }; + + public executeQuery = async (schemaType: DatabaseSchemaType, query: string): Promise => { + try { + const config = databaseConfig(schemaType); + const db = new sqlite3.Database(config.DatabaseName); + db.run(query, (err) => { + Logger.instance().log(err.message); + return false; + }); + return true; + } catch (error) { + Logger.instance().log(error.message); + } + }; + +} + +*/ diff --git a/src/common/error.handler.ts b/src/common/handlers/error.handler.ts similarity index 97% rename from src/common/error.handler.ts rename to src/common/handlers/error.handler.ts index d7fb52c..e69c518 100644 --- a/src/common/error.handler.ts +++ b/src/common/handlers/error.handler.ts @@ -1,4 +1,4 @@ -import { ApiError } from "./api.error"; +import { ApiError } from "../api.error"; export class ErrorHandler { diff --git a/src/common/response.handler.ts b/src/common/handlers/response.handler.ts similarity index 93% rename from src/common/response.handler.ts rename to src/common/handlers/response.handler.ts index c10cd17..88340c7 100644 --- a/src/common/response.handler.ts +++ b/src/common/handlers/response.handler.ts @@ -1,7 +1,7 @@ import express from 'express'; -import { ApiError } from './api.error'; -import { ResponseDto } from '../domain.types/miscellaneous/response.dto'; -import { Logger } from './logger'; +import { ApiError } from '../api.error'; +import { ResponseDto } from '../../domain.types/miscellaneous/response.dto'; +import { Logger } from '../logger'; // import errsole from 'errsole'; export class ResponseHandler { diff --git a/src/config/configuration.manager.ts b/src/config/configuration.manager.ts new file mode 100644 index 0000000..12f1441 --- /dev/null +++ b/src/config/configuration.manager.ts @@ -0,0 +1,221 @@ +import path from 'path'; +import * as defaultConfiguration from '../../config.json'; +import * as localConfiguration from '../../config.local.json'; +import { + AuthenticationType, + AuthorizationType, + CareplanConfig, + Configurations, + DatabaseORM, + DatabaseType, + EHRProvider, + EHRSpecification, + EmailServiceProvider, + FeatureFlagsProvider, + Processor, + FileStorageProvider, + InAppNotificationServiceProvider, + SMSServiceProvider, + ProcessorsProvider +} from './configuration.types'; + +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +export class ConfigurationManager { + + static _config: Configurations = null; + + public static loadConfigurations = (): void => { + + const configuration = process.env.NODE_ENV === 'local' + || process.env.NODE_ENV === 'test' + ? localConfiguration : defaultConfiguration; + + ConfigurationManager._config = { + SystemIdentifier : configuration.SystemIdentifier, + BaseUrl : process.env.BASE_URL, + Auth : { + Authentication : configuration.Auth.Authentication as AuthenticationType, + Authorization : configuration.Auth.Authorization as AuthorizationType, + UseRefreshToken : configuration.Auth.UseRefreshToken, + AccessTokenExpiresInSeconds : configuration.Auth.AccessTokenExpiresInSeconds, + RefreshTokenExpiresInSeconds : configuration.Auth.RefreshTokenExpiresInSeconds + }, + Database : { + Type : configuration.Database.Type as DatabaseType, + ORM : configuration.Database.ORM as DatabaseORM, + }, + Ehr : { + Enabled : configuration.Ehr.Enabled, + Specification : configuration.Ehr.Specification as EHRSpecification, + Provider : configuration.Ehr.Provider as EHRProvider, + }, + FileStorage : { + Provider : configuration?.FileStorage?.Provider as FileStorageProvider ?? 'Custom', + }, + FeatureFlags : { + Provider : configuration?.FeatureFlags?.Provider as FeatureFlagsProvider ?? 'Custom', + }, + Communication : { + SMSProvider : configuration.Communication.SMS.Provider as SMSServiceProvider, + EmailProvider : configuration.Communication.Email.Provider as EmailServiceProvider, + // eslint-disable-next-line max-len + InAppNotificationProvider : configuration.Communication.InAppNotifications.Provider as InAppNotificationServiceProvider, + }, + Careplans : configuration.Careplans, + TemporaryFolders : { + Upload : configuration.TemporaryFolders.Upload as string, + Download : configuration.TemporaryFolders.Download as string, + CleanupFolderBeforeMinutes : configuration.TemporaryFolders.CleanupFolderBeforeMinutes as number, + }, + FormServiceProviders : configuration.FormServiceProviders, + WebhookControllerProviders : configuration.WebhookControllerProviders, + Processor : { + Provider : configuration.Processor?.Provider as ProcessorsProvider, + }, + Logger : configuration.Logger, + MaxUploadFileSize : configuration.MaxUploadFileSize, + EHRAnalytics : configuration.EHRAnalytics, + Gamification : configuration.Gamification, + }; + + ConfigurationManager.checkConfigSanity(); + }; + + public static BaseUrl = (): string => { + return ConfigurationManager._config.BaseUrl; + }; + + public static SystemIdentifier = (): string => { + return ConfigurationManager._config.SystemIdentifier; + }; + + public static Authentication = (): AuthenticationType => { + return ConfigurationManager._config.Auth.Authentication; + }; + + public static Authorization = (): AuthorizationType => { + return ConfigurationManager._config.Auth.Authorization; + }; + + public static UseRefreshToken = (): boolean => { + return ConfigurationManager._config.Auth.UseRefreshToken; + }; + + public static AccessTokenExpiresInSeconds = (): number => { + return ConfigurationManager._config.Auth.AccessTokenExpiresInSeconds; + }; + + public static RefreshTokenExpiresInSeconds = (): number => { + return ConfigurationManager._config.Auth.RefreshTokenExpiresInSeconds; + }; + + public static DatabaseType = (): DatabaseType => { + return ConfigurationManager._config.Database.Type; + }; + + public static DatabaseORM = (): DatabaseORM => { + return ConfigurationManager._config.Database.ORM; + }; + + public static EhrEnabled = (): boolean => { + return ConfigurationManager._config.Ehr.Enabled; + }; + + public static EhrSpecification = (): EHRSpecification => { + return ConfigurationManager._config.Ehr.Specification; + }; + + public static EhrProvider = (): EHRProvider => { + return ConfigurationManager._config.Ehr.Provider; + }; + + public static MaxUploadFileSize = (): number => { + return ConfigurationManager._config.MaxUploadFileSize; + }; + + public static get Processor(): Processor { + return ConfigurationManager._config?.Processor; + } + + public static get Logger(): string { + return ConfigurationManager._config?.Logger; + } + + public static FileStorageProvider = (): FileStorageProvider => { + return ConfigurationManager._config.FileStorage.Provider; + }; + + public static FeatureFlagsProvider = (): FeatureFlagsProvider => { + return ConfigurationManager._config.FeatureFlags.Provider; + }; + + public static SMSServiceProvider = (): SMSServiceProvider => { + return ConfigurationManager._config.Communication.SMSProvider; + }; + + public static EmailServiceProvider = (): EmailServiceProvider => { + return ConfigurationManager._config.Communication.EmailProvider; + }; + + public static UploadTemporaryFolder = (): string => { + var location = ConfigurationManager._config.TemporaryFolders.Upload; + return path.join(process.cwd(), location); + }; + + public static DownloadTemporaryFolder = (): string => { + var location = ConfigurationManager._config.TemporaryFolders.Download; + return path.join(process.cwd(), location); + }; + + public static TemporaryFolderCleanupBefore = (): number => { + return ConfigurationManager._config.TemporaryFolders.CleanupFolderBeforeMinutes; + }; + + public static InAppNotificationServiceProvider = (): InAppNotificationServiceProvider => { + return ConfigurationManager._config.Communication.InAppNotificationProvider; + }; + + public static careplans = () + : { Enabled: boolean, Provider: string; Service: string; Plans: CareplanConfig[] } [] => { + return ConfigurationManager._config.Careplans; + }; + + public static formServiceProviders = (): { Provider: string; Code: string; } [] => { + return ConfigurationManager._config.FormServiceProviders; + }; + + public static webhookControllerProviders = (): { Provider: string; Code: string; } [] => { + return ConfigurationManager._config.WebhookControllerProviders; + }; + + public static EHRAnalyticsEnabled = (): boolean => { + return ConfigurationManager._config?.EHRAnalytics; + }; + + public static GamificationEnabled = (): boolean => { + return ConfigurationManager._config?.Gamification; + }; + + private static checkConfigSanity() { + + //Check database configurations + + if (ConfigurationManager._config.Database.Type === 'SQL') { + var orm = ConfigurationManager._config.Database.ORM; + if (orm !== 'Sequelize' && orm !== 'TypeORM') { + throw new Error('Database configuration error! - Unspported/non-matching ORM'); + } + } + if (ConfigurationManager._config.Database.Type === 'NoSQL') { + var orm = ConfigurationManager._config.Database.ORM; + const dialect = process.env.DB_DIALECT; + if (dialect === 'MongoDB') { + if (orm !== 'Mongoose') { + throw new Error('Database configuration error! - Unspported/non-matching ORM'); + } + } + } + } + +} diff --git a/src/config/configuration.types.ts b/src/config/configuration.types.ts new file mode 100644 index 0000000..5b8896f --- /dev/null +++ b/src/config/configuration.types.ts @@ -0,0 +1,104 @@ + +export type DatabaseType = 'SQL' | 'NoSQL'; +export type DatabaseORM = 'Sequelize' | 'TypeORM' | 'Mongoose'; +export type EHRSpecification = 'FHIR'| 'OpenEHR' | 'Mock'; +export type FHIRProvider = 'GCP-FHIR' | 'Azure-FHIR' | 'AWS-HealthLake' | 'Hapi-FHIR'; +export type OpenEHRProvider = 'OpenEHRBase'; +export type FileStorageProvider = 'AWS-S3' | 'GCP-FileStore' | 'Custom'; +export type FeatureFlagsProvider = 'Firebase-Remote-Config' | 'Custom'; +export type SMSServiceProvider = 'Twilio' | 'Mock'; +export type EmailServiceProvider = 'SendGrid' | 'Mock'; +export type ProcessorsProvider = 'Custom' | 'Mock'; +export type InAppNotificationServiceProvider = 'Firebase' | 'Mock'; +export type EHRProvider = FHIRProvider | OpenEHRProvider; +export type AuthorizationType = 'Custom'; //TBD: Other options need to be supported +export type AuthenticationType = 'Custom'; //TBD: Other options need to be supported + +/////////////////////////////////////////////////////////////////////////////////////////// + +export interface AuthConfig { + Authentication: AuthenticationType; + Authorization : AuthorizationType; + UseRefreshToken: boolean; + AccessTokenExpiresInSeconds: number; + RefreshTokenExpiresInSeconds: number; +} + +export interface DatabaseConfig { + Type : DatabaseType; + ORM : DatabaseORM; +} + +export interface Processor { + Provider : ProcessorsProvider; +} + +export interface EHRConfig { + Enabled : boolean; + Specification: EHRSpecification; + Provider : EHRProvider; +} + +export interface FileStorageConfig { + Provider: FileStorageProvider; +} + +export interface FeatureFlagsConfig { + Provider: FeatureFlagsProvider; +} + +export interface CommunicationConfig { + SMSProvider : SMSServiceProvider, + EmailProvider : EmailServiceProvider, + InAppNotificationProvider: InAppNotificationServiceProvider +} + +export interface TemporaryFoldersConfig { + Upload : string, + Download : string, + CleanupFolderBeforeMinutes: number +} + +export interface CareplanConfig { + Provider : string; + Name : string; + Code : string; + DisplayName : string; + DefaultDurationDays?: number; + Description : string; +} + +export interface FormServiceProvider { + Provider: string; + Code : string; +} + +export interface WebhookControllerProvider { + Provider: string; + Code : string; +} + +export interface Configurations { + SystemIdentifier : string; + BaseUrl : string; + Auth : AuthConfig; + Database : DatabaseConfig; + Ehr : EHRConfig; + FileStorage : FileStorageConfig; + Processor : Processor; + Logger : string; //'Custom' | 'Winston' | 'Pino' | 'Bunyan', + FeatureFlags : FeatureFlagsConfig; + Communication : CommunicationConfig; + TemporaryFolders : TemporaryFoldersConfig; + Careplans : { + Enabled : boolean; + Provider: string; + Service : string; + Plans : CareplanConfig[] + } []; + MaxUploadFileSize : number; + FormServiceProviders: FormServiceProvider[]; + WebhookControllerProviders: WebhookControllerProvider[]; + Gamification : boolean; + EHRAnalytics : boolean; +} diff --git a/src/database/database.connector.interface.ts b/src/database/database.connector.interface.ts new file mode 100644 index 0000000..4f9ae8d --- /dev/null +++ b/src/database/database.connector.interface.ts @@ -0,0 +1,9 @@ + +export interface IPrimaryDatabaseConnector { + + connect(): Promise; + + sync(): Promise; + + migrate(): Promise; +} diff --git a/src/database/database.connector.ts b/src/database/database.connector.ts new file mode 100644 index 0000000..08ae3cf --- /dev/null +++ b/src/database/database.connector.ts @@ -0,0 +1,44 @@ +import 'reflect-metadata'; +import { IPrimaryDatabaseConnector } from './database.connector.interface'; +import { injectable, inject } from 'tsyringe'; +import { Logger } from '../common/logger'; + +//////////////////////////////////////////////////////////////////////// + +@injectable() +export class PrimaryDatabaseConnector { + + constructor(@inject('IPrimaryDatabaseConnector') private _db: IPrimaryDatabaseConnector) {} + + public init = async (): Promise => { + try { + return await this._db.connect(); + } catch (error) { + Logger.instance().log('Create database error: ' + error.message); + return false; + } + }; + + public sync = async (): Promise => { + try { + await this._db.sync(); + return true; + } catch (error) { + Logger.instance().log('Sync database error: ' + error.message); + return false; + } + }; + + public migrate = async (): Promise => { + try { + await this._db.migrate(); + return true; + } catch (error) { + Logger.instance().log('Migrate database error: ' + error.message); + return false; + } + }; + +} + +//////////////////////////////////////////////////////////////////////// diff --git a/src/database/database.injector.ts b/src/database/database.injector.ts new file mode 100644 index 0000000..d9defee --- /dev/null +++ b/src/database/database.injector.ts @@ -0,0 +1,22 @@ +import 'reflect-metadata'; +import { ConfigurationManager } from '../config/configuration.manager'; +import { DependencyContainer } from 'tsyringe'; +import { SQLInjector } from './sql/sql.injector'; + +//////////////////////////////////////////////////////////////////////////////// + +export class DatabaseInjector { + + static registerInjections(container: DependencyContainer) { + + const databaseType = ConfigurationManager.DatabaseType(); + if (databaseType === 'SQL') { + SQLInjector.registerInjections(container); + } + // else if (databaseType === 'NoSQL') { + // NoSQLInjector.registerInjections(container); + // } + + } + +} diff --git a/src/database/repository.interfaces/form.section/form.section.repo.interface.ts b/src/database/repository.interfaces/form.section/form.section.repo.interface.ts new file mode 100644 index 0000000..b8fc428 --- /dev/null +++ b/src/database/repository.interfaces/form.section/form.section.repo.interface.ts @@ -0,0 +1,17 @@ +import { FormSectionCreateModel, FormSectionResponseDto, FormSectionSearchFilters, FormSectionUpdateModel } from "../../../domain.types/forms/form.section.domain.types"; + +export interface IFormSectionRepo{ + + create(model: FormSectionCreateModel): Promise; + + update(id: string, model: FormSectionUpdateModel): Promise; + + getById(id: string): Promise; + + delete(id: string): Promise; + + getByTemplateId(id: string): Promise; + + search(filters: FormSectionSearchFilters) : Promise; + +} \ No newline at end of file diff --git a/src/database/repository.interfaces/form.submission/form.submission.repo.interface.ts b/src/database/repository.interfaces/form.submission/form.submission.repo.interface.ts new file mode 100644 index 0000000..a76a732 --- /dev/null +++ b/src/database/repository.interfaces/form.submission/form.submission.repo.interface.ts @@ -0,0 +1,22 @@ +import { FormSubmissionCreateModel, FormSubmissionDto, FormSubmissionSearchFilters, FormSubmissionUpdateModel } from "../../../domain.types/forms/form.submission.domain.types"; +import { uuid } from "../../../domain.types/miscellaneous/system.types"; + +export interface IFormSubmissionRepo{ + + create(model: FormSubmissionCreateModel) : Promise; + + update(id: string, model: FormSubmissionUpdateModel) : Promise; + + getById(id: string) : Promise; + + delete(id: string) : Promise; + + submit(id: uuid) : Promise; + + search(filters: FormSubmissionSearchFilters) : Promise; + + + + + +} \ No newline at end of file diff --git a/src/database/repository.interfaces/form.template/form.template.repo.interface.ts b/src/database/repository.interfaces/form.template/form.template.repo.interface.ts new file mode 100644 index 0000000..1c0c253 --- /dev/null +++ b/src/database/repository.interfaces/form.template/form.template.repo.interface.ts @@ -0,0 +1,25 @@ +import { ExportFormTemplateDto, FormTemplateCreateModel, FormTemplateResponseDto, FormTemplateSearchFilters, FormTemplateUpdateModel } from "../../../domain.types/forms/form.template.domain.types"; + + +export interface IFormTemplateRepo{ + + + create(model: FormTemplateCreateModel) : Promise; + + update(id: string, model: FormTemplateUpdateModel) : Promise; + + getById(id: string) : Promise; + + getDetailsById(id: string) : Promise; + + readTemplateObjToExport(id: string): Promise; + + previewTemplate(id: string): Promise; + + delete(id: string): Promise; + + submissions(id: string): Promise; + + search(filters: FormTemplateSearchFilters) : Promise + +} \ No newline at end of file diff --git a/src/database/repository.interfaces/question.response/question.response.repo.interface.ts b/src/database/repository.interfaces/question.response/question.response.repo.interface.ts new file mode 100644 index 0000000..a6f930c --- /dev/null +++ b/src/database/repository.interfaces/question.response/question.response.repo.interface.ts @@ -0,0 +1,28 @@ +import { QuestionResponseDto } from "../../../domain.types/forms/question.domain.types"; +import { QuestionResponseCreateModel, QuestionResponseResponseDto, QuestionResponseSearchFilters, QuestionResponseUpdateModel } from "../../../domain.types/forms/response.domain.types"; + +export interface IResponseRepo{ + + + create(model: QuestionResponseCreateModel) : Promise; + + save(model: any) : Promise; + + update(id: string, model: QuestionResponseUpdateModel) : Promise; + + getById(id: string) : Promise; + + getQuestionById(id: string) : Promise; + + delete(id: string) : Promise; + + getAll() : Promise; + + exportCsv(): Promise; + + exportPdf(): Promise; + + search(filters: QuestionResponseSearchFilters) : Promise; + + +} \ No newline at end of file diff --git a/src/database/repository.interfaces/question/question.repo.interface.ts b/src/database/repository.interfaces/question/question.repo.interface.ts new file mode 100644 index 0000000..d7979c4 --- /dev/null +++ b/src/database/repository.interfaces/question/question.repo.interface.ts @@ -0,0 +1,18 @@ +import { QuestionCreateModel, QuestionResponseDto, QuestionSearchFilters, QuestionUpdateModel } from "../../../domain.types/forms/question.domain.types"; + + + +export interface IQuestionRepo{ + + create(model: QuestionCreateModel) : Promise; + + update(id: string, model: QuestionUpdateModel) : Promise; + + getById(id: string) : Promise; + + getByTemplateId(id: string) : Promise; + + delete(id: string) : Promise; + + search(filters: QuestionSearchFilters) : Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/user/user.repo.interface.ts b/src/database/repository.interfaces/user/user.repo.interface.ts new file mode 100644 index 0000000..88adb45 --- /dev/null +++ b/src/database/repository.interfaces/user/user.repo.interface.ts @@ -0,0 +1,17 @@ +import { UserCreateModel, UserResponseDto, UserSearchFilters, UserUpdateModel } from "../../../domain.types/forms/user.domain.types"; + +export interface IUserRepo{ + + allUsers(): Promise ; + + create(model: UserCreateModel) : Promise; + + update(id: string, model: UserUpdateModel) : Promise; + + getById(id: string) : Promise; + + delete(id: string) : Promise; + + search(filters: UserSearchFilters): Promise; + +} \ No newline at end of file diff --git a/src/database/sql/sql.injector.ts b/src/database/sql/sql.injector.ts new file mode 100644 index 0000000..2cf3783 --- /dev/null +++ b/src/database/sql/sql.injector.ts @@ -0,0 +1,20 @@ +import 'reflect-metadata'; +import { DependencyContainer } from 'tsyringe'; +import { ConfigurationManager } from '../../config/configuration.manager'; +import { TypeOrmInjector } from './typeorm/typeorm.injector'; + +//////////////////////////////////////////////////////////////////////////////// + +export class SQLInjector +{ + + static registerInjections(container: DependencyContainer) { + + const databaseORM = ConfigurationManager.DatabaseORM(); + if (databaseORM === 'Sequelize') { + TypeOrmInjector.registerInjections(container); + } + + } + +} diff --git a/src/database/sql/typeorm/database.connector.typeorm.ts b/src/database/sql/typeorm/database.connector.typeorm.ts new file mode 100644 index 0000000..e13fcba --- /dev/null +++ b/src/database/sql/typeorm/database.connector.typeorm.ts @@ -0,0 +1,272 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { execSync } from 'child_process'; +// import { Dialect } from 'sequelize'; +// import { Sequelize } from 'sequelize-typescript'; +// import { Logger } from '../../../common/logger'; +import { IPrimaryDatabaseConnector } from '../../database.connector.interface'; +import { DatabaseSchemaType, databaseConfig } from '../../../common/database.utils/database.config'; +import { DataSource } from 'typeorm'; +import { FavoriteTemplate } from './models/favorite.template/favorite.template.model'; +import { FormSection } from './models/form.section/form.section.model'; +import { FormSubmission } from './models/form.submission/form.submission.model'; +import { FormTemplate } from './models/form.template/form.template.model'; +import { FormTemplateApproval } from './models/form.template.approval/form.template.approval.model'; +import { InputUnitList } from './models/input.unit.list/input.unit.list.model'; +import { Question } from './models/question/question.model'; +import { QuestionResponse } from './models/question.response/question.response.model'; +import { TemplateFolder } from './models/template.folder/template.folder.model'; +import { User } from './models/user/user.model'; +import { Logger } from '../../../startup/logger'; + +const Config = databaseConfig(DatabaseSchemaType.Primary); + +////////////////////////////////////////////////////////////// + +export class DatabaseConnector_TypeOrm implements IPrimaryDatabaseConnector { + // private _sequelize: Sequelize = null; + + // public static db: Sequelize = null; + + public _source = new DataSource({ + name: Config.Dialect, + type: Config.Dialect as any, + host: Config.Host, + port: Config.Port, + username: Config.Username, + password: Config.Password, + database: Config.DatabaseName, + synchronize: true, + entities: [ + FavoriteTemplate, + FormSection, + FormSubmission, + FormTemplate, + FormTemplateApproval, + InputUnitList, + Question, + QuestionResponse, + TemplateFolder, + User, + ], + migrations: [], + subscribers: [], + // logger: new DBLogger(), + logger: Logger.instance(), + logging: true, + poolSize: Config.Pool.Max, + cache: true, + }); + + // public connect = async (): Promise => { + // try { + + // const config = databaseConfig(DatabaseSchemaType.Primary); + // const modelsFolder = path.join(__dirname, '/models'); + // const modelsPath = getFoldersRecursively(modelsFolder); + // const options = { + // host : config.Host, + // dialect : config.Dialect, + // models : modelsPath, + // pool : { + // max : config.Pool.Max, + // min : config.Pool.Min, + // acquire : config.Pool.Acquire, + // idle : config.Pool.Idle, + // }, + // logging : false, //TODO: Please provide a function here to handle logging... + // }; + + // const sequelize = new Sequelize(config.DatabaseName, config.Username, config.Password, options); + // this._sequelize = sequelize; + + // Logger.instance().log(`Connecting to database '${config.DatabaseName}' ...`); + + // const databaseClient = Injector.Container.resolve(DatabaseClient); + // await databaseClient.createDb(DatabaseSchemaType.Primary); + + // await this._sequelize.authenticate(); + // await this._sequelize.sync({ force: false, alter: true }); + + // Logger.instance().log(`Connected to database '${config.DatabaseName}'.`); + + // DatabaseConnector_Sequelize.db = this._sequelize; + + // return true; + + // } catch (error) { + // Logger.instance().log(error.message); + // return false; + // } + // }; + + public connect = async (): Promise => { + try { + const config = databaseConfig(DatabaseSchemaType.Primary); + const entitiesFolder = path.join(__dirname, "/models"); + const entitiesPath = getFoldersRecursively(entitiesFolder); + + Logger.instance().log( + `Connecting to database '${config.DatabaseName}' ...` + ); + + const options = { + type: config.Dialect as any, + host: config.Host, + port: config.Port || 3306, + username: config.Username, + password: config.Password, + database: config.DatabaseName, + entity: entitiesPath, + entities: [ + path.join(entitiesFolder, '**/*.model{.ts,.js}') // Common TypeORM entity pattern + ], + synchronize: true, + logging: false, + poolSize: config.Pool?.Max || 10, + extra: { + connectionLimit: config.Pool?.Max || 10, + idleTimeout: config.Pool?.Idle || 10000, + acquireTimeout: config.Pool?.Acquire || 30000, + }, + }; + + this._source = new DataSource(options); + await this._source.initialize(); + + Logger.instance().log(`Connected to database '${config.DatabaseName}'.`); + + return true; + } catch (error) { + Logger.instance().log( + error instanceof Error + ? error.message + : "Unknown database connection error" + ); + return false; + } + }; + + // public db = (): Sequelize => { + // return this._sequelize; + // }; + + // public sync = async () => { + // try { + // await this._sequelize.sync({ alter: true }); + // return true; + // } catch (error) { + // Logger.instance().log(error.message); + // } + // return false; + // }; + + public sync = async () => { + try { + if (!this._source.isInitialized) { + await this._source.initialize(); + } + + await this._source.synchronize(); + + return true; + } catch (error) { + Logger.instance().log( + error instanceof Error ? error.message : "Unknown synchronization error" + ); + return false; + } + }; + + // public migrate = async () => { + // try { + // const output = execSync('npx sequelize-cli db:migrate'); + + // const str = output.toString(); + // Logger.instance().log('Database migrated successfully!'); + // Logger.instance().log(str); + + // return true; + // } catch (error) { + // Logger.instance().log(error.message); + // } + // return false; + // }; + public migrate = async (): Promise => { + try { + if (!this._source.isInitialized) { + await this._source.initialize(); + } + + // Run pending migrations + const migrations = await this._source.runMigrations(); + + if (migrations.length > 0) { + Logger.instance().log("Database migrated successfully!"); + migrations.forEach((migration) => { + Logger.instance().log(`Executed migration: ${migration.name}`); + }); + } else { + Logger.instance().log("No pending migrations found."); + } + + return true; + } catch (error) { + Logger.instance().log( + error instanceof Error ? error.message : "Migration error" + ); + return false; + } + }; + +// public executeQuery = async (query: string): Promise => { +// try { +// const result = await this._sequelize.query(query); +// return result; +// } catch (error) { +// Logger.instance().log(error.message); +// Logger.instance().log(`Error trace: ${error.stack}`); +// } +// return null; +// }; + + public executeQuery = async (query: string): Promise => { + try { + if (!this._source.isInitialized) { + await this._source.initialize(); + } + + // For SELECT queries that return data + const result = await this._source.query(query); + return result; + + } catch (error) { + Logger.instance().log(error instanceof Error ? error.message : 'Query execution error'); + Logger.instance().log(`Error trace: ${error instanceof Error ? error.stack : 'No stack trace'}`); + throw error; // Consider throwing instead of returning null + } +}; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +function getFoldersRecursively(location: string) { + const items = fs.readdirSync(location, { withFileTypes: true }); + let paths = []; + for (const item of items) { + if (item.isDirectory()) { + const fullPath = path.join(location, item.name); + const childrenPaths = getFoldersRecursively(fullPath); + paths = [ + ...paths, + fullPath, + ...childrenPaths, + ]; + } + } + return paths; +} + +const dbTypeOrm= new DatabaseConnector_TypeOrm(); + +export const Source = dbTypeOrm._source; diff --git a/src/mappers/form.section.mapper.ts b/src/database/sql/typeorm/mappers/form.section.mapper.ts similarity index 93% rename from src/mappers/form.section.mapper.ts rename to src/database/sql/typeorm/mappers/form.section.mapper.ts index 511bd42..459c919 100644 --- a/src/mappers/form.section.mapper.ts +++ b/src/database/sql/typeorm/mappers/form.section.mapper.ts @@ -1,4 +1,4 @@ -import { FormSectionResponseDto } from "../domain.types/forms/form.section.domain.types"; +import { FormSectionResponseDto } from "../../../../domain.types/forms/form.section.domain.types"; export class FormSectionMapper { static toDto = (record: any): FormSectionResponseDto => { diff --git a/src/mappers/form.submission.mapper.ts b/src/database/sql/typeorm/mappers/form.submission.mapper.ts similarity index 96% rename from src/mappers/form.submission.mapper.ts rename to src/database/sql/typeorm/mappers/form.submission.mapper.ts index 52cf987..0fd4ee9 100644 --- a/src/mappers/form.submission.mapper.ts +++ b/src/database/sql/typeorm/mappers/form.submission.mapper.ts @@ -1,4 +1,4 @@ -import { FormSubmissionDto } from "../domain.types/forms/form.submission.domain.types"; +import { FormSubmissionDto } from "../../../../domain.types/forms/form.submission.domain.types"; export class FormMapper { static toDto = (record: any): FormSubmissionDto => { diff --git a/src/mappers/form.template.mapper.ts b/src/database/sql/typeorm/mappers/form.template.mapper.ts similarity index 82% rename from src/mappers/form.template.mapper.ts rename to src/database/sql/typeorm/mappers/form.template.mapper.ts index d823ee4..8b7f197 100644 --- a/src/mappers/form.template.mapper.ts +++ b/src/database/sql/typeorm/mappers/form.template.mapper.ts @@ -1,4 +1,4 @@ -import { FormTemplateResponseDto } from "../domain.types/forms/form.template.domain.types"; +import { FormTemplateResponseDto } from "../../../../domain.types/forms/form.template.domain.types"; export class FormTemplateMapper { static toDto = (record: any): FormTemplateResponseDto => { @@ -11,9 +11,9 @@ export class FormTemplateMapper { Title: record.Title, Description: record.Description, CurrentVersion: record.CurrentVersion, - TenantId: record.TenantId, + TenantCode: record.TenantCode, Type: record.Type, - ItemsPerPage: record.ItemsPerPage, + // ItemsPerPage: record.ItemsPerPage, DisplayCode: record.DisplayCode, OwnerUserId: record.OwnerUserId, RootSectionId: record.RootSectionId, diff --git a/src/mappers/question.mapper.ts b/src/database/sql/typeorm/mappers/question.mapper.ts similarity index 98% rename from src/mappers/question.mapper.ts rename to src/database/sql/typeorm/mappers/question.mapper.ts index 01d450b..3aa7243 100644 --- a/src/mappers/question.mapper.ts +++ b/src/database/sql/typeorm/mappers/question.mapper.ts @@ -1,4 +1,4 @@ -import { QuestionOption, QuestionResponseDto } from "../domain.types/forms/question.domain.types"; +import { QuestionOption, QuestionResponseDto } from "../../../../domain.types/forms/question.domain.types"; // export class QuestionMapper { // static toDto = (record: any): QuestionResponseDto => { diff --git a/src/mappers/question.response.mapper.ts b/src/database/sql/typeorm/mappers/question.response.mapper.ts similarity index 95% rename from src/mappers/question.response.mapper.ts rename to src/database/sql/typeorm/mappers/question.response.mapper.ts index 174f70d..3f8656c 100644 --- a/src/mappers/question.response.mapper.ts +++ b/src/database/sql/typeorm/mappers/question.response.mapper.ts @@ -1,4 +1,4 @@ -import { QuestionResponseResponseDto } from "../domain.types/forms/response.domain.types"; +import { QuestionResponseResponseDto } from "../../../../domain.types/forms/response.domain.types"; export class ResponseMapper { static toDto = (record: any): QuestionResponseResponseDto => { diff --git a/src/mappers/user.login.mapper.ts b/src/database/sql/typeorm/mappers/user.login.mapper.ts similarity index 94% rename from src/mappers/user.login.mapper.ts rename to src/database/sql/typeorm/mappers/user.login.mapper.ts index 52cef2b..29383e7 100644 --- a/src/mappers/user.login.mapper.ts +++ b/src/database/sql/typeorm/mappers/user.login.mapper.ts @@ -1,6 +1,6 @@ // import { UserLoginSessionResponseDto } from "../domain.types/forms/form.section.domain.types"; -import { UserLoginSessionResponseDto } from "../domain.types/forms/user.login.session.domain.types"; +import { UserLoginSessionResponseDto } from "../../../../domain.types/forms/user.login.session.domain.types"; export class UserLoginSessionMapper { static toDto = (record: any): UserLoginSessionResponseDto => { diff --git a/src/mappers/user.mapper.ts b/src/database/sql/typeorm/mappers/user.mapper.ts similarity index 93% rename from src/mappers/user.mapper.ts rename to src/database/sql/typeorm/mappers/user.mapper.ts index 9b6a087..fc2a4e1 100644 --- a/src/mappers/user.mapper.ts +++ b/src/database/sql/typeorm/mappers/user.mapper.ts @@ -1,4 +1,4 @@ -import { UserResponseDto } from "../domain.types/forms/user.domain.types"; +import { UserResponseDto } from "../../../../domain.types/forms/user.domain.types"; export class UserMapper { static toDto = (record: any): UserResponseDto => { diff --git a/src/database/sql/typeorm/models/base.entity.ts b/src/database/sql/typeorm/models/base.entity.ts new file mode 100644 index 0000000..c66dd17 --- /dev/null +++ b/src/database/sql/typeorm/models/base.entity.ts @@ -0,0 +1,15 @@ +import { PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm'; + +export abstract class BaseEntity { + @PrimaryGeneratedColumn('uuid') + id: string; + + @CreateDateColumn() + CreatedAt: Date; + + @UpdateDateColumn() + UpdatedAt?: Date; + + @DeleteDateColumn() + DeletedAt?: Date; +} diff --git a/src/database/sql/typeorm/models/favorite.template/favorite.template.model.ts b/src/database/sql/typeorm/models/favorite.template/favorite.template.model.ts new file mode 100644 index 0000000..884fe15 --- /dev/null +++ b/src/database/sql/typeorm/models/favorite.template/favorite.template.model.ts @@ -0,0 +1,13 @@ +import { Column, Entity } from "typeorm"; +import { BaseEntity } from "../base.entity"; + +@Entity({ name: 'favorite_templates' }) +export class FavoriteTemplate extends BaseEntity { + + @Column({ type: 'uuid', nullable: false }) + UserId: string; + + @Column({ type: 'uuid', nullable: false }) + TemplateId: string + +} diff --git a/src/database/sql/typeorm/models/form.section/form.section.model.ts b/src/database/sql/typeorm/models/form.section/form.section.model.ts new file mode 100644 index 0000000..8103224 --- /dev/null +++ b/src/database/sql/typeorm/models/form.section/form.section.model.ts @@ -0,0 +1,54 @@ +import { Entity, Column, ManyToOne, OneToMany, JoinColumn } from 'typeorm'; +import { BaseEntity } from '../base.entity'; +import { FormTemplate } from '../form.template/form.template.model'; +import { Question } from '../question/question.model'; + +enum SectionNodeType { + RootSection='RootSection', + ChildSection='ChildSection' +} + +@Entity('form_sections') +export class FormSection extends BaseEntity { + @Column({type: 'uuid', nullable: false }) + FormTemplateId: string; + + @Column({type: 'uuid', nullable: true }) + ParentSectionId?: string; + + @Column({type: 'varchar', length: 128, nullable: false }) + Title: string; + + @Column({type: 'varchar', length: 128, nullable: false }) + DisplayCode?: string; + + @Column({type: 'varchar',length: 512, nullable: true }) + Description?: string; + + //Represent sequence within parent section + @Column({type: 'number', default: 0, nullable: false }) + Sequence?: number; + + // @Column({ type : 'enum', enum: SectionNodeType, nullable: false }) + // SectionNodeType: SectionNodeType; + + @Column({type: 'uuid', nullable: true }) + ImageResourceId?: string; + + // @Column({type: 'boolean', default: true, nullable: false }) + // DefaultSectionNumbering?: boolean; + + @ManyToOne(() => FormTemplate, (template) => template.FormSections) + @JoinColumn({ name: 'FormTemplateId' }) + FormTemplate: FormTemplate; + + @ManyToOne(() => FormSection, (section) => section.ChildSections, { nullable: true }) + @JoinColumn({ name: 'ParentSectionId' }) + ParentSection?: FormSection; + + @OneToMany(() => FormSection, (section) => section.ParentSection) + ChildSections: FormSection[]; + + @OneToMany(() => Question, (question) => question.ParentFormSection) + Questions: Question[]; +} diff --git a/src/database/sql/typeorm/models/form.submission/form.submission.model.ts b/src/database/sql/typeorm/models/form.submission/form.submission.model.ts new file mode 100644 index 0000000..dcd9474 --- /dev/null +++ b/src/database/sql/typeorm/models/form.submission/form.submission.model.ts @@ -0,0 +1,65 @@ +import { Entity, Column, ManyToOne, OneToMany, JoinColumn } from 'typeorm'; +import { BaseEntity } from '../base.entity'; +import { FormTemplate } from '../form.template/form.template.model'; +import { QuestionResponse } from '../question.response/question.response.model'; + + +export enum FormStatus { + LinkShared = 'LinkShared', + Saved = 'Saved', + InProgress='InProgress', + LinkExpired = 'LinkExpired', + Submitted = 'Submitted' +} + +@Entity('form_submissions') +export class FormSubmission extends BaseEntity { + @Column({type: 'uuid', nullable: false }) + FormTemplateId: string; + + @Column({type: 'varchar', length: 128, nullable: false }) + Title: string; + + @Column({ type: 'varchar', length: 128, default: 'General', nullable: false }) + Type: string; + + @Column({type: 'uuid', nullable: true }) + TenantId?: string; + + @Column({type: 'uuid', nullable: true }) + UserId?: string; + + @Column({type: 'json', length: 2048, nullable: true }) + UserMetaData?: string; + + @Column({type: 'varchar', length: 1024, nullable: true }) + Encrypted?: string; + + @Column({type: 'varchar', length: 1024, nullable: true }) + Unencrypted?: string; + + @Column({type: 'varchar', length: 1024, nullable: true }) + Link?: string; + + @Column({ type: 'varchar', nullable: true }) + LinkQueryParams?: string; + + @Column({ type: 'enum', enum: FormStatus, default: FormStatus.LinkShared, nullable: false }) + Status: FormStatus; + + @Column({type: 'timestamp', nullable: false }) + ValidTill: Date; + + @Column({type: 'timestamp', nullable: true }) + SubmittedAt?: Date; + + @Column({type: 'number', nullable: true }) + Score?: number; + + @ManyToOne(() => FormTemplate, (template) => template.FormSubmissions) + @JoinColumn({ name: 'FormTemplateId' }) + FormTemplate: FormTemplate; + + @OneToMany(() => QuestionResponse, (response) => response.FormSubmission) + QuestionResponses: QuestionResponse[]; +} diff --git a/src/database/sql/typeorm/models/form.template.approval/form.template.approval.model.ts b/src/database/sql/typeorm/models/form.template.approval/form.template.approval.model.ts new file mode 100644 index 0000000..4290dab --- /dev/null +++ b/src/database/sql/typeorm/models/form.template.approval/form.template.approval.model.ts @@ -0,0 +1,18 @@ +import { Column, Entity } from "typeorm"; +import { BaseEntity } from "../base.entity"; + +@Entity({ name: 'form_template_approvals' }) +export class FormTemplateApproval extends BaseEntity { + + @Column({ type: 'uuid', nullable: false }) + ApproverUserId: string; + + @Column({ type: 'uuid', nullable: false, unique: true }) + TemplateId: string + + @Column({ type: 'boolean', nullable: false, default: false }) + Approved: boolean + + @Column({ type: 'varchar', length: 512, nullable: true }) + ReviewComments?: string +} diff --git a/src/database/sql/typeorm/models/form.template/form.template.model.ts b/src/database/sql/typeorm/models/form.template/form.template.model.ts new file mode 100644 index 0000000..3f39437 --- /dev/null +++ b/src/database/sql/typeorm/models/form.template/form.template.model.ts @@ -0,0 +1,70 @@ +import { Entity, Column, OneToMany, ManyToOne, JoinColumn } from 'typeorm'; +import { BaseEntity } from '../base.entity'; +import { FormSubmission } from '../form.submission/form.submission.model'; +import { FormSection } from '../form.section/form.section.model'; +import { Question } from '../question/question.model'; + +export enum FormType { + Survey = 'Survey', + Questionnaire = 'Questionnaire', + TestPaper = 'TestPaper', + DataCollection = 'DataCollection' +} + +export enum NavigationStrategy { + OneQuestion = 'OneQuestion', + OneSection = 'OneSection', + AllAtOnce = 'AllAtOnce', + Paginated = 'Paginated' +} + +@Entity('form_templates') +export class FormTemplate extends BaseEntity { + @Column({type: 'uuid', nullable: true }) + TenantId?: string; + + @Column({ type: 'varchar', length: 128, nullable: false }) + DisplayCode?: string; + + @Column({type: 'varchar', length: 128, default: '1.0', nullable: false }) + Version?: number; + + @Column({ type: 'varchar', length: 128, default: 'Survey', nullable: false }) + Type: string; + + @Column({type: 'varchar', length: 128, nullable: false }) + Title: string; + + @Column({ type: 'varchar', length: 256, nullable: true }) + Description?: string; + + @Column({ type: 'enum', enum: NavigationStrategy, nullable: false, default: NavigationStrategy.AllAtOnce }) + NavigationStrategy: string; + + @Column({type: 'boolean', nullable: false, default: false }) + ScoringApplicable: boolean; + + @Column({type: 'uuid', nullable: true }) + RootSectionId?: string; + + @Column({type: 'uuid', nullable: true }) + ParentFolderId?: string; + + @Column({type: 'boolean', nullable: false, default: false }) + DefaultSectionNumbering: boolean; + + @Column({type: 'boolean', nullable: false, default: false }) + ApprovalRequired: boolean; + + @Column({ type: 'varchar', length: 512, nullable: true }) + Tags?: string; + + @OneToMany(() => FormSubmission, (submission) => submission.FormTemplate) + FormSubmissions: FormSubmission[]; + + @OneToMany(() => FormSection, (section) => section.FormTemplate) + FormSections: FormSection[]; + + @OneToMany(() => Question, (question) => question.FormTemplate) + Questions: Question[]; +} diff --git a/src/database/sql/typeorm/models/input.unit.list/input.unit.list.model.ts b/src/database/sql/typeorm/models/input.unit.list/input.unit.list.model.ts new file mode 100644 index 0000000..b57b926 --- /dev/null +++ b/src/database/sql/typeorm/models/input.unit.list/input.unit.list.model.ts @@ -0,0 +1,17 @@ +import { Column, Entity } from "typeorm"; +import { BaseEntity } from "../base.entity"; + +@Entity({ name: 'input_unit_lists' }) +export class InputUnitList extends BaseEntity { + + @Column({ type: 'varchar', length: 128, nullable: false }) + Name: string + + @Column({ type: 'varchar', length: 512, nullable: false }) + Description: string + + // Units represents JSON array + @Column({ type: 'json', nullable: false }) + Units: string +} + \ No newline at end of file diff --git a/src/database/sql/typeorm/models/question.response/question.response.model.ts b/src/database/sql/typeorm/models/question.response/question.response.model.ts new file mode 100644 index 0000000..c0eae92 --- /dev/null +++ b/src/database/sql/typeorm/models/question.response/question.response.model.ts @@ -0,0 +1,59 @@ +import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { BaseEntity } from '../base.entity'; +import { FormSubmission } from '../form.submission/form.submission.model'; +import { QueryResponseType, Question } from '../question/question.model'; + + +@Entity('question_responses') +export class QuestionResponse extends BaseEntity { + @Column({type: 'uuid', nullable: false }) + FormSubmissionId: string; + + @Column({type: 'uuid', nullable: false }) + QuestionId: string; + + @Column({ type: 'enum', enum: QueryResponseType, nullable: false, default: QueryResponseType.SingleChoiceSelection }) + ResponseType: QueryResponseType; + + @Column({type: 'number', nullable: true }) + Sequence: number; + + @Column({type: 'number', nullable: true }) + IntegerValue?: number; + + @Column({ nullable: true, type: 'float' }) + FloatValue?: number; + + @Column({type: 'varchar', nullable: true }) + BooleanValue?: string; + + @Column({type: 'timestamp', nullable: true }) + DateTimeValue?: Date; + + @Column({type: 'varchar', nullable: true }) + Url?: string; + + @Column({type: 'uuid', nullable: true }) + FileResourceId?: string; + + @Column({type: 'varchar', length: 2048, nullable: true }) + Text?: string; + + //Represent the response of the user + @Column({type: 'text', nullable: true }) + UserResponse: string; + + @Column() + SubmissionTimestamp: Date; + + @Column() + LastSaveTimestamp: Date; + + @ManyToOne(() => FormSubmission, (submission) => submission.QuestionResponses) + @JoinColumn({ name: 'FormSubmissionId' }) + FormSubmission: FormSubmission; + + @ManyToOne(() => Question, (question) => question.Responses) + @JoinColumn({ name: 'QuestionId' }) + Question: Question; +} diff --git a/src/database/sql/typeorm/models/question/question.model.ts b/src/database/sql/typeorm/models/question/question.model.ts new file mode 100644 index 0000000..3f70696 --- /dev/null +++ b/src/database/sql/typeorm/models/question/question.model.ts @@ -0,0 +1,91 @@ +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm'; +import { FormTemplate } from '../form.template/form.template.model'; +import { FormSection } from '../form.section/form.section.model'; +import { BaseEntity } from '../base.entity'; +import { QuestionResponse } from '../question.response/question.response.model'; +import { InputUnitList } from '../input.unit.list/input.unit.list.model'; + +export enum QueryResponseType { + Text = "Text", + Float = "Float", + Integer = "Integer", + Boolean = "Boolean", + Object = "Object", + TextArray = "TextArray", + SingleChoiceSelection = "SingleChoiceSelection", + MultiChoiceSelection = "MultiChoiceSelection", + File = "File", + Date = "Date", + DateTime = "DateTime", + Rating = "Rating", + Location = "Location", + Url = "Url", + Range = "Range" + } + +@Entity({ name: 'questions' }) +export class Question extends BaseEntity { + + @Column({type: 'uuid', nullable: true }) + TemplateId: string; + + @Column({type: 'uuid', nullable: false }) + ParentSectionId: string; + + @Column({type: 'varchar',length: 128, nullable: true }) + Title: string; + + @Column({type: 'varchar',length: 512, nullable: true }) + Description: string; + + @Column({type: 'varchar',length: 128, nullable: false }) + DisplayCode: string; + + @Column({ type: 'enum', enum: QueryResponseType }) + ResponseType: QueryResponseType; + + @Column({type: 'number', nullable: true }) + Score: number; + + @Column({type: 'number', nullable: true }) + Sequence: number; + + @Column({type: 'varchar', nullable: true }) + ExpectedAnswer: string; + + @Column({type: 'boolean', nullable: true }) + IsRequired: boolean; + + @Column({type: 'varchar', length: 512, nullable: true }) + Hint: string; + + @Column({ type: 'json', nullable: true }) + Options: string; + + @Column({type: 'uuid', nullable: true }) + ImageResourceId?: string; + + @Column({type: 'number', nullable: true }) + RangeMin: number; + + @Column({type: 'number', nullable: true }) + RangeMax: number; + + @Column({type: 'varchar', nullable: true }) + DefaultExpectedUnit: string; + + @Column({type: 'boolean', nullable: false, default: false }) + PageBreakAfter: boolean; + + @OneToMany(() => QuestionResponse, (response) => response.Question) + Responses: QuestionResponse[]; + + @ManyToOne(() => FormTemplate, (template) => template.Questions) + FormTemplate: FormTemplate; + + @ManyToOne(() => FormSection, (section) => section.Questions) + ParentFormSection: FormSection; + + @ManyToOne(() => InputUnitList) + ExpectedInputUnitList: InputUnitList +} diff --git a/src/database/sql/typeorm/models/template.folder/template.folder.model.ts b/src/database/sql/typeorm/models/template.folder/template.folder.model.ts new file mode 100644 index 0000000..021a1b4 --- /dev/null +++ b/src/database/sql/typeorm/models/template.folder/template.folder.model.ts @@ -0,0 +1,15 @@ +import { Column, Entity } from "typeorm"; +import { BaseEntity } from "../base.entity"; + +@Entity({ name: 'template_folders' }) +export class TemplateFolder extends BaseEntity { + + @Column({ type: 'varchar', length: 128, nullable: false }) + Name: string + + @Column({ type: 'varchar', length: 512, nullable: true }) + Description?: string + + @Column({ type: 'uuid', nullable: true }) + ParentFolderId?: string +} diff --git a/src/database/sql/typeorm/models/user/user.model.ts b/src/database/sql/typeorm/models/user/user.model.ts new file mode 100644 index 0000000..0282347 --- /dev/null +++ b/src/database/sql/typeorm/models/user/user.model.ts @@ -0,0 +1,31 @@ +import { Entity, Column, OneToMany } from 'typeorm'; +import { BaseEntity } from '../base.entity'; +import { FormTemplate } from '../form.template/form.template.model'; + + +@Entity('users') +export class User extends BaseEntity { + @Column() + FirstName: string; + + @Column() + LastName: string; + + @Column() + CountryCode: number; + + @Column() + Phone: string; + + @Column() + Email: string; + + @Column() + Username: string; + + @Column() + Password: string; + + // @OneToMany(() => FormTemplate, (template) => template.User) + // Templates: FormTemplate[]; +} diff --git a/src/database/sql/typeorm/repositories/base.repo.ts b/src/database/sql/typeorm/repositories/base.repo.ts new file mode 100644 index 0000000..f4c92ab --- /dev/null +++ b/src/database/sql/typeorm/repositories/base.repo.ts @@ -0,0 +1,36 @@ +import { FindManyOptions } from "typeorm"; +import { BaseSearchFilters } from "../../../../domain.types/miscellaneous/base.search.types"; + +export class BaseRepo{ + protected addSortingAndPagination = ( + search: FindManyOptions, filters: BaseSearchFilters) => { + + //Sorting + let orderByColumn = 'CreatedAt'; + if (filters.OrderBy) { + orderByColumn = filters.OrderBy; + } + let order = 'ASC'; + if (filters.Order === 'descending') { + order = 'DESC'; + } + search['order'] = {}; + search['order'][orderByColumn] = order; + + //Pagination + let limit = 25; + if (filters.ItemsPerPage) { + limit = filters.ItemsPerPage; + } + let offset = 0; + let pageIndex = 0; + if (filters.PageIndex) { + pageIndex = filters.PageIndex < 0 ? 0 : filters.PageIndex; + offset = pageIndex * limit; + } + search['take'] = limit; + search['skip'] = offset; + + return { search, pageIndex, limit, order, orderByColumn }; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts b/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts new file mode 100644 index 0000000..a54841c --- /dev/null +++ b/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts @@ -0,0 +1,200 @@ +import { FormSectionCreateModel, FormSectionResponseDto, FormSectionSearchFilters, FormSectionUpdateModel } from "../../../../../domain.types/forms/form.section.domain.types"; +import { IFormSectionRepo } from "../../../../repository.interfaces/form.section/form.section.repo.interface"; +import { FormSection } from "../../models/form.section/form.section.model"; +import { FormSectionMapper } from "../../mappers/form.section.mapper"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { FindManyOptions } from "typeorm"; +import { BaseRepo } from "../base.repo"; + + + +export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ + + _formSectionRepo = Source.getRepository(FormSection); + + create= async(model: FormSectionCreateModel): Promise =>{ + + // const entity={ + // ParentFormTemplate: { + // connect: { id: model.ParentFormTemplateId } + // }, + // SectionIdentifier: model.SectionIdentifier, + // Title: model.Title, + // Description: model.Description, + // DisplayCode: model.DisplayCode, + // Sequence: model.Sequence, + // ParentSectionId: model.ParentSectionId, + // } + + try { + const data = await this._formSectionRepo.create({ + // FormTemplate: { + // connect: { id: model.ParentFormTemplateId } + // }, + // SectionIdentifier: model.SectionIdentifier, + Title: model.Title, + Description: model.Description, + DisplayCode: model.DisplayCode, + Sequence: parseInt(model.Sequence), + ParentSectionId: model.ParentSectionId, + }); + const record = await this._formSectionRepo.save(data); + return FormSectionMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + + }; + + update=async (id: string, model: FormSectionUpdateModel): Promise => { + try { + const updateData = await this._formSectionRepo.findOne({ + where : { + id : id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError('Form Section Data not found!'); + } + // if (model.SectionIdentifier) { + // updateData.SectionIdentifier = model.SectionIdentifier; + // } + if (model.Title) { + updateData.Title = model.Title; + } + if ( model.Description) { + updateData.Description = model.Description; + } + if (model.DisplayCode) { + updateData.DisplayCode = model.DisplayCode; + } + + if (model.Sequence) { + updateData.Sequence = parseInt(model.Sequence); + } + + if (model.ParentSectionId) { + updateData.ParentSectionId = model.ParentSectionId; + } + var record = await this._formSectionRepo.save(updateData); + return FormSectionMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById=async (id: string): Promise => { + try { + var record = await this._formSectionRepo.findOne({ + where : { + id : id, + DeletedAt: null + }, + }); + return FormSectionMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete=async (id: string): Promise =>{ + try { + var record = await this._formSectionRepo.findOne({ + where : { + id : id, + DeletedAt: null, + }, + }); + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._formSectionRepo.save(record); + return true; // Soft delete successful + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getByTemplateId=async (id: string): Promise=>{ + try { + var record = await this._formSectionRepo.findOne({ + where : { + FormTemplateId : id + }, + }); + return FormSectionMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search=async (filters: FormSectionSearchFilters) : Promise =>{ + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._formSectionRepo.findAndCount(search); + + const searchResults = { + TotalCount : count, + RetrievedCount : list.length, + PageIndex : pageIndex, + ItemsPerPage : limit, + Order : order === 'DESC' ? 'descending' : 'ascending', + OrderedBy : orderByColumn, + Items : list.map(x => FormSectionMapper.toDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + } + }; + + private getSearchModel = (filters: FormSectionSearchFilters) => { + + var search : FindManyOptions = { + relations : {}, + where : {}, + }; + + if (filters.id) { + search.where['id'] = filters.id; + } + + if (filters.parentFormTemplateId) { + search.where['FormTemplateId'] = filters.parentFormTemplateId; + } + + // if (filters.sectionIdentifier) { + // where.SectionIdentifier = { + // equals: filters.sectionIdentifier, + // }; + // } + + if (filters.title) { + search.where['Title'] = filters.title; + } + + if (filters.description) { + search.where['Description'] = filters.description; + } + + if (filters.sequence) { + search.where['Sequence'] = filters.sequence; + } + if (filters.parentSectionId) { + search.where['ParentSectionId'] = filters.parentSectionId; + } + + return search; + }; + + +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts b/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts new file mode 100644 index 0000000..723f2bb --- /dev/null +++ b/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts @@ -0,0 +1,219 @@ +import { FormSubmissionCreateModel, FormSubmissionSearchFilters, FormSubmissionUpdateModel } from "../../../../../domain.types/forms/form.submission.domain.types"; +import { IFormSubmissionRepo } from "../../../../repository.interfaces/form.submission/form.submission.repo.interface"; +import { FormSubmissionDto } from "../../../../../domain.types/forms/form.submission.domain.types"; +import { FormStatus, FormSubmission } from "../../models/form.submission/form.submission.model"; +import { Source } from "../../database.connector.typeorm"; +import { FormMapper } from "../../mappers/form.submission.mapper"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { FindManyOptions } from "typeorm"; +import { uuid } from "../../../../../domain.types/miscellaneous/system.types"; +import { BaseRepo } from "../base.repo"; + +export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo{ + + _formSubmissionRepo = Source.getRepository(FormSubmission); + + create=async (model: FormSubmissionCreateModel) : Promise => { + try { + const data = await this._formSubmissionRepo.create({ + // FormTemplate: { + // connect: { id: model.ParentFormTemplateId } + // }, + // SectionIdentifier: model.SectionIdentifier, + // FormTemplate: { + // connect: { id: model.FormTemplateId } + // }, + + UserId: model.UserId, + Status: model.Status, + ValidTill: model.ValidTill, + + }); + const record = await this._formSubmissionRepo.save(data); + return FormMapper.toDto(record); + } + + catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + + }; + + update=async (id: string, model: FormSubmissionUpdateModel) : Promise => { + try { + const updateData = await this._formSubmissionRepo.findOne({ + where : { + id : id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError('Form Section Data not found!'); + } + // if (model.SectionIdentifier) { + // updateData.SectionIdentifier = model.SectionIdentifier; + // } + if (model.UserId) { + updateData.UserId = model.UserId; + } + if ( model.Encrypted) { + updateData.Encrypted = model.Encrypted; + } + if (model.Unencrypted) { + updateData.Unencrypted = model.Unencrypted; + } + + if (model.Link) { + updateData.Link = model.Link; + } + + // if (model.QueryParams) { + // updateData.QueryParams = model.QueryParams; + // } + + if (model.LinkQueryParams) { + updateData.LinkQueryParams = model.LinkQueryParams; + } + + if (model.ValidTill) { + updateData.ValidTill = model.ValidTill; + } + + if (model.SubmittedAt) { + updateData.SubmittedAt = model.SubmittedAt; + } + + // if (model.Status) { + // updateData.Status = model.Status; + // } + + // if (model.Category) { + // updateData.Category = model.Category; + // } + + var record = await this._formSubmissionRepo.save(updateData); + return FormMapper.toDto(record); + } + catch (error) + { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById=async (id: string) : Promise => { + try { + var record = await this._formSubmissionRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return FormMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete=async (id: string) : Promise => { + try { + var record = await this._formSubmissionRepo.findOne({ + where : { + id : id, + DeletedAt: null, + }, + }); + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._formSubmissionRepo.save(record); + return true; // Soft delete successful + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + submit=async (id: uuid) : Promise => { + try { + const record = await this._formSubmissionRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!record) { + ErrorHandler.throwNotFoundError("Form Section Data not found!"); + } + + record.SubmittedAt= new Date(); + record.Status=FormStatus.Submitted; + record.UpdatedAt=new Date(); + + var submittedData = await this._formSubmissionRepo.save(record); + return FormMapper.toDto(submittedData); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search=async (filters: FormSubmissionSearchFilters) : Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._formSubmissionRepo.findAndCount(search); + + const searchResults = { + TotalCount : count, + RetrievedCount : list.length, + PageIndex : pageIndex, + ItemsPerPage : limit, + Order : order === 'DESC' ? 'descending' : 'ascending', + OrderedBy : orderByColumn, + Items : list.map(x => FormMapper.toDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + } + }; + + private getSearchModel = (filters: FormSubmissionSearchFilters) => { + + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.FormTemplateId) { + search.where["FormTemplateId"] = filters.FormTemplateId; + } + + if (filters.UserId) { + search.where["UserId"] = filters.UserId; + } + + if (filters.Encrypted) { + search.where["Encrypted"] = filters.Encrypted; + } + + if (filters.Status) { + search.where["Status"] = filters.Status; + } + + if (filters.ValidTill) { + search.where["ValidTill"] = filters.ValidTill; + } + + if (filters.SubmittedAt) { + search.where["SubmittedAt"] = filters.SubmittedAt; + } + + + return search; + }; + +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts b/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts new file mode 100644 index 0000000..59d34cd --- /dev/null +++ b/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts @@ -0,0 +1,568 @@ +import { ExportFormTemplateDto, FormTemplateCreateModel, SectionDto, SectionPreviewDto, SubsectionDto, TemplateDto, TemplatePreviewDto } from "../../../../../domain.types/forms/form.template.domain.types"; +import { IFormTemplateRepo } from "../../../../repository.interfaces/form.template/form.template.repo.interface"; +import { FormTemplateResponseDto } from "../../../../../domain.types/forms/form.template.domain.types"; +import { FormTemplateUpdateModel } from "../../../../../domain.types/forms/form.template.domain.types"; +import { FormTemplateSearchFilters } from "../../../../../domain.types/forms/form.template.domain.types"; +import { FormTemplate } from "../../models/form.template/form.template.model"; +import { Source } from "../../database.connector.typeorm"; +import { FormType } from "../../models/form.template/form.template.model"; +import { FormTemplateMapper } from "../../mappers/form.template.mapper"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, IsNull } from "typeorm"; +import { Question } from "../../models/question/question.model"; +import { QuestionMapper } from "../../mappers/question.mapper"; +import { FormSection } from "../../models/form.section/form.section.model"; + +export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { + _formTemplateRepo = Source.getRepository(FormTemplate); + + _question = Source.getRepository(Question); + + _formSection = Source.getRepository(FormSection); + + create = async ( + model: FormTemplateCreateModel + ): Promise => { + try { + const data = await this._formTemplateRepo.create({ + // FormTemplate: { + // connect: { id: model.ParentFormTemplateId } + // }, + // SectionIdentifier: model.SectionIdentifier, + // FormTemplate: { + // connect: { id: model.FormTemplateId } + // }, + Title: model.Title, + Description: model.Description, + // CurrentVersion: model.CurrentVersion, + // TenantCode: model.TenantCode, + Type: model.Type as FormType, + // ItemsPerPage: model.ItemsPerPage, + DisplayCode: model.DisplayCode, + // OwnerUserId: model.OwnerUserId, + RootSectionId: model.RootSectionId, + DefaultSectionNumbering: model.DefaultSectionNumbering, + }); + const record = await this._formTemplateRepo.save(data); + return FormTemplateMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update = async ( + id: string, + model: FormTemplateUpdateModel + ): Promise => { + try { + const updateData = await this._formTemplateRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError("Form Section Data not found!"); + } + // if (model.SectionIdentifier) { + // updateData.SectionIdentifier = model.SectionIdentifier; + // } + if (model.Title) { + updateData.Title = model.Title; + } + if (model.Description) { + updateData.Description = model.Description; + } + // if (model.CurrentVersion) { + // updateData.CurrentVersion = model.CurrentVersion; + // } + + // if (model.TenantCode) { + // updateData.TenantCode = model.TenantCode; + // } + + // if (model.QueryParams) { + // updateData.QueryParams = model.QueryParams; + // } + + if (model.Type) { + updateData.Type = model.Type; + } + + // if (model.ItemsPerPage) { + // updateData.ItemsPerPage = model.ItemsPerPage; + // } + + // if (model.Status) { + // updateData.Status = model.Status; + // } + + // if (model.Category) { + // updateData.Category = model.Category; + // } + + updateData.UpdatedAt = new Date(); + + var record = await this._formTemplateRepo.save(updateData); + return FormTemplateMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById = async (id: string): Promise => { + try { + var record = await this._formTemplateRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return FormTemplateMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getDetailsById = async (id: string) => { + const record = await this._formTemplateRepo.findOne({ + where: { + id: id, + DeletedAt: IsNull(), + }, + relations: { + FormSections: { + Questions: true, + FormTemplate: true, + }, + }, + order: { + FormSections: { + CreatedAt: "ASC", + Questions: { + CreatedAt: "ASC", + }, + }, + }, + }); + + if (record && record.FormSections) { + record.FormSections = record.FormSections.filter( + (section) => section.DeletedAt === null + ); + + record.FormSections.forEach((section) => { + if (section.Questions) { + section.Questions = section.Questions.filter( + (question) => question.DeletedAt === null + ); + } + }); + } + + const subsections = await this.mapSections(record.FormSections); + record.FormSections = subsections; + + return record; + }; + + mapSections = async (sections: any[]) => { + const sectionMap = new Map(); + + // Initialize sections and assign an empty array for Subsections + sections.forEach((section) => { + sectionMap.set(section.id, { ...section, Subsections: [] }); + }); + + const rootSections: any[] = []; + + // Assign subsections to their respective parents + sections.forEach((section) => { + if (section.ParentSectionId !== null) { + const parent = sectionMap.get(section.ParentSectionId); + if (parent) { + parent.Subsections.push(sectionMap.get(section.id)); + } + } else { + rootSections.push(sectionMap.get(section.id)); + } + }); + + return rootSections; + }; + + readTemplateObjToExport = async ( + id: string + ): Promise => { + // Fetch main template + const template = await this._formTemplateRepo.findOne({ + where: { id, DeletedAt: null }, + }); + if (!template) { + throw new Error(`Template with ID ${id} not found`); + } + + // Define the Template DTO with required fields + const templateDto: TemplateDto = { + id: template.id, + Title: template.Title, + Description: template.Description, + // CurrentVersion: template.CurrentVersion, + // TenantCode: template.TenantCode, + Type: template.Type as FormType, + // ItemsPerPage: template.ItemsPerPage, + DisplayCode: template.DisplayCode, + // OwnerUserId: template.OwnerUserId, + RootSectionId: template.RootSectionId, + DefaultSectionNumbering: template.DefaultSectionNumbering, + CreatedAt: template.CreatedAt, + UpdatedAt: template.UpdatedAt, + Sections: [], + }; + + // Fetch all top-level sections related to the template + // const sections = await this._formTemplateRepo.find({ + // where: { + // ParentFormTemplateId: id, + // ParentSectionId: null, + // DeletedAt: null, + // }, + // include: { ParentFormTemplate: true }, + // }); + + const sections = await this._formSection.find({ + where: { + FormTemplateId: id, + ParentSectionId: null, + DeletedAt: null, + }, + }); + + // For each section, fetch subsections and questions + for (const section of sections) { + const dtoSection: SectionDto = { + id: section.id, + // SectionIdentifier: section.SectionIdentifier, + Title: section.Title, + Description: section.Description, + DisplayCode: section.DisplayCode, + // Sequence: section.Sequence, + ParentSectionId: section.ParentSectionId, + CreatedAt: section.CreatedAt, + UpdatedAt: section.UpdatedAt, + Subsections: [], + Questions: [], + }; + + // Fetch and map questions associated with this section + const sectionQuestions = await this._question.find({ + where: { ParentSectionId: section.id, DeletedAt: null }, + }); + dtoSection.Questions = sectionQuestions.map((question) => + QuestionMapper.toDto(question) + ); + + // Fetch and map subsections + const subsections = await this._formSection.find({ + where: { ParentSectionId: section.id, DeletedAt: null }, + }); + + for (const subsection of subsections) { + const dtoSubsection: SubsectionDto = { + id: subsection.id, + // SectionIdentifier: subsection.SectionIdentifier, + Title: subsection.Title, + Description: subsection.Description, + DisplayCode: subsection.DisplayCode, + Sequence: subsection.Sequence, + ParentSectionId: subsection.ParentSectionId, + CreatedAt: subsection.CreatedAt, + UpdatedAt: subsection.UpdatedAt, + Questions: [], + }; + + // Fetch questions for this subsection + const subsectionQuestions = await this._question.find({ + where: { ParentSectionId: subsection.id, DeletedAt: null }, + }); + dtoSubsection.Questions = subsectionQuestions.map((question) => + QuestionMapper.toDto(question) + ); + + // Add the subsection to the parent section + dtoSection.Subsections.push(dtoSubsection); + } + + // Add the section to the Template DTO + templateDto.Sections.push(dtoSection); + } + + // Flatten Sections to top-level property as required by ExportFormTemplateDto + const exportDto: ExportFormTemplateDto = { + Template: templateDto, + Sections: templateDto.Sections, // Explicitly include Sections at the top level + }; + + return exportDto; + }; + + previewTemplate = async (id: string) => { + const template = await this._formTemplateRepo.findOne({ + where: { id, DeletedAt: null }, + }); + + if (!template) { + throw new Error(`Template with ID ${id} not found`); + } + + const templateDto: TemplatePreviewDto = { + id: template.id, + Title: template.Title, + Description: template.Description, + // CurrentVersion: template.CurrentVersion, + // TenantCode: template.TenantCode, + Type: template.Type as FormType, + // ItemsPerPage: template.ItemsPerPage, + DisplayCode: template.DisplayCode, + // OwnerUserId: template.OwnerUserId, + RootSectionId: template.RootSectionId, + DefaultSectionNumbering: template.DefaultSectionNumbering, + CreatedAt: template.CreatedAt, + UpdatedAt: template.UpdatedAt, + RootSection: [], + }; + + // const sections = await this.prisma.formSection.findMany({ + // where: { ParentFormTemplateId: id, DeletedAt: null }, + // include: { + // ParentFormTemplate: true, + + // Questions: true + // } + // }) + // console.log('****', JSON.stringify(sections)) + const rootSection = await this._formSection.findOne({ + where: { + FormTemplateId: id, + Title: "Assessment Root Section", + DeletedAt: null, + }, + }); + + if (!rootSection) { + throw new Error( + `No section found with Title 'Assessment Root Section' for template ID ${id}` + ); + } + + const rootSectionId = rootSection.id; + const allSections = await this._formSection.find({ + where: { + FormTemplateId: id, + DeletedAt: IsNull(), + }, + relations: { + FormTemplate: true, + Questions: true, + }, + order: { + CreatedAt: "ASC", // Sorting sections in ascending order + Questions: { + CreatedAt: "ASC", // Sorting questions within each section + }, + }, + }); + // return allSections; + return await this.mapSections1(allSections); + + console.log("****", JSON.stringify(allSections)); + + const mapSections = async ( + parentId: string | null + ): Promise => { + const sections = await this._formSection.find({ + where: { + ParentSectionId: parentId, + FormTemplateId: id, + DeletedAt: null, + }, + }); + + return Promise.all( + sections.map(async (section) => { + const sectionDto: SectionPreviewDto = { + id: section.id, + // SectionIdentifier: section.SectionIdentifier, + Title: section.Title, + Description: section.Description, + DisplayCode: section.DisplayCode, + Sequence: section.Sequence, + ParentSectionId: section.ParentSectionId, + CreatedAt: section.CreatedAt, + UpdatedAt: section.UpdatedAt, + Questions: [], + Sections: [], + }; + + const questions = await this._question.find({ + where: { ParentSectionId: section.id, DeletedAt: null }, + }); + sectionDto.Questions = questions.map((q) => QuestionMapper.toDto(q)); + + const subSections = await mapSections(section.id); + if (subSections.length > 0) { + sectionDto.Sections = subSections; + } + + return sectionDto; + }) + ); + }; + + const rootSectionDto: SectionPreviewDto = { + id: rootSection.id, + // SectionIdentifier: rootSection.SectionIdentifier, + Title: rootSection.Title, + Description: rootSection.Description, + DisplayCode: rootSection.DisplayCode, + Sequence: rootSection.Sequence, + ParentSectionId: rootSection.ParentSectionId, + CreatedAt: rootSection.CreatedAt, + UpdatedAt: rootSection.UpdatedAt, + Questions: [], + Sections: await mapSections(rootSection.id), + }; + + templateDto.RootSection.push(rootSectionDto); + + return templateDto; + }; + + mapSections1 = async (sections: any[]) => { + const sectionMap = new Map(); + + // Initialize sections and assign an empty array for Subsections + sections.forEach((section) => { + sectionMap.set(section.id, { ...section, Subsections: [] }); + }); + + const rootSections: any[] = []; + + // Assign subsections to their respective parents + sections.forEach((section) => { + if (section.ParentSectionId !== null) { + const parent = sectionMap.get(section.ParentSectionId); + if (parent) { + parent.Subsections.push(sectionMap.get(section.id)); + } + } else { + rootSections.push(sectionMap.get(section.id)); + } + }); + + return rootSections; + }; + + delete = async (id: string) : Promise=> { + const record = await this._formTemplateRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._formTemplateRepo.save(record); + + return true; // Soft delete successful + }; + + submissions = async (id: string) : Promise=> { + const response = await this._formTemplateRepo.find({ + where: { + id: id, + DeletedAt: null, + }, + }); + return FormTemplateMapper.toArrayDto(response); + }; + + search = async (filters: FormTemplateSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._formTemplateRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => FormTemplateMapper.toDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: FormTemplateSearchFilters) => { + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where["id"] = filters.id; + } + + if (filters.Title) { + search.where["Title"] = filters.Title; + } + + if (filters.TenantCode) { + search.where["TenantCode"] = filters.TenantCode; + } + + if (filters.Description) { + search.where["Description"] = filters.Description; + } + + if (filters.CurrentVersion) { + search.where["CurrentVersion"] = filters.CurrentVersion; + } + + if (filters.Type) { + search.where["Type"] = filters.Type; + } + + if (filters.DisplayCode) { + search.where["DisplayCode"] = filters.DisplayCode; + } + + if (filters.OwnerUserId) { + search.where["OwnerUserId"] = filters.OwnerUserId; + } + if (filters.RootSectionId) { + search.where["RootSectionId"] = filters.RootSectionId; + } + if (filters.DefaultSectionNumbering) { + search.where["DefaultSectionNumbering"] = filters.DefaultSectionNumbering; + } + + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts b/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts new file mode 100644 index 0000000..800f135 --- /dev/null +++ b/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts @@ -0,0 +1,353 @@ +import { QuestionResponseCreateModel } from "../../../../../domain.types/forms/response.domain.types"; +import { QueryResponseType } from "../../models/question/question.model"; +import { IResponseRepo } from "../../../../repository.interfaces/question.response/question.response.repo.interface"; +import { QuestionResponseUpdateModel } from "../../../../../domain.types/forms/response.domain.types"; +import { QuestionResponseResponseDto } from "../../../../../domain.types/forms/response.domain.types"; +import { QuestionResponseSearchFilters } from "../../../../../domain.types/forms/response.domain.types"; +import { QuestionResponse } from "../../models/question.response/question.response.model"; +import { Source } from "../../database.connector.typeorm"; +import { Logger } from "../../../../../common/logger"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { ResponseMapper } from "../../mappers/question.response.mapper"; +import * as fs from 'fs'; +import { FindManyOptions } from "typeorm"; +import { QuestionMapper } from "../../mappers/question.mapper"; +import { Question } from "../../models/question/question.model"; +import PDFDocument from 'pdfkit'; +import { BaseRepo } from "../base.repo"; +import path from "path"; +import { createObjectCsvWriter } from "csv-writer"; +import { QuestionResponseDto } from "../../../../../domain.types/forms/question.domain.types"; + +export class ResponseRepo extends BaseRepo implements IResponseRepo{ + + _responseRepo = Source.getRepository(QuestionResponse); + + _questionRepo = Source.getRepository(Question); + + create=async (model: QuestionResponseCreateModel) : Promise => { + try { + const data = await this._responseRepo.create({ + ResponseType: model.ResponseType as QueryResponseType, + IntegerValue: model.IntegerValue, + FloatValue: model.FloatValue, + BooleanValue: model.BooleanValue, + DateTimeValue: model.DateTimeValue, + Url: model.Url, + FileResourceId: model.FileResourceId, + Text: model.TextValue, + SubmissionTimestamp: null, + LastSaveTimestamp: new Date(), + }); + const record = await this._responseRepo.save(data); + return ResponseMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + save=async (model: any) => { + try { + const data = await this._responseRepo.create({ + ResponseType: model.ResponseType as QueryResponseType, + FloatValue: model.FloatValue, + IntegerValue: model.IntegerValue, + BooleanValue: model.BooleanValue, + DateTimeValue: model.DateTimeValue, + Url: model.Url, + Text: model.TextValue, + SubmissionTimestamp: null, + LastSaveTimestamp: new Date(), + }); + const record = await this._responseRepo.save(data); + return ResponseMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update=async (id: string, model: QuestionResponseUpdateModel) : Promise => { + try { + + const updateData = await this._responseRepo.findOne({ + where : { + id : id, + DeletedAt: null, + }, + }); + + + if (!updateData) { + ErrorHandler.throwNotFoundError('Question Response Data not found!'); + } + // if (model.SectionIdentifier) { + // updateData.SectionIdentifier = model.SectionIdentifier; + // } + if (model.ResponseType) { + updateData.ResponseType = model.ResponseType; + } + if ( model.IntegerValue) { + updateData.IntegerValue = model.IntegerValue; + } + if (model.FloatValue) { + updateData.FloatValue = model.FloatValue; + } + + if (model.BooleanValue) { + updateData.BooleanValue = model.BooleanValue; + } + + // if (model.QueryParams) { + // updateData.QueryParams = model.QueryParams; + // } + + if (model.DateTimeValue) { + updateData.DateTimeValue = model.DateTimeValue; + } + + if (model.Url) { + updateData.Url = model.Url; + } + + if (model.FileResourceId) { + updateData.FileResourceId = model.FileResourceId; + } + + if (model.TextValue) { + updateData.Text = model.TextValue; + } + + updateData.LastSaveTimestamp=new Date(); + + updateData.UpdatedAt=new Date(); + + // if (model.Status) { + // updateData.Status = model.Status; + // } + + // if (model.Category) { + // updateData.Category = model.Category; + // } + + var record = await this._responseRepo.save(updateData); + return ResponseMapper.toDto(record); + } + catch (error) + { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById=async (id: string) : Promise => { + try { + var record = await this._responseRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return ResponseMapper.toDto(record); + } + + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getQuestionById=async (id: string) : Promise => { + try { + var record = await this._questionRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return QuestionMapper.toDto(record); + } + catch (error) + { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete=async (id: string) : Promise => { + try { + var record = await this._responseRepo.findOne({ + where : { + id : id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._responseRepo.save(record); + return true; // Soft delete successful + } + catch (error) + { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getAll=async () : Promise => { + try { + const data = []; + var prompts = await this._responseRepo.find(); + for (var i of prompts) { + const record = ResponseMapper.toDto(i); + // const record = i; + data.push(record); + } + return data; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to get Llm prompt record!', error); + } + }; + + exportCsv = async (): Promise => { + const data = await this.getAll(); + const csvPath = path.join(__dirname, '../../../storage/data.csv'); + const csvWriter = createObjectCsvWriter({ + path: csvPath, + header: [ + { id: 'id', title: 'ID' }, + { id: 'FormSubmission.id', title: 'Submission ID' }, + { id: 'FormSubmission.TemplateId', title: 'Template ID' }, + { id: 'FormSubmission.FormUrl', title: 'Form URL' }, + { id: 'FormSubmission.UserId', title: 'User ID' }, + { id: 'FormSubmission.Status', title: 'Status' }, + { id: 'FormSubmission.SubmissionTimestamp', title: 'Submission Timestamp' }, + { id: 'FormSubmission.CreatedAt', title: 'Created At' }, + { id: 'Question.id', title: 'Question ID' }, + { id: 'Question.Title', title: 'Question Title' }, + { id: 'Question.Description', title: 'Question Description' }, + { id: 'Question.DisplayCode', title: 'Display Code' }, + { id: 'Question.ResponseType', title: 'Response Type' }, + { id: 'Question.Score', title: 'Score' }, + { id: 'Question.CorrectAnswer', title: 'Correct Answer' }, + { id: 'Question.Hint', title: 'Hint' }, + { id: 'Question.TemplateId', title: 'Question Template ID' }, + { id: 'Question.SectionId', title: 'Question Section ID' }, + { id: 'Question.CreatedAt', title: 'Question Created At' }, + { id: 'Question.UpdatedAt', title: 'Question Updated At' }, + { id: 'ResponseType', title: 'Query Response Type' }, + { id: 'IntegerValue', title: 'Integer Value' }, + { id: 'FloatValue', title: 'Float Value' }, + { id: 'BooleanValue', title: 'Boolean Value' }, + { id: 'DateTimeValue', title: 'Date Time Value' }, + { id: 'Url', title: 'URL' }, + { id: 'FileResourceId', title: 'File Resource ID' }, + { id: 'TextValue', title: 'Text Value' }, + { id: 'SubmissionTimestamp', title: 'Submission Timestamp' }, + { id: 'LastSaveTimestamp', title: 'Last Save Timestamp' } + ] + }); + await csvWriter.writeRecords(data); + return csvPath; + }; + + exportPdf = async (): Promise => { + const data = await this.getAll(); + const pdfPath = path.join(__dirname, '../../../storage/data.pdf'); + const doc = new PDFDocument(); + doc.pipe(fs.createWriteStream(pdfPath)); + + doc.text('ID\tSubmission ID\tTemplate ID\tForm URL\tUser ID\tStatus\tSubmission Timestamp\tCreated At\t' + + 'Question ID\tQuestion Title\tQuestion Description\tDisplay Code\tResponse Type\tScore\t' + + 'Correct Answer\tHint\tQuestion Template ID\tQuestion Section ID\tQuestion Created At\t' + + 'Question Updated At\tQuery Response Type\tInteger Value\tFloat Value\tBoolean Value\t' + + 'Date Time Value\tURL\tFile Resource ID\tText Value\tSubmission Timestamp\tLast Save Timestamp'); + + data.forEach(row => { + doc.text(`${row.id}\t${row.FormSubmission.id}\t${row.FormSubmission.TemplateId}\t${row.FormSubmission.FormUrl}\t${row.FormSubmission.UserId}\t${row.FormSubmission.Status}\t${row.FormSubmission.SubmissionTimestamp}\t${row.FormSubmission.CreatedAt}\t` + + `${row.Question.id}\t${row.Question.Title}\t${row.Question.Description}\t${row.Question.DisplayCode}\t${row.Question.ResponseType}\t${row.Question.Score}\t${row.Question.CorrectAnswer}\t${row.Question.Hint}\t` + + `${row.Question.TemplateId}\t${row.Question.SectionId}\t${row.Question.CreatedAt}\t${row.Question.UpdatedAt}\t${row.ResponseType}\t${row.IntegerValue}\t${row.FloatValue}\t${row.BooleanValue}\t` + + `${row.DateTimeValue}\t${row.Url}\t${row.FileResourceId}\t${row.TextValue}\t${row.SubmissionTimestamp}\t${row.LastSaveTimestamp}`); + }); + + doc.end(); + return pdfPath; + }; + + + search=async (filters: QuestionResponseSearchFilters) : Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._responseRepo.findAndCount(search); + + const searchResults = { + TotalCount : count, + RetrievedCount : list.length, + PageIndex : pageIndex, + ItemsPerPage : limit, + Order : order === 'DESC' ? 'descending' : 'ascending', + OrderedBy : orderByColumn, + Items : list.map(x => ResponseMapper.toDto(x)), + }; + + return searchResults; + } + + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + } + }; + + private getSearchModel = (filters: QuestionResponseSearchFilters) => { + + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.FormSubmissionId) { + search.where["FormSubmissionId"] = + filters.FormSubmissionId; + } + + if (filters.QuestionId) { + search.where["QuestionId"] = filters.QuestionId; + } + + if (filters.ResponseType) { + search.where["ResponseType"] = + filters.ResponseType; + } + + if (filters.IntegerValue) { + search.where["IntegerValue"] = + filters.IntegerValue; + } + + if (filters.FloatValue) { + search.where["FloatValue"] = filters.FloatValue; + } + + if (filters.BooleanValue) { + search.where["BooleanValue"] = + filters.BooleanValue; + } + if (filters.Url) { + search.where["Url"] = filters.Url; + } + if (filters.FileResourceId) { + search.where["FileResourceId"] = + filters.FileResourceId; + } + if (filters.TextValue) { + search.where["TextValue"] = filters.TextValue; + } + + + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/question/question.repo.ts b/src/database/sql/typeorm/repositories/question/question.repo.ts new file mode 100644 index 0000000..2dc9055 --- /dev/null +++ b/src/database/sql/typeorm/repositories/question/question.repo.ts @@ -0,0 +1,278 @@ +import { QuestionCreateModel, QuestionOption } from "../../../../../domain.types/forms/question.domain.types"; +import { IQuestionRepo } from "../../../../repository.interfaces/question/question.repo.interface"; +import { QuestionResponseDto } from "../../../../../domain.types/forms/question.domain.types"; +import { QuestionUpdateModel } from "../../../../../domain.types/forms/question.domain.types"; +import { QuestionSearchFilters } from "../../../../../domain.types/forms/question.domain.types"; +import { Source } from "../../database.connector.typeorm"; +import { Question } from "../../models/question/question.model"; +import { QuestionMapper } from "../../mappers/question.mapper"; +import { Logger } from "../../../../../common/logger"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { FindManyOptions } from "typeorm"; +import { BaseRepo } from "../base.repo"; +import { QueryResponseType } from "../../models/question/question.model"; + +export class QuestionRepo extends BaseRepo implements IQuestionRepo{ + + _questionRepo = Source.getRepository(Question); + + create=async (model: QuestionCreateModel) : Promise => { + try { + + // let jsonData: Prisma.JsonValue | undefined; + + let jsonData: QuestionOption[] | undefined; + + // Map model.Options to the appropriate structure for JSON storage + if (model.Options && model.Options.length > 0) { + jsonData = model.Options.map((option) => ({ + Text: option.Text, + Sequence: option.Sequence, + ImageUrl: option.ImageUrl, + })); + } + + const data = await this._questionRepo.create({ + Title: model.Title, + Description: model.Description, + DisplayCode: model.DisplayCode, + ResponseType: model.ResponseType as QueryResponseType, + Score: model.Score, + // CorrectAnswer: model.CorrectAnswer, + IsRequired: model.IsRequired, + Hint: model.Hint, + Sequence: model.Sequence, + Options: JSON.stringify(jsonData), // Only assign if jsonData is defined + // QuestionImageUrl: model.QuestionImageUrl, + RangeMax: model.RangeMax, + RangeMin: model.RangeMin, + CreatedAt: new Date(), + // UpdatedAt: new Date(), // Uncomment and modify as needed + // DeletedAt: null, // Uncomment and modify as needed + }); + const record = await this._questionRepo.save(data); + return QuestionMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update=async (id: string, model: QuestionUpdateModel) : Promise => { + try { + + // let jsonData: Prisma.JsonValue | undefined; + + let jsonData: QuestionOption[] | undefined; + + // Map model.Options to the appropriate structure for JSON storage + if (model.Options && model.Options.length > 0) { + jsonData = model.Options.map((option) => ({ + Text: option.Text, + Sequence: option.Sequence, + ImageUrl: option.ImageUrl, + })); + } + + + const updateData = await this._questionRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError("Question Data not found!"); + } + // if (model.SectionIdentifier) { + // updateData.SectionIdentifier = model.SectionIdentifier; + // } + if (model.Title) { + updateData.Title = model.Title; + } + if (model.Description) { + updateData.Description = model.Description; + } + if (model.DisplayCode) { + updateData.DisplayCode = model.DisplayCode; + } + + if (model.Score) { + updateData.Score = model.Score; + } + + // if (model.CorrectAnswer) { + // updateData.CorrectAnswer = model.CorrectAnswer; + // } + + if (model.IsRequired) { + updateData.IsRequired = model.IsRequired; + } + + if (model.Hint) { + updateData.Hint = model.Hint; + } + + updateData.Options=JSON.stringify(jsonData); + + // if (model.QuestionImageUrl) { + // updateData.QuestionImageUrl = model.QuestionImageUrl; + // } + + if (model.RangeMax) { + updateData.RangeMax = model.RangeMax; + } + + if (model.RangeMin) { + updateData.RangeMin = model.RangeMin; + } + + updateData.UpdatedAt=new Date(); + + var record = await this._questionRepo.save(updateData); + return QuestionMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById=async (id: string) : Promise => { + try { + var record = await this._questionRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return QuestionMapper.toDto(record); + } + catch (error) + { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getByTemplateId=async (id: string) : Promise => { + try { + var record = await this._questionRepo.findOne({ + where : { + TemplateId : id + }, + }); + return QuestionMapper.toDto(record); + } + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete=async (id: string) : Promise => { + try { + var record = await this._questionRepo.findOne({ + where : { + id : id, + DeletedAt: null, + }, + }); + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._questionRepo.save(record); + return true; // Soft delete successful + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search=async (filters: QuestionSearchFilters) : Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._questionRepo.findAndCount(search); + + const searchResults = { + TotalCount : count, + RetrievedCount : list.length, + PageIndex : pageIndex, + ItemsPerPage : limit, + Order : order === 'DESC' ? 'descending' : 'ascending', + OrderedBy : orderByColumn, + Items : list.map(x => QuestionMapper.toDto(x)), + }; + + return searchResults; + } + + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + } + }; + + private getSearchModel = (filters: QuestionSearchFilters) => { + + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where['id'] = filters.id + } + + if (filters.parentTemplateId) { + search.where['ParentTemplateId'] = filters.parentTemplateId + } + + if (filters.parentSectionId) { + search.where['ParentSectionId'] = filters.parentSectionId + } + + if (filters.title) { + search.where['Title'] = filters.title + } + + if (filters.description) { + search.where['Description'] = filters.description + } + + if (filters.displayCode) { + search.where['DisplayCode'] = filters.displayCode + } + + if (filters.responseType) { + search.where['ResponseType'] = filters.responseType + } + + if (filters.score) { + search.where['Score'] = filters.score + } + if (filters.isRequired) { + search.where['IsRequired'] = filters.isRequired + } + if (filters.hint) { + search.where['Hint'] = filters.hint + } + + if (filters.questionImageUrl) { + search.where['QuestionImageUrl'] = filters.questionImageUrl + } + + if (filters.rangeMin) { + search.where['RangeMin'] = filters.rangeMin + } + + if (filters.rangeMax) { + search.where['RangeMax'] = filters.rangeMax + } + + if (filters.correctAnswer) { + search.where['CorrectAnswer'] = filters.correctAnswer + } + + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/user/user.repo.ts b/src/database/sql/typeorm/repositories/user/user.repo.ts new file mode 100644 index 0000000..9ac5fab --- /dev/null +++ b/src/database/sql/typeorm/repositories/user/user.repo.ts @@ -0,0 +1,207 @@ +import { UserCreateModel } from "../../../../../domain.types/forms/user.domain.types"; +import { IUserRepo } from "../../../../repository.interfaces/user/user.repo.interface"; +import { UserResponseDto } from "../../../../../domain.types/forms/user.domain.types"; +import { UserUpdateModel } from "../../../../../domain.types/forms/user.domain.types"; +import { UserSearchFilters } from "../../../../../domain.types/forms/user.domain.types"; +import { Source } from "../../database.connector.typeorm"; +import { User } from "../../models/user/user.model"; +import { UserMapper } from "../../mappers/user.mapper"; +import { Logger } from "../../../../../common/logger"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { FindManyOptions } from "typeorm"; +import { BaseRepo } from "../base.repo"; + +export class UserRepo extends BaseRepo implements IUserRepo{ + + _userRepo = Source.getRepository(User); + + allUsers = async () => { + const response = await this._userRepo.find({ + where: { + DeletedAt: null + } + }); + return UserMapper.toArrayDto(response); + }; + + + create=async (model: UserCreateModel) : Promise => { + try { + const data = await this._userRepo.create({ + FirstName: model.FirstName, + LastName: model.LastName, + CountryCode: model.CountryCode, + Phone: model.Phone, + Email: model.Email, + Username: model.Username, + Password: model.Password, + }); + const record = await this._userRepo.save(data); + return UserMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update=async (id: string, model: UserUpdateModel) : Promise => { + try { + + const updateData = await this._userRepo.findOne({ + where : { + id : id, + DeletedAt: null, + }, + }); + + + if (!updateData) { + ErrorHandler.throwNotFoundError('Question Response Data not found!'); + } + + if (model.FirstName) { + updateData.FirstName = model.FirstName; + } + if ( model.LastName) { + updateData.LastName = model.LastName; + } + if (model.CountryCode) { + updateData.CountryCode = model.CountryCode; + } + + if (model.Phone) { + updateData.Phone = model.Phone; + } + + if (model.Email) { + updateData.Email = model.Email; + } + + if (model.Username) { + updateData.Username = model.Username; + } + + if (model.Password) { + updateData.Password = model.Password; + } + + updateData.UpdatedAt=new Date(); + + + var record = await this._userRepo.save(updateData); + return UserMapper.toDto(record); + } + catch (error) + { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById=async (id: string) : Promise => { + try { + var record = await this._userRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return UserMapper.toDto(record); + } + + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete=async (id: string) : Promise => { + try { + var record = await this._userRepo.findOne({ + where : { + id : id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._userRepo.save(record); + return true; // Soft delete successful + } + catch (error) + { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search=async (filters: UserSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._userRepo.findAndCount(search); + + const searchResults = { + TotalCount : count, + RetrievedCount : list.length, + PageIndex : pageIndex, + ItemsPerPage : limit, + Order : order === 'DESC' ? 'descending' : 'ascending', + OrderedBy : orderByColumn, + Items : list.map(x => UserMapper.toDto(x)), + }; + + return searchResults; + } + + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + } + }; + + private getSearchModel = (filters: UserSearchFilters) => { + + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.firstName) { + search.where["FirstName"] = filters.firstName; + }; + + if (filters.lastName) { + search.where["LastName"] = { + equals: filters.lastName, + }; + } + + if (filters.countryCode) { + search.where["CountryCode"] = { + equals: filters.countryCode, + }; + } + + if (filters.email) { + search.where["Email"] = { + equals: filters.email, + }; + } + + if (filters.username) { + search.where["Username"] = { + equals: filters.username, + }; + } + + if (filters.password) { + search.where["Password"] = { + equals: filters.password, + }; + } + + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/typeorm.injector.ts b/src/database/sql/typeorm/typeorm.injector.ts new file mode 100644 index 0000000..f343875 --- /dev/null +++ b/src/database/sql/typeorm/typeorm.injector.ts @@ -0,0 +1,30 @@ +import 'reflect-metadata'; +import { DependencyContainer } from 'tsyringe'; +import { DatabaseConnector_TypeOrm } from './database.connector.typeorm'; +import { FormSectionRepo } from './repositories/form.section/form.section.repo'; +import { FormSubmissionRepo } from './repositories/form.submission/form.submission.repo'; +import { FormTemplateRepo } from './repositories/form.template/form.template.repo'; +import { QuestionRepo } from './repositories/question/question.repo'; +import { ResponseRepo } from './repositories/question.response/question.response.repo'; +import { UserRepo } from './repositories/user/user.repo'; + + + +//////////////////////////////////////////////////////////////////////////////// + +export class TypeOrmInjector { + + static registerInjections(container: DependencyContainer) { + + container.register('IPrimaryDatabaseConnector', DatabaseConnector_TypeOrm); + + container.register('IFormSectionRepo', FormSectionRepo); + container.register('IFormSubmissionRepo', FormSubmissionRepo); + container.register('IFormTemplateRepo', FormTemplateRepo); + container.register('IQuestionRepo', QuestionRepo); + container.register('IResponseRepo', ResponseRepo); + container.register('IUserRepo', UserRepo); + + + } +} diff --git a/src/domain.types/forms/form.section.domain.types.ts b/src/domain.types/forms/form.section.domain.types.ts index c39970c..2262644 100644 --- a/src/domain.types/forms/form.section.domain.types.ts +++ b/src/domain.types/forms/form.section.domain.types.ts @@ -14,8 +14,8 @@ export interface FormSectionUpdateModel { SectionIdentifier?: string; Title?: string; Description?: string; - // DisplayCode?: string; - // Sequence?: string-; + DisplayCode?: string; + Sequence?: string; ParentSectionId?: string; } diff --git a/src/domain.types/forms/form.submission.domain.types.ts b/src/domain.types/forms/form.submission.domain.types.ts index 074c256..a38746b 100644 --- a/src/domain.types/forms/form.submission.domain.types.ts +++ b/src/domain.types/forms/form.submission.domain.types.ts @@ -1,4 +1,5 @@ import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; +import { FormStatus } from "../../database/sql/typeorm/models/form.submission/form.submission.model"; export interface FormSubmissionCreateModel { FormTemplateId: string; @@ -84,12 +85,12 @@ export interface FormSubmissionSearchResponseDto extends BaseSearchResults{ UpdatedAt: Date; }; -export enum FormStatus { - LinkShared = "LinkShared", - Presented = "Presented", - InProgress = "InProgress", - Submitted = "Submitted", - } +// export enum FormStatus { +// LinkShared = "LinkShared", +// Presented = "Presented", +// InProgress = "InProgress", +// Submitted = "Submitted", +// } export enum FormType { Survey = "Survey", diff --git a/src/domain.types/forms/form.template.domain.types.ts b/src/domain.types/forms/form.template.domain.types.ts index 0f1a0c3..a0e0cfb 100644 --- a/src/domain.types/forms/form.template.domain.types.ts +++ b/src/domain.types/forms/form.template.domain.types.ts @@ -1,6 +1,7 @@ // import { FormType } from "../miscellaneous/system.types"; -import { FormType, ItemsPerPage, QueryResponseType } from "@prisma/client"; +// import { FormType, ItemsPerPage, QueryResponseType } from "@prisma/client"; +import { FormType } from "../../database/sql/typeorm/models/form.template/form.template.model"; import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; import { uuid } from "../miscellaneous/system.types"; import { QuestionOption } from "./question.domain.types"; @@ -9,9 +10,9 @@ export interface FormTemplateCreateModel { Title: string; Description?: string; CurrentVersion?: number; - TenantId?: string; + TenantCode?: string; Type: FormType; - ItemsPerPage: ItemsPerPage; + // ItemsPerPage: ItemsPerPage; DisplayCode?: string; OwnerUserId?: string; RootSectionId?: string; @@ -22,22 +23,25 @@ export interface FormTemplateUpdateModel { Title?: string; Description?: string; CurrentVersion?: number; - TenantId?: string; + TenantCode?: string; Type?: FormType; - ItemsPerPage?: ItemsPerPage; + // ItemsPerPage?: ItemsPerPage; + DisplayCode?: string; + OwnerUserId?: string; RootSectionId?: string; + DefaultSectionNumbering?: boolean } export interface FormTemplateResponseDto { id: string; Title: string; Description: string; - CurrentVersion: number; - TenantId: string; + CurrentVersion?: number; + TenantCode?: string; Type: FormType; - ItemsPerPage: ItemsPerPage; + // ItemsPerPage: ItemsPerPage; DisplayCode: string; - OwnerUserId: string; + OwnerUserId?: string; RootSectionId: string; DefaultSectionNumbering: boolean CreatedAt: Date; @@ -49,7 +53,7 @@ export interface FormTemplateSearchFilters extends BaseSearchFilters { Title?: string; Description?: string; CurrentVersion?: number; - TenantId: string; + TenantCode: string; Type?: FormType; DisplayCode?: string; OwnerUserId?: uuid; @@ -67,7 +71,7 @@ export interface FormTemplateSearchResponseDto extends BaseSearchResults { Title: string; Description: string; CurrentVersion: number; - TenantId: string; + TenantCode: string; Type: FormType; DisplayCode: string; OwnerUserId: string; @@ -89,25 +93,26 @@ export interface TemplateDto extends FormTemplateResponseDto { export interface SectionDto { id: string; - SectionIdentifier: string; + SectionIdentifier? : string; Title: string; Description: string; DisplayCode: string; - Sequence: string; - ParentSectionId: string | null; + Sequence?: string; + ParentSectionId?: string | null; CreatedAt: Date; UpdatedAt: Date; - Questions: QuestionDto[]; - Subsections: SubsectionDto[]; + Questions?: QuestionDto[]; + Subsections?: SubsectionDto[]; } export interface SubsectionDto { id: string; - SectionIdentifier: string; + SectionIdentifier?: string; Title: string; Description: string; DisplayCode: string; - Sequence: string; + // Sequence: string; + Sequence: number; ParentSectionId: string; CreatedAt: Date; UpdatedAt: Date; @@ -119,7 +124,7 @@ export interface QuestionDto { Title: string; Description?: string; DisplayCode: string | null; - ResponseType: QueryResponseType; + // ResponseType: QueryResponseType; Score: number; Sequence: string; CorrectAnswer: string; @@ -160,11 +165,12 @@ export interface QuestionDto { export interface SectionPreviewDto { id: string; - SectionIdentifier: string; + SectionIdentifier?: string; Title: string; Description: string; DisplayCode: string; - Sequence: string; + // Sequence: string; + Sequence: number; ParentSectionId: string | null; CreatedAt: Date; UpdatedAt: Date; @@ -176,12 +182,12 @@ export interface TemplatePreviewDto { id: string; Title: string; Description: string; - CurrentVersion: number; - TenantId: string; + CurrentVersion?: number; + TenantCode?: string; Type: FormType; - ItemsPerPage: ItemsPerPage; + // ItemsPerPage: ItemsPerPage; DisplayCode: string; - OwnerUserId: string; + OwnerUserId?: string; RootSectionId: string; DefaultSectionNumbering: boolean CreatedAt: Date; diff --git a/src/domain.types/forms/question.domain.types.ts b/src/domain.types/forms/question.domain.types.ts index 8f159fe..9e3aac2 100644 --- a/src/domain.types/forms/question.domain.types.ts +++ b/src/domain.types/forms/question.domain.types.ts @@ -1,6 +1,7 @@ // import { QueryResponseType } from "../miscellaneous/system.types"; -import { QueryResponseType } from "@prisma/client"; +// import { QueryResponseType } from "@prisma/client"; +import { QueryResponseType } from "../../database/sql/typeorm/models/question/question.model"; import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; // export interface QuestionOption { @@ -41,6 +42,7 @@ export interface QuestionCreateModel { DisplayCode: string; ResponseType: QueryResponseType; Score?: number; + // Sequence?: string; Sequence?: number; CorrectAnswer: string; IsRequired?: boolean; @@ -62,8 +64,6 @@ export interface QuestionUpdateModel { CorrectAnswer?: string; IsRequired?: boolean; Hint?: string; - Sequence?: number; - ParentSectionId?: string; Options?: QuestionOption[]; // FileResourceId ?: string; QuestionImageUrl?: string; @@ -165,7 +165,6 @@ export interface QuestionSearchFilters extends BaseSearchFilters { correctAnswer?: string; isRequired?: boolean; hint?: string; - sequence?: number; options?: QuestionOption[]; // FileResourceId : string; questionImageUrl?: string; diff --git a/src/domain.types/forms/response.domain.types.ts b/src/domain.types/forms/response.domain.types.ts index 315c752..181ab31 100644 --- a/src/domain.types/forms/response.domain.types.ts +++ b/src/domain.types/forms/response.domain.types.ts @@ -1,6 +1,7 @@ // import { FormStatus, QueryResponseType } from "../miscellaneous/system.types" import { FormStatus} from "@prisma/client"; +import { QueryResponseType } from "../../database/sql/typeorm/models/question/question.model"; import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; export interface QuestionResponseCreateModel { @@ -132,4 +133,4 @@ export interface QuestionResponseSearchResponseDto extends BaseSearchResults{ LastSaveTimestamp : Date } -export type QueryResponseType = "Text" | "Float" | "Integer" | "Boolean" | "Object" | "TextArray" | "SingleChoiceSelection" | "MultiChoiceSelection" | "File" | "Date" | "DateTime" | "Rating" | "Location" | "Url" | "Range"; +// export type QueryResponseType = "Text" | "Float" | "Integer" | "Boolean" | "Object" | "TextArray" | "SingleChoiceSelection" | "MultiChoiceSelection" | "File" | "Date" | "DateTime" | "Rating" | "Location" | "Url" | "Range"; diff --git a/src/domain.types/miscellaneous/system.types.ts b/src/domain.types/miscellaneous/system.types.ts index 57f2f5d..823d52d 100644 --- a/src/domain.types/miscellaneous/system.types.ts +++ b/src/domain.types/miscellaneous/system.types.ts @@ -1,3 +1,5 @@ export type uuid = string | undefined | null; +export type DatabaseDialect = 'postgres' | 'mysql' | 'sqlite'; + export type integer = number | undefined | null; \ No newline at end of file diff --git a/src/modules/module.injector.ts b/src/modules/module.injector.ts new file mode 100644 index 0000000..dfef8e2 --- /dev/null +++ b/src/modules/module.injector.ts @@ -0,0 +1,19 @@ +import 'reflect-metadata'; +import { DependencyContainer } from 'tsyringe'; +// import { CommunicationInjector } from './communication/communication.injector'; +// import { EhrInjector } from './ehr/ehr.injector'; +import { FileStorageInjector } from './storage/file.storage.injector'; + +//////////////////////////////////////////////////////////////////////////////// + +export class ModuleInjector { + + static registerInjections(container: DependencyContainer) { + + // EhrInjector.registerInjections(container); + // CommunicationInjector.registerInjections(container); + FileStorageInjector.registerInjections(container); + + } + +} diff --git a/src/modules/reports/pdf.generator.ts b/src/modules/reports/pdf.generator.ts new file mode 100644 index 0000000..f1cbe9b --- /dev/null +++ b/src/modules/reports/pdf.generator.ts @@ -0,0 +1,228 @@ + +import fs from 'fs'; +import path from 'path'; +// import { Logger } from '../../common/logger'; +// import { ApiError } from '../../common/api.error'; +// import { Helper } from '../../common/helper'; +import { TimeHelper } from '../../common/time.helper'; +import pdfkit from 'pdfkit'; +import { ConfigurationManager } from "../../config/configuration.manager"; +import { DateStringFormat } from '../../domain.types/miscellaneous/time.types'; + +/////////////////////////////////////////////////////////////////////////////////////// + +export class PDFGenerator { + + static getAbsoluteFilePath = async (filePrefix) => { + var uploadFolder = ConfigurationManager.UploadTemporaryFolder(); + var dateFolder = TimeHelper.getDateString(new Date(), DateStringFormat.YYYY_MM_DD); + var fileFolder = path.join(uploadFolder, dateFolder); + if (!fs.existsSync(fileFolder)) { + await fs.promises.mkdir(fileFolder, { recursive: true }); + } + const timestamp = TimeHelper.timestamp(new Date()); + var filename = filePrefix + timestamp + '.pdf'; + var absFilepath = path.join(fileFolder, filename); + return { absFilepath, filename }; + }; + + static createDocument = (title, author, writeStream) => { + + const document = new pdfkit({ + bufferPages : true, + size : 'A4', + info : { + Title : title, + Author : author, + }, + margins : { + top : 0, + bottom : 0, + left : 0, + right : 50 + } + }); + document.pipe(writeStream); + return document; + }; + + static addNewPage = (document) => { + + document.addPage({ + size : 'A4', + margins : { + top : 0, + bottom : 0, + left : 0, + right : 50 + } + }); + }; + + static addOrderPageNumber = (document, pageNumber, totalPages, color = '#222222') => { + + var pageNumberStr = 'Page: ' + pageNumber.toString() + ' of ' + totalPages.toString(); + + document + .fontSize(8) + .fillColor(color) + .text(pageNumberStr, 0, 780, { align: "right" }); + + }; + + static savePDFLocally = async (writeStream, absFilepath: string): Promise => { + + return new Promise((resolve, reject) => { + + writeStream + .on('finish', () => { + return resolve(absFilepath); + }) + .on('error', (error) => { + return reject(error); + }); + }); + }; + + static addHeader = (document, model, y: number, headerImagePath) => { + + var imageFile = path.join(process.cwd(), headerImagePath); + + document + .image(imageFile, 0, 0, { width: 595 }) + .fillColor("#ffffff") + .font('Helvetica') + .fontSize(16) + .text(model.DoctorName, 100, y, { align: 'right' }); + + document + .fontSize(7); + + y = y + 20; + if (model.Specialization != null){ + document + .font('Helvetica') + .text(model.Specialization, 100, y, { align: "right" }); + } + + document + .fontSize(9); + + y = y + 12; + if (model.DoctorPhonenumber != null){ + document + .font('Helvetica-Bold') + .text(model.DoctorPhonenumber, 100, y, { align: "right" }); + } + + document.moveDown(); + + return y; + }; + + static addOrderFooter = (document, model, y, logoImagePath) => { + + //var imageFile = path.join(process.cwd(), "./assets/images/REANCare_Footer.png"); + var imageFile = path.join(process.cwd(), logoImagePath); + + document + .image(imageFile, 0, 800, { width: 595 }); + + document + .fontSize(8) + .fillColor('#ffffff'); + if (model.ClinicName != null){ + document + .text(model.ClinicName, 100, 810, { align: "right" }); + } + + document.fontSize(7); + if (model.ClinicAddress != null){ + document + .text(model.ClinicAddress, 100, 822, { align: "right" }); + } + + return y; + }; + + static drawLine = (document, fromX, fromY, toX, toY) => { + document + .strokeColor("#aaaaaa") + .lineWidth(1) + .moveTo(fromX, fromY) + .lineTo(toX, toY) + .stroke(); + }; + + static addOrderMetadata = (y, document, model) => { + + y = y + 35; + + document + .fillColor('#444444') + .fontSize(10) + .text('Date: ' + model.OrderDate, 200, y, { align: "right" }) + .moveDown(); + + var imageFile = path.join(process.cwd(), "./assets/images/Prescription_symbol.png"); + document + .image(imageFile, 50, y, { width: 25, height: 25, align: 'left' }); + + y = y + 10; + + document + .fillColor('#444444') + .fontSize(16) + .font('Helvetica-Bold') + .text(model.DocumentTitle, 0, y, { align: "center" }) + .moveDown(); + + y = y + 30; + + //DrawLine(document, y); + document + .roundedRect(50, y, 500, 60, 1) + .lineWidth(0.1) + .fillOpacity(0.8) + //.fillAndStroke("#EBE0FF", "#6541A5"); + .fill("#EBE0FF"); + + y = y + 10; + + document + .fillOpacity(1.0) + .lineWidth(1) + .fill("#444444"); + + document + .fillColor("#444444") + .font('Helvetica') + .fontSize(10); + + document + .font('Helvetica-Bold') + .text('Patient', 75, y, { align: "left" }) + .font('Helvetica') + .text(model.PatientName, 175, y, { align: "left" }) + .moveDown(); + + y = y + 15; + document + .font('Helvetica-Bold') + .text('Patient ID', 75, y, { align: "left" }) + .font('Helvetica') + .text(model.PatientDisplayId, 175, y, { align: "left" }) + .moveDown(); + + y = y + 15; + document + .font('Helvetica-Bold') + .text('Prescription ID', 75, y, { align: "left" }) + .font('Helvetica') + .text(model.OrderDisplayId, 175, y, { align: "left" }) + .moveDown(); + + return y; + }; + +} diff --git a/src/modules/storage/file.storage.injector.ts b/src/modules/storage/file.storage.injector.ts new file mode 100644 index 0000000..52cc37a --- /dev/null +++ b/src/modules/storage/file.storage.injector.ts @@ -0,0 +1,22 @@ +import 'reflect-metadata'; +import { ConfigurationManager } from '../../config/configuration.manager'; +import { DependencyContainer } from 'tsyringe'; +import { AWSS3FileStorageService } from './providers/aws.s3.file.storage.service'; +import { CustomFileStorageService } from './providers/custom.file.storage.service'; + +//////////////////////////////////////////////////////////////////////////////// + +export class FileStorageInjector { + + static registerInjections(container: DependencyContainer) { + + const provider = ConfigurationManager.FileStorageProvider(); + if (provider === 'AWS-S3') { + container.register('IFileStorageService', AWSS3FileStorageService); + } + else if (provider === 'Custom') { + container.register('IFileStorageService', CustomFileStorageService); + } + } + +} diff --git a/src/modules/storage/interfaces/file.storage.service.interface.ts b/src/modules/storage/interfaces/file.storage.service.interface.ts new file mode 100644 index 0000000..7e6b0d2 --- /dev/null +++ b/src/modules/storage/interfaces/file.storage.service.interface.ts @@ -0,0 +1,18 @@ +import { Stream } from "stream"; + +export interface IFileStorageService { + + exists(storageKey: string): Promise; + + upload(storageKey: string, sourceFilePath: string): Promise; + + uploadStream(storageKey: string, stream: Stream): Promise; + + download(storageKey: string, localFilePath: string): Promise; + + rename(existingStorageKey: string, newFileName: string): Promise; + + getShareableLink(storageKey: string, durationInMinutes: number): Promise; + + delete(storageKey: string): Promise; +} diff --git a/src/modules/storage/providers/aws.s3.file.storage.service.ts b/src/modules/storage/providers/aws.s3.file.storage.service.ts new file mode 100644 index 0000000..4089a82 --- /dev/null +++ b/src/modules/storage/providers/aws.s3.file.storage.service.ts @@ -0,0 +1,198 @@ +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; +import * as aws from "@aws-sdk/client-s3"; +import fs from 'fs'; +import { Logger } from '../../../common/logger'; +import { IFileStorageService } from '../interfaces/file.storage.service.interface'; +import { GetObjectCommand } from "@aws-sdk/client-s3"; +import { Readable } from "stream"; + +/////////////////////////////////////////////////////////////////////////////////// + +export class AWSS3FileStorageService implements IFileStorageService { + + //#region Publics + + exists = async (storageKey: string): Promise => { + try { + const s3 = this.getS3Client(); + const params = { + Bucket : process.env.STORAGE_BUCKET, + Key : storageKey, + }; + const command = new aws.HeadObjectCommand(params); + const result = await s3.send(command); + Logger.instance().log(JSON.stringify(result, null, 2)); + + if (result.$metadata.httpStatusCode <= 204) { + return null; + } + return storageKey; + } + catch (error) { + Logger.instance().log(JSON.stringify(error, null, 2)); + return null; + } + }; + + uploadStream = async (storageKey: string, stream: Readable): Promise => { + + try { + const s3 = this.getS3Client(); + const params = { + Bucket : process.env.STORAGE_BUCKET, + Key : storageKey, + Body : stream.read() + }; + const command = new aws.PutObjectCommand(params); + const response = await s3.send(command); + + Logger.instance().log(JSON.stringify(response, null, 2)); + + return storageKey; + } + catch (error) { + Logger.instance().log(error.message); + } + }; + + upload = async (storageKey: string, localFilePath?: string): Promise => { + + try { + const fileContent = fs.readFileSync(localFilePath); + Logger.instance().log(`Upload file to S3: ${storageKey}`); + + const s3 = this.getS3Client(); + const params = { + Bucket : process.env.STORAGE_BUCKET, + Key : storageKey, + Body : fileContent + }; + + const command = new aws.PutObjectCommand(params); + const response = await s3.send(command); + + Logger.instance().log(JSON.stringify(response, null, 2)); + + return storageKey; + } + catch (error) { + Logger.instance().log(error.message); + } + }; + + download = async (storageKey: string, localFilePath: string): Promise => { + + const s3 = this.getS3Client(); + const params = { + Bucket : process.env.STORAGE_BUCKET, + Key : storageKey, + }; + //var s3Path = storageKey; + // var tokens = s3Path.split('/'); + // var localFile = tokens[tokens.length - 1]; + // var folderPath = path.join(TEMP_DOWNLOAD_FOLDER, localFolder); + // var localDestination = path.join(folderPath, localFile); + + const file = fs.createWriteStream(localFilePath); + const command = new GetObjectCommand(params); + const response = await s3.send(command); + const stream = response.Body as Readable; + return new Promise((resolve, reject) => { + stream.on('end', () => { + //var st = fs.existsSync(localFilePath); + var stats = fs.statSync(localFilePath); + var count = 0; + while (stats.size === 0 && count < 5) { + setTimeout(() => { + stats = fs.statSync(localFilePath); + }, 3000); + count++; + } + return resolve(localFilePath); + }).on('error', (error) => { + return reject(error); + }) + .pipe(file); + }); + }; + + rename = async (storageKey: string, newFileName: string): Promise => { + + const s3 = this.getS3Client(); + + var s3Path = storageKey; + var tokens = s3Path.split('/'); + var existingFileName = tokens[tokens.length - 1]; + var newPath = s3Path.replace(existingFileName, newFileName); + if (newPath === s3Path){ + throw new Error('Old and new file identifiers are same!'); + } + + var BUCKET_NAME = process.env.STORAGE_BUCKET; + var OLD_KEY = s3Path; + var NEW_KEY = newPath; + + const params = { + Bucket : BUCKET_NAME, + CopySource : `${BUCKET_NAME}/${OLD_KEY}`, + Key : NEW_KEY + }; + + // copy object + const copyCommand = new aws.CopyObjectCommand(params); + await s3.send(copyCommand); + + // delete old object + const deleteCommand = new aws.DeleteObjectCommand({ + Bucket : BUCKET_NAME, + Key : OLD_KEY + }); + await s3.send(deleteCommand); + + return true; + }; + + delete = async (storageKey: string): Promise => { + + const s3 = this.getS3Client(); + const params = { + Bucket : process.env.STORAGE_BUCKET, + Key : storageKey + }; + + const command = new aws.DeleteObjectCommand(params); + const result = await s3.send(command); + Logger.instance().log(`Delete result from S3: ${JSON.stringify(result)}`); + if (result.$metadata.httpStatusCode !== 204) { + return false; + } + return true; + }; + + getShareableLink = async (storageKey: string, durationInMinutes: number): Promise => { + + const s3 = this.getS3Client(); + const getObjectParams = { Bucket: process.env.STORAGE_BUCKET, Key: storageKey }; + const command = new aws.GetObjectCommand(getObjectParams); + const url = await getSignedUrl(s3, command, { expiresIn: durationInMinutes * 60 }); + + return url; + }; + + //#endregion + + //#region Privates + + getS3Client = (): aws.S3 => { + return new aws.S3({ + credentials : { + accessKeyId : process.env.STORAGE_BUCKET_ACCESS_KEY_ID, + secretAccessKey : process.env.STORAGE_BUCKET_ACCESS_KEY_SECRET + }, + region : process.env.STORAGE_CLOUD_REGION + }); + }; + + //#endregion + +} diff --git a/src/modules/storage/providers/custom.file.storage.service.ts b/src/modules/storage/providers/custom.file.storage.service.ts new file mode 100644 index 0000000..ad28f93 --- /dev/null +++ b/src/modules/storage/providers/custom.file.storage.service.ts @@ -0,0 +1,121 @@ +import fs from 'fs'; +import path from 'path'; +import { Stream } from 'stream'; +import { Logger } from '../../../common/logger'; +import { IFileStorageService } from '../interfaces/file.storage.service.interface'; + +/////////////////////////////////////////////////////////////////////////////////// + +export class CustomFileStorageService implements IFileStorageService { + + _storagePath: string = path.join(process.env.STORAGE_BUCKET, process.env.NODE_ENV); + + //#region Publics + + exists = async (storageKey: string): Promise => { + try { + const location = path.join(this._storagePath, storageKey); + var fileExists = fs.existsSync(location); + if (!fileExists) { + return null; + } + return storageKey; + } + catch (error) { + Logger.instance().log(JSON.stringify(error, null, 2)); + return null; + } + }; + + uploadStream = async (storageKey: string, stream: Stream): Promise => { + return new Promise( (resolve, reject) => { + try { + const location = path.join(this._storagePath, storageKey); + const directory = path.dirname(location); + fs.mkdirSync(directory, { recursive: true }); + const writeStream = fs.createWriteStream(location); + stream.pipe(writeStream); + writeStream.on('finish', async () => { + writeStream.end(); + resolve(storageKey); + }); + } + catch (error) { + Logger.instance().log(error.message); + reject(error.message); + } + }); + }; + + upload = async (storageKey: string, localFilePath?: string): Promise => { + try { + const fileContent = fs.readFileSync(localFilePath); + const location = path.join(this._storagePath, storageKey); + + const directory = path.dirname(location); + await fs.promises.mkdir(directory, { recursive: true }); + + fs.writeFileSync(location, fileContent, { flag: 'w' }); + return storageKey; + } + catch (error) { + Logger.instance().log(error.message); + return null; + } + }; + + download = async (storageKey: string, localFilePath: string): Promise => { + try { + const location = path.join(this._storagePath, storageKey); + const fileContent = fs.readFileSync(location); + + const directory = path.dirname(localFilePath); + await fs.promises.mkdir(directory, { recursive: true }); + + fs.writeFileSync(localFilePath, fileContent, { flag: 'w' }); + return localFilePath; + } + catch (error) { + Logger.instance().log(error.message); + return null; + } + }; + + rename = async (storageKey: string, newFileName: string): Promise => { + try { + var oldPath = path.join(this._storagePath, storageKey); + var tokens = oldPath.split('/'); + var existingFileName = tokens[tokens.length - 1]; + var newPath = oldPath.replace(existingFileName, newFileName); + if (newPath === oldPath){ + throw new Error('Old and new file identifiers are same!'); + } + fs.renameSync(oldPath, newPath); + return true; + } + catch (error) { + Logger.instance().log(error.message); + return false; + } + }; + + delete = async (storageKey: string): Promise => { + try { + const location = path.join(this._storagePath, storageKey); + fs.unlinkSync(location); + return true; + } + catch (error) { + Logger.instance().log(error.message); + return false; + } + }; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getShareableLink = async (storageKey: string, _durationInMinutes: number): Promise => { + return path.join(this._storagePath, storageKey); + }; + + //#endregion + +} diff --git a/src/services/form.section.service.ts b/src/services/form.section.service.ts deleted file mode 100644 index 5fcf67e..0000000 --- a/src/services/form.section.service.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { Prisma, PrismaClient } from "@prisma/client"; -import { PrismaClientInit } from "../startup/prisma.client.init"; -import { FormSectionMapper } from "../mappers/form.section.mapper"; -import { FormSectionCreateModel, FormSectionSearchFilters, FormSectionUpdateModel } from "../domain.types/forms/form.section.domain.types"; -import { ErrorHandler } from "../common/error.handler"; - - -export class FormSectionService { - prisma: PrismaClient = null; - constructor() { - this.prisma = PrismaClientInit.instance().getPrismaInstance(); - } - - // allFormSections = async (): Promise => { - // const response = await this.prisma.formSection.findMany({ - // include: { - // ParentFormTemplate: true - // }, - // where: { - // DeletedAt: null, - // } - // }); - // return FormSectionMapper.toArrayDto(response); - // }; - - create = async (model: FormSectionCreateModel) => { - const response = await this.prisma.formSection.create({ - data: { - ParentFormTemplate: { - connect: { id: model.ParentFormTemplateId } - }, - SectionIdentifier: model.SectionIdentifier, - Title: model.Title, - Description: model.Description, - DisplayCode: model.DisplayCode, - Sequence: model.Sequence, - ParentSectionId: model.ParentSectionId, - }, - include: { - ParentFormTemplate: true - } - }); - return FormSectionMapper.toDto(response); - }; - - update = async (id: string, model: FormSectionUpdateModel) => { - const response = await this.prisma.formSection.update({ - data: { - SectionIdentifier: model.SectionIdentifier, - Title: model.Title, - Description: model.Description, - // DisplayCode: model.DisplayCode, - // Sequence: model.Sequence, - ParentSectionId: model.ParentSectionId, - UpdatedAt: new Date(), - }, - where: { - id: id, - DeletedAt: null - }, - include: { - ParentFormTemplate: true, - }, - }); - return FormSectionMapper.toDto(response); - }; - - getById = async (id: string) => { - const response = await this.prisma.formSection.findUnique({ - where: { - id: id, - DeletedAt: null - }, - include: { - ParentFormTemplate: true, - } - }); - return FormSectionMapper.toDto(response); - }; - - delete = async (id: string) => { - const response = await this.prisma.formSection.update({ - where: { - id: id, - DeletedAt: null - }, - data: { - DeletedAt: new Date(), - }, - include: { - ParentFormTemplate: true, - } - }); - return FormSectionMapper.toDto(response); - }; - - getByTemplateId = async (id: string) => { - const response = await this.prisma.formSection.findMany({ - where: { - ParentFormTemplateId: id, - DeletedAt: null - }, - include: { - ParentFormTemplate: true, - } - }); - return FormSectionMapper.toArrayDto(response); - }; - - protected addSortingAndPagination = ( - search: Prisma.FormSectionFindManyArgs, - filters: FormSectionSearchFilters - ) => { - // Sorting - let orderByColumn: keyof typeof Prisma.FormSectionScalarFieldEnum = 'CreatedAt'; - if (filters.OrderBy) { - orderByColumn = filters.OrderBy as keyof typeof Prisma.FormSectionScalarFieldEnum; - } - let order: Prisma.SortOrder = 'asc'; - if (filters.Order === 'descending') { - order = 'desc'; - } - - search.orderBy = { - [orderByColumn]: order, - }; - - // Pagination - let limit = 25; - if (filters.ItemsPerPage) { - limit = filters.ItemsPerPage; - } - let offset = 0; - let pageIndex = 1; - if (filters.PageIndex) { - pageIndex = filters.PageIndex < 1 ? 1 : filters.PageIndex; - offset = (pageIndex - 1) * limit; - } - - search.take = limit; - search.skip = offset; - - // Update where clause - const whereClause = this.getSearchModel(filters); - if (Object.keys(whereClause).length > 0) { - search.where = whereClause; - } - - return { search, pageIndex, limit, order, orderByColumn }; - }; - - public search = async (filters: FormSectionSearchFilters) => { - try { - const { search: prismaSearch, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination({}, filters); - - const list = await this.prisma.formSection.findMany({ - where: prismaSearch.where, - include: { - ParentFormTemplate: true, - }, - take: limit, - skip: (pageIndex - 1) * limit, - orderBy: { - [orderByColumn]: order === 'desc' ? 'desc' : 'asc', - }, - }); - - const count = await this.prisma.formSection.count({ - where: prismaSearch.where, - }); - - const searchResults = { - TotalCount: count, - RetrievedCount: list.length, - PageIndex: pageIndex, - ItemsPerPage: limit, - Order: order === 'desc' ? 'descending' : 'ascending', - OrderedBy: orderByColumn, - Items: list.map((x) => FormSectionMapper.toDto(x)), - // Items: FormSectionMapper.toArrayDto(list), - }; - - return searchResults; - } catch (error) { - ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); - } - }; - - private getSearchModel = (filters: FormSectionSearchFilters): Prisma.FormSectionWhereInput => { - const where: Prisma.FormSectionWhereInput = { - DeletedAt: null - }; - - if (filters.id) { - where.id = { - equals: filters.id, - }; - } - - if (filters.parentFormTemplateId) { - where.ParentFormTemplateId = { - equals: filters.parentFormTemplateId, - }; - } - - if (filters.sectionIdentifier) { - where.SectionIdentifier = { - equals: filters.sectionIdentifier, - }; - } - - if (filters.title) { - where.Title = { - equals: filters.title, - }; - } - - if (filters.description) { - where.Description = { - equals: filters.description, - }; - } - - if (filters.sequence) { - where.Sequence = { - equals: filters.sequence, - }; - } - if (filters.parentSectionId) { - where.ParentSectionId = { - equals: filters.parentSectionId, - }; - } - return where; - }; -} diff --git a/src/services/form.section/form.section.service.ts b/src/services/form.section/form.section.service.ts new file mode 100644 index 0000000..ac12178 --- /dev/null +++ b/src/services/form.section/form.section.service.ts @@ -0,0 +1,58 @@ +import { Prisma, PrismaClient } from "@prisma/client"; +import { PrismaClientInit } from "../../startup/prisma.client.init"; +import { FormSectionMapper } from "../../database/sql/typeorm/mappers/form.section.mapper"; +import { FormSectionCreateModel, FormSectionResponseDto, FormSectionSearchFilters, FormSectionUpdateModel } from "../../domain.types/forms/form.section.domain.types"; +// import { ErrorHandler } from "../../common/error.handler"; +import { inject,injectable } from "tsyringe"; +import { IFormSectionRepo } from "../../database/repository.interfaces/form.section/form.section.repo.interface"; + +@injectable() +export class FormSectionService { + + constructor(@inject('IFormSectionRepo') private _formSectionRepo: IFormSectionRepo) { + + } + + // allFormSections = async (): Promise => { + // const response = await this.prisma.formSection.findMany({ + // include: { + // ParentFormTemplate: true + // }, + // where: { + // DeletedAt: null, + // } + // }); + // return FormSectionMapper.toArrayDto(response); + // }; + + create = async (model: FormSectionCreateModel) : Promise => { + var dto = await this._formSectionRepo.create(model); + return dto; + }; + + update = async (id: string, model: FormSectionUpdateModel) : Promise => { + var dto = await this._formSectionRepo.update(id,model); + return dto; + }; + + getById = async (id: string) : Promise => { + var dto = await this._formSectionRepo.getById(id); + return dto; + }; + + delete = async (id: string) : Promise => { + var dto = await this._formSectionRepo.delete(id); + return dto; + }; + + getByTemplateId = async (id: string) : Promise => { + var dto = await this._formSectionRepo.getByTemplateId(id); + return dto; + }; + + public search=async(filters: FormSectionSearchFilters) : Promise => { + var dto = await this._formSectionRepo.search(filters); + return dto; + } + +} diff --git a/src/services/form.submission.service.ts b/src/services/form.submission.service.ts deleted file mode 100644 index 6572094..0000000 --- a/src/services/form.submission.service.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { FormStatus, PrismaClient } from "@prisma/client"; -import { PrismaClientInit } from "../startup/prisma.client.init"; -import { FormMapper } from "../mappers/form.submission.mapper" -import { FormSubmissionCreateModel, FormSubmissionSearchFilters, FormSubmissionUpdateModel } from "../domain.types/forms/form.submission.domain.types"; -import { uuid } from "../domain.types/miscellaneous/system.types"; -import { ErrorHandler } from "../common/error.handler"; -import { autoInjectable } from "tsyringe"; - -/////////////////////////////////////////////////////////////////////////////////////////////// -@autoInjectable() -export class FormService { - prisma: PrismaClient = null; - constructor() { - this.prisma = PrismaClientInit.instance().getPrismaInstance(); - } - - create = async (model: FormSubmissionCreateModel) => { - const response = await this.prisma.formSubmission.create({ - data: { - FormTemplate: { - connect: { id: model.FormTemplateId } - }, - UserId: model.UserId, - Status: model.Status, - ValidTill: model.ValidTill, - }, - include: { - FormTemplate: true, - } - }); - - return FormMapper.toDto(response); - }; - - update = async (id: string, model: FormSubmissionUpdateModel) => { - - const response = await this.prisma.formSubmission.update({ - where: { - id: id, - DeletedAt: null - }, - data: { ...model, - UpdatedAt: new Date() - }, - include: { - FormTemplate: true, - - }, - }); - return FormMapper.toDto(response); - }; - - getById = async (id: string) => { - const response = await this.prisma.formSubmission.findUnique({ - include: { - FormTemplate: true, - }, - where: { - id: id, - DeletedAt: null - }, - }); - return FormMapper.toDto(response); - }; - - delete = async (id: string) => { - const response = await this.prisma.formSubmission.update({ - where: { - id: id, - }, - data: { - DeletedAt: new Date(), - }, - include: { - FormTemplate: true, - } - }); - return FormMapper.toDto(response); - }; - - submit = async (id: uuid) => { - const response = await this.prisma.formSubmission.update({ - where: { - id: id, - DeletedAt: null - }, - data: { - SubmittedAt: new Date(), - Status: FormStatus.Submitted, - UpdatedAt: new Date() - }, - include: { - FormTemplate: true, - }, - }); - return FormMapper.toDto(response); - }; - - public search = async (filters: FormSubmissionSearchFilters) => { - try { - const search = this.getSearchModel(filters); - - const list = await this.prisma.formSubmission.findMany(search); - - const count = await this.prisma.formSubmission.count({ - where: search.where, - }); - - const searchResults = { - TotalCount: count, - RetrievedCount: list.length, - PageIndex: filters.PageIndex, - ItemsPerPage: filters.ItemsPerPage, - Order: filters.Order, - OrderedBy: filters.OrderBy, - Items: list.map((x) => FormMapper.toDto(x)), - }; - - return searchResults; - } catch (error) { - ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); - } - }; - - private getSearchModel = (filters: FormSubmissionSearchFilters) => { - const searchFilter = { - where: {} - } - - if (filters.FormTemplateId) { - searchFilter.where["FormTemplateId"] = filters.FormTemplateId - } - - if (filters.UserId) { - searchFilter.where["UserId"] = filters.UserId - } - - if (filters.Encrypted) { - searchFilter.where["Encrypted"] = filters.Encrypted - } - - if (filters.Status) { - searchFilter.where["Status"] = filters.Status - } - - if (filters.ValidTill) { - searchFilter.where["ValidTill"] = filters.ValidTill - } - - if (filters.SubmittedAt) { - searchFilter.where["SubmittedAt"] = filters.SubmittedAt - } - - let limit = 25; - if (filters.ItemsPerPage) { - limit = filters.ItemsPerPage; - } - - let order = 'asc'; - if (filters.Order === 'descending') { - order = 'desc'; - } - - let orderByColum = 'CreatedAt'; - if (filters.OrderBy) { - searchFilter['orderBy'] = { - [orderByColum]: order - } - } - - let offset = 0; - let pageIndex = 0; - if (filters.PageIndex) { - pageIndex = filters.PageIndex < 0 ? 0 : filters.PageIndex; - offset = pageIndex * limit; - } - searchFilter['take'] = limit; - searchFilter['skip'] = offset; - return searchFilter; - }; - -} diff --git a/src/services/form.submission/form.submission.service.ts b/src/services/form.submission/form.submission.service.ts new file mode 100644 index 0000000..fac6365 --- /dev/null +++ b/src/services/form.submission/form.submission.service.ts @@ -0,0 +1,65 @@ +import { FormStatus, PrismaClient } from "@prisma/client"; +import { PrismaClientInit } from "../../startup/prisma.client.init"; +import { FormMapper } from "../../database/sql/typeorm/mappers/form.submission.mapper" +import { FormSubmissionCreateModel, FormSubmissionDto, FormSubmissionSearchFilters, FormSubmissionUpdateModel } from "../../domain.types/forms/form.submission.domain.types"; +import { uuid } from "../../domain.types/miscellaneous/system.types"; +import { ErrorHandler } from "../../common/handlers/error.handler"; +import { inject, injectable } from "tsyringe"; +import { IFormSubmissionRepo } from "../../database/repository.interfaces/form.submission/form.submission.repo.interface"; + +/////////////////////////////////////////////////////////////////////////////////////////////// +@injectable() +export class FormService { + + constructor(@inject('IFormSubmissionRepo') private _formSubmissionRepo : IFormSubmissionRepo) { + + } + // const response = await this.prisma.formSubmission.create({ + // data: { + // FormTemplate: { + // connect: { id: model.FormTemplateId } + // }, + // UserId: model.UserId, + // Status: model.Status, + // ValidTill: model.ValidTill, + // }, + // include: { + // FormTemplate: true, + // } + // }); + + // return FormMapper.toDto(response); + // }; + + create = async (model: FormSubmissionCreateModel) : Promise => { + const dto=await this._formSubmissionRepo.create(model); + return dto; + }; + + update = async (id: string, model: FormSubmissionUpdateModel) : Promise=> { + const dto=await this._formSubmissionRepo.update(id,model); + return dto; + }; + + getById = async (id: string) : Promise => { + const dto=await this._formSubmissionRepo.getById(id); + return dto; + }; + + delete = async (id: string) : Promise=> { + const dto=await this._formSubmissionRepo.delete(id); + return dto; + }; + + + submit = async (id: uuid) : Promise=> { + const dto=await this._formSubmissionRepo.submit(id); + return dto; + }; + + public search = async (filters: FormSubmissionSearchFilters) : Promise=> { + const dto=await this._formSubmissionRepo.search(filters); + return dto; + }; + +} diff --git a/src/services/form.template.service.ts b/src/services/form.template.service.ts deleted file mode 100644 index c1df385..0000000 --- a/src/services/form.template.service.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { FormType, ItemsPerPage, Prisma, PrismaClient } from "@prisma/client"; -import { PrismaClientInit } from "../startup/prisma.client.init"; -import { - ExportFormTemplateDto, - FormTemplateCreateModel, - FormTemplateSearchFilters, - FormTemplateUpdateModel, - SectionDto, - SectionPreviewDto, - SubsectionDto, - TemplateDto, - TemplatePreviewDto -} from "../domain.types/forms/form.template.domain.types"; -import { FormTemplateMapper } from "../mappers/form.template.mapper"; -import { ErrorHandler } from "../common/error.handler"; -import { FormSectionMapper } from "../mappers/form.section.mapper"; -import { QuestionMapper } from "../mappers/question.mapper"; - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -export class FormTemplateService { - prisma: PrismaClient = null; - constructor() { - this.prisma = PrismaClientInit.instance().getPrismaInstance(); - } - - create = async (model: FormTemplateCreateModel) => { - const response = await this.prisma.formTemplate.create({ - data: { - User: { - connect: { id: model.OwnerUserId } - }, - Title: model.Title, - Description: model.Description, - CurrentVersion: model.CurrentVersion, - TenantId: model.TenantId, - Type: model.Type as FormType, - ItemsPerPage: model.ItemsPerPage as ItemsPerPage, - DisplayCode: model.DisplayCode, - // OwnerUserId: model.OwnerUserId, - RootSectionId: model.RootSectionId, - DefaultSectionNumbering: model.DefaultSectionNumbering, - // DeletedAt : null - }, - }); - return FormTemplateMapper.toDto(response); - }; - - update = async (id: string, model: FormTemplateUpdateModel) => { - const response = await this.prisma.formTemplate.update({ - where: { - id: id, - DeletedAt: null - }, - data: { - Title: model.Title, - Description: model.Description, - CurrentVersion: model.CurrentVersion, - TenantId: model.TenantId, - Type: model.Type as FormType, - ItemsPerPage: model.ItemsPerPage as ItemsPerPage, - UpdatedAt: new Date() - }, - }); - return FormTemplateMapper.toDto(response); - }; - - getById = async (id: string) => { - const response = await this.prisma.formTemplate.findUnique({ - where: { - id: id, - DeletedAt: null - }, - }); - return FormTemplateMapper.toDto(response); - }; - - getDetailsById = async (id: string) => { - const record = await this.prisma.formTemplate.findUnique({ - where: { - id: id, - DeletedAt: null - }, - include: { - FormSections: { - where: { - DeletedAt: null - }, - orderBy: { - Sequence: "asc" // Sort sections within each template - }, - include: { - Questions: { - where: { - DeletedAt: null - }, - orderBy: { - Sequence: 'asc' // Sort questions within each section - } - }, - ParentFormTemplate: true - } - } - } - }) - - const subsections = await this.mapSections(record.FormSections); - record.FormSections = subsections; - - return record; - }; - - mapSections = async (sections: any[]) => { - const sectionMap = new Map(); - - sections.forEach((section) => { - sectionMap.set(section.id, { ...section, Subsections: [] }); - }); - - const rootSections: any[] = []; - - sections.forEach((section) => { - if (section.ParentSectionId !== null) { - const parent = sectionMap.get(section.ParentSectionId); - if (parent) { - parent.Subsections.push(sectionMap.get(section.id)); - } - } else { - rootSections.push(sectionMap.get(section.id)); - } - }); - - return rootSections; - } - - - delete = async (id: string) => { - const response = await this.prisma.formTemplate.update({ - where: { - id: id, - DeletedAt: null - }, - data: { - DeletedAt: new Date(), - } - }); - return FormTemplateMapper.toDto(response); - }; - - submissions = async (id: string) => { - const response = await this.prisma.formTemplate.findMany({ - where: { - id: id, - DeletedAt: null - } - }); - return FormTemplateMapper.toArrayDto(response); - }; - - - public search = async (filters: FormTemplateSearchFilters) => { - try { - const search = this.getSearchModel(filters); - - const list = await this.prisma.formTemplate.findMany(search); - - const count = await this.prisma.formTemplate.count({ - where: search.where, - }); - - const searchResults = { - TotalCount: count, - RetrievedCount: list.length, - PageIndex: filters.PageIndex, - ItemsPerPage: filters.ItemsPerPage, - Order: filters.Order, - OrderedBy: filters.OrderBy, - Items: list.map((x) => FormTemplateMapper.toDto(x)), - }; - - return searchResults; - } catch (error) { - ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); - } - }; - - - private getSearchModel = (filters: FormTemplateSearchFilters) => { - // const where: Prisma.FormTemplateWhereInput = { - // DeletedAt: null - // }; - const searchFilter = { - where: { - DeletedAt: null, - } - } - - if (filters.id) { - searchFilter.where['id'] = filters.id - } - - if (filters.Title) { - searchFilter.where['Title'] = filters.Title - } - - if (filters.TenantId) { - searchFilter.where['TenantId'] = filters.TenantId - } - - if (filters.Description) { - searchFilter.where['Description'] = filters.Description - } - - if (filters.CurrentVersion) { - searchFilter.where['CurrentVersion'] = filters.CurrentVersion - } - - if (filters.Type) { - searchFilter.where['Type'] = filters.Type - } - - if (filters.DisplayCode) { - searchFilter.where['DisplayCode'] = filters.DisplayCode - } - - if (filters.OwnerUserId) { - searchFilter.where['OwnerUserId'] = filters.OwnerUserId - } - if (filters.RootSectionId) { - searchFilter.where['RootSectionId'] = filters.RootSectionId - } - if (filters.DefaultSectionNumbering) { - searchFilter.where['DefaultSectionNumbering'] = filters.DefaultSectionNumbering - } - - let limit = 25; - if (filters.ItemsPerPage) { - limit = filters.ItemsPerPage; - } - - let order = 'asc'; - if (filters.Order === 'descending') { - order = 'desc'; - } - - let orderByColum = 'CreatedAt'; - if (filters.OrderBy) { - searchFilter['orderBy'] = { - [orderByColum]: order - } - } - - let offset = 0; - - let pageIndex = 0; - - if (filters.PageIndex) { - pageIndex = filters.PageIndex < 0 ? 0 : filters.PageIndex; - offset = pageIndex * limit; - } - - searchFilter['take'] = limit; - searchFilter['skip'] = offset; - - return searchFilter; - }; - -} diff --git a/src/services/form.template/form.template.service.ts b/src/services/form.template/form.template.service.ts new file mode 100644 index 0000000..93549fe --- /dev/null +++ b/src/services/form.template/form.template.service.ts @@ -0,0 +1,331 @@ +import { FormType, ItemsPerPage, Prisma, PrismaClient } from "@prisma/client"; +import { PrismaClientInit } from "../../startup/prisma.client.init"; +import { + ExportFormTemplateDto, + FormTemplateCreateModel, + FormTemplateResponseDto, + FormTemplateSearchFilters, + FormTemplateUpdateModel, + SectionDto, + SectionPreviewDto, + SubsectionDto, + TemplateDto, + TemplatePreviewDto +} from "../../domain.types/forms/form.template.domain.types"; +import { FormTemplateMapper } from "../../database/sql/typeorm/mappers/form.template.mapper"; +import { ErrorHandler } from "../../common/handlers/error.handler"; +import { FormSectionMapper } from "../../database/sql/typeorm/mappers/form.section.mapper"; +import { QuestionMapper } from "../../database/sql/typeorm/mappers/question.mapper"; +import { IFormTemplateRepo } from "../../database/repository.interfaces/form.template/form.template.repo.interface"; +import { inject, injectable } from "tsyringe"; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@injectable() +export class FormTemplateService { + // prisma: PrismaClient = null; + constructor(@inject('IFormTemplateRepo') private _formTempRepo : IFormTemplateRepo) { + // this.prisma = PrismaClientInit.instance().getPrismaInstance(); + } + + // allFormTemplates = async () => { + // const response = await this.prisma.formTemplate.findMany({ + // where: { + // DeletedAt: null, + // } + // }); + // return FormTemplateMapper.toArrayDto(response); + // }; + + // create = async (model: FormTemplateCreateModel) => { + // const response = await this.prisma.formTemplate.create({ + // data: { + // User: { + // connect: { id: model.OwnerUserId } + // }, + // Title: model.Title, + // Description: model.Description, + // CurrentVersion: model.CurrentVersion, + // TenantCode: model.TenantCode, + // Type: model.Type as FormType, + // ItemsPerPage: model.ItemsPerPage as ItemsPerPage, + // DisplayCode: model.DisplayCode, + // // OwnerUserId: model.OwnerUserId, + // RootSectionId: model.RootSectionId, + // DefaultSectionNumbering: model.DefaultSectionNumbering, + // // DeletedAt : null + // }, + // }); + // return FormTemplateMapper.toDto(response); + // }; + + create = async (model: FormTemplateCreateModel) : Promise => { + const dto=await this._formTempRepo.create(model); + return dto; + }; + + update = async (id: string, model: FormTemplateUpdateModel) : Promise => { + const dto=await this._formTempRepo.update(id,model); + return dto; + }; + + getById = async (id: string) : Promise => { + const dto=await this._formTempRepo.getById(id); + return dto; + }; + + // getDetailsById = async (id: string) => { + // const record = await this.prisma.formTemplate.findUnique({ + // where: { + // id: id, + // DeletedAt: null + // }, + // include: { + // FormSections: { + // where: { + // DeletedAt: null + // }, + // orderBy: { + // CreatedAt: 'asc' // Sort sections within each template + // }, + // include: { + // Questions: { + // where: { + // DeletedAt: null + // }, + // orderBy: { + // CreatedAt: 'asc' // Sort questions within each section + // } + // }, + // ParentFormTemplate: true + // } + // } + // } + // }) + + // const subsections = await this.mapSections(record.FormSections); + // record.FormSections = subsections; + + // return record; + // // const template = await this.prisma.formTemplate.findUnique({ + // // where: { + // // id: id, + // // DeletedAt: null + // // }, + // // }); + // // const sections = await this.prisma.formSection.findMany({ + // // where: { + // // ParentFormTemplateId: id, + // // DeletedAt: null + // // }, + // // include: { + // // ParentFormTemplate: true + // // } + // // }); + // // const questions = await this.prisma.question.findMany({ + // // where: { + // // ParentTemplateId: id, + // // DeletedAt: null + // // }, + // // include: { + // // ParentFormTemplate: true, + // // ParentFormSection: true + // // } + // // }); + + // // const searchResult = { + // // Template: FormTemplateMapper.toDto(template), + // // Sections: sections.map((x) => FormSectionMapper.toDto(x)), + // // Questions: questions.map((x) => QuestionMapper.toDto(x)) + // // }; + // // return searchResult; + // }; + + getDetailsById = async (id: string) : Promise => { + const dto=await this._formTempRepo.getDetailsById(id); + return dto; + + }; + + readTemplateObjToExport = async (id: string): Promise => { + const dto=await this._formTempRepo.readTemplateObjToExport(id); + return dto; + } + + previewTemplate = async (id: string) : Promise => { + const dto=await this._formTempRepo.previewTemplate(id); + return dto; + }; + + + delete = async (id: string) : Promise => { + const dto=await this._formTempRepo.delete(id); + return dto; + }; + + submissions = async (id: string) : Promise => { + const dto=await this._formTempRepo.submissions(id); + return dto; + }; + + // protected addSortingAndPagination = ( + // search: Prisma.FormTemplateFindManyArgs, + // filters: FormTemplateSearchFilters + // ) => { + // // Sorting + // let orderByColumn: keyof typeof Prisma.FormTemplateScalarFieldEnum = 'CreatedAt'; + // if (filters.OrderBy) { + // orderByColumn = filters.OrderBy as keyof typeof Prisma.FormTemplateScalarFieldEnum; + // } + // let order: Prisma.SortOrder = 'asc'; + // if (filters.Order === 'descending') { + // order = 'desc'; + // } + + // search.orderBy = { + // [orderByColumn]: order, + // }; + + // // Pagination + // let limit = 25; + // if (filters.ItemsPerPage) { + // limit = filters.ItemsPerPage; + // } + // let offset = 0; + // let pageIndex = 1; + // if (filters.PageIndex) { + // pageIndex = filters.PageIndex < 1 ? 1 : filters.PageIndex; + // offset = (pageIndex - 1) * limit; + // } + + // search.take = limit; + // search.skip = offset; + + // // Update where clause + // const whereClause = this.getSearchModel(filters); + // if (Object.keys(whereClause).length > 0) { + // search.where = whereClause; + // } + + // return { search, pageIndex, limit, order, orderByColumn }; + // }; + + + public search = async (filters: FormTemplateSearchFilters) : Promise=> { + const dto=await this._formTempRepo.search(filters); + return dto; + }; + // public search = async (filters: FormTemplateSearchFilters) => { + // try { + // const { search: prismaSearch, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination({}, filters); + + // const list = await this.prisma.formTemplate.findMany({ + // where: prismaSearch.where, + // take: limit, + // skip: (pageIndex - 1) * limit, + // orderBy: { + // [orderByColumn]: order === 'desc' ? 'desc' : 'asc', + // }, + // }); + + // const count = await this.prisma.formTemplate.count({ + // where: prismaSearch.where, + // }); + + // const searchResults = { + // TotalCount: count, + // RetrievedCount: list.length, + // PageIndex: pageIndex, + // ItemsPerPage: limit, + // Order: order === 'desc' ? 'descending' : 'ascending', + // OrderedBy: orderByColumn, + // Items: list.map((x) => FormTemplateMapper.toDto(x)), + // }; + + // return searchResults; + // } catch (error) { + // ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + // } + // }; + + private getSearchModel = (filters: FormTemplateSearchFilters) => { + // const where: Prisma.FormTemplateWhereInput = { + // DeletedAt: null + // }; + const searchFilter = { + where: { + DeletedAt: null, + } + } + + if (filters.id) { + searchFilter.where['id'] = filters.id + } + + if (filters.Title) { + searchFilter.where['Title'] = filters.Title + } + + if (filters.TenantCode) { + searchFilter.where['TenantCode'] = filters.TenantCode + } + + if (filters.Description) { + searchFilter.where['Description'] = filters.Description + } + + if (filters.CurrentVersion) { + searchFilter.where['CurrentVersion'] = filters.CurrentVersion + } + + if (filters.Type) { + searchFilter.where['Type'] = filters.Type + } + + if (filters.DisplayCode) { + searchFilter.where['DisplayCode'] = filters.DisplayCode + } + + if (filters.OwnerUserId) { + searchFilter.where['OwnerUserId'] = filters.OwnerUserId + } + if (filters.RootSectionId) { + searchFilter.where['RootSectionId'] = filters.RootSectionId + } + if (filters.DefaultSectionNumbering) { + searchFilter.where['DefaultSectionNumbering'] = filters.DefaultSectionNumbering + } + + let limit = 25; + if (filters.ItemsPerPage) { + limit = filters.ItemsPerPage; + } + + let order = 'asc'; + if (filters.Order === 'descending') { + order = 'desc'; + } + + let orderByColum = 'CreatedAt'; + if (filters.OrderBy) { + searchFilter['orderBy'] = { + [orderByColum]: order + } + } + + let offset = 0; + + let pageIndex = 0; + + if (filters.PageIndex) { + pageIndex = filters.PageIndex < 0 ? 0 : filters.PageIndex; + offset = pageIndex * limit; + } + + searchFilter['take'] = limit; + searchFilter['skip'] = offset; + + return searchFilter; + }; + +} diff --git a/src/services/question.response.service.ts b/src/services/question.response.service.ts deleted file mode 100644 index 191eeed..0000000 --- a/src/services/question.response.service.ts +++ /dev/null @@ -1,350 +0,0 @@ -import { Prisma, PrismaClient, QueryResponseType } from "@prisma/client"; -import { PrismaClientInit } from "../startup/prisma.client.init"; -import { ResponseMapper } from "../mappers/question.response.mapper"; -import { QuestionResponseCreateModel, QuestionResponseResponseDto, QuestionResponseSearchFilters, QuestionResponseSearchResponseDto, QuestionResponseUpdateModel } from "../domain.types/forms/response.domain.types"; -import { QuestionMapper } from "../mappers/question.mapper"; -import { createObjectCsvWriter } from 'csv-writer'; -import PDFDocument from 'pdfkit'; -import * as fs from 'fs'; -import * as path from 'path'; -import { ErrorHandler } from "../common/error.handler"; - - -export class ResponseService { - prisma: PrismaClient = null; - private exportDirectory = path.join(__dirname, '../exports'); - - constructor() { - this.prisma = PrismaClientInit.instance().getPrismaInstance(); - if (!fs.existsSync(this.exportDirectory)) { - fs.mkdirSync(this.exportDirectory); - } - } - - // allResponses = async (): Promise => { - // const response = await this.prisma.questionResponse.findMany({ - // include: { - // FormSubmission: true, - // Question: true - // }, - // where: { - // DeletedAt: null - // } - // }); - // return ResponseMapper.toArrayDto(response); - // }; - - create = async (model: QuestionResponseCreateModel) => { - const response = await this.prisma.questionResponse.create({ - data: { - Question: { - connect: { id: model.QuestionId } - }, - FormSubmission: { - connect: { id: model.FormSubmissionId } - }, - ResponseType: model.ResponseType as QueryResponseType, - IntegerValue: model.IntegerValue, - FloatValue: model.FloatValue, - BooleanValue: model.BooleanValue, - DateTimeValue: model.DateTimeValue, - Url: model.Url, - FileResourceId: model.FileResourceId, - TextValue: model.TextValue, - SubmissionTimestamp: null, - LastSaveTimestamp: new Date(), - // DeletedAt : null, - }, - include: { - FormSubmission: true, - Question: true - } - }); - return ResponseMapper.toDto(response); - }; - - save = async (model: any) => { - const response = await this.prisma.questionResponse.create({ - data: { - Question: { - connect: { id: model.QuestionId } - }, - FormSubmission: { - connect: { id: model.FormSubmissionId } - }, - ResponseType: model.ResponseType as QueryResponseType, - FloatValue: model.FloatValue, - IntegerValue: model.IntegerValue, - BooleanValue: model.BooleanValue, - DateTimeValue: model.DateTimeValue, - Url: model.Url, - TextValue: model.TextValue, - SubmissionTimestamp: null, - LastSaveTimestamp: new Date(), - }, - include: { - FormSubmission: true, - Question: true - } - }); - return ResponseMapper.toDto(response); - }; - - update = async (id: string, model: QuestionResponseUpdateModel) => { - const record = await this.prisma.questionResponse.findUnique({ - where: { - id: id, - DeletedAt: null - } - }); - const response = await this.prisma.questionResponse.update({ - data: { - ResponseType: model.ResponseType as QueryResponseType ?? record.ResponseType, - IntegerValue: model.IntegerValue ?? record.IntegerValue, - FloatValue: model.FloatValue ?? record.FloatValue, - BooleanValue: model.BooleanValue ?? record.BooleanValue, - DateTimeValue: model.DateTimeValue ?? record.DateTimeValue, - Url: model.Url ?? record.Url, - FileResourceId: model.FileResourceId ?? record.FileResourceId, - TextValue: model.TextValue ?? record.TextValue, - LastSaveTimestamp: new Date(), - UpdatedAt: new Date() - }, - include: { - FormSubmission: true, - Question: true - }, - where: { - id: id, - DeletedAt: null - }, - }); - return ResponseMapper.toDto(response); - }; - - getById = async (id: string) => { - const response = await this.prisma.questionResponse.findUnique({ - where: { - id: id, - DeletedAt: null - }, - include: { - FormSubmission: true, - Question: true - } - }); - return ResponseMapper.toDto(response); - }; - - getQuestionById = async (id: string) => { - const response = await this.prisma.question.findUnique({ - where: { - id: id, - DeletedAt: null - }, - include: { - ParentFormSection: true, - ParentFormTemplate: true - } - }); - return QuestionMapper.toDto(response); - }; - - delete = async (id: string) => { - const response = await this.prisma.questionResponse.update({ - where: { - id: id, - }, - data: { - DeletedAt: new Date() - }, - include: { - FormSubmission: true, - Question: true - } - }); - return ResponseMapper.toDto(response); - }; - - getAll = async () => { - const responses = await this.prisma.questionResponse.findMany({ - where: { - DeletedAt: null, - }, - include: { - FormSubmission: true, - Question: true, - }, - }); - - return responses.map(ResponseMapper.toDto); - }; - - exportCsv = async (): Promise => { - const data = await this.getAll(); - const csvPath = path.join(__dirname, '../../../storage/data.csv'); - const csvWriter = createObjectCsvWriter({ - path: csvPath, - header: [ - { id: 'id', title: 'ID' }, - { id: 'FormSubmission.id', title: 'Submission ID' }, - { id: 'FormSubmission.TemplateId', title: 'Template ID' }, - { id: 'FormSubmission.FormUrl', title: 'Form URL' }, - { id: 'FormSubmission.UserId', title: 'User ID' }, - { id: 'FormSubmission.Status', title: 'Status' }, - { id: 'FormSubmission.SubmissionTimestamp', title: 'Submission Timestamp' }, - { id: 'FormSubmission.CreatedAt', title: 'Created At' }, - { id: 'Question.id', title: 'Question ID' }, - { id: 'Question.Title', title: 'Question Title' }, - { id: 'Question.Description', title: 'Question Description' }, - { id: 'Question.DisplayCode', title: 'Display Code' }, - { id: 'Question.ResponseType', title: 'Response Type' }, - { id: 'Question.Score', title: 'Score' }, - { id: 'Question.CorrectAnswer', title: 'Correct Answer' }, - { id: 'Question.Hint', title: 'Hint' }, - { id: 'Question.TemplateId', title: 'Question Template ID' }, - { id: 'Question.SectionId', title: 'Question Section ID' }, - { id: 'Question.CreatedAt', title: 'Question Created At' }, - { id: 'Question.UpdatedAt', title: 'Question Updated At' }, - { id: 'ResponseType', title: 'Query Response Type' }, - { id: 'IntegerValue', title: 'Integer Value' }, - { id: 'FloatValue', title: 'Float Value' }, - { id: 'BooleanValue', title: 'Boolean Value' }, - { id: 'DateTimeValue', title: 'Date Time Value' }, - { id: 'Url', title: 'URL' }, - { id: 'FileResourceId', title: 'File Resource ID' }, - { id: 'TextValue', title: 'Text Value' }, - { id: 'SubmissionTimestamp', title: 'Submission Timestamp' }, - { id: 'LastSaveTimestamp', title: 'Last Save Timestamp' } - ] - }); - await csvWriter.writeRecords(data); - return csvPath; - }; - - exportPdf = async (): Promise => { - const data = await this.getAll(); - const pdfPath = path.join(__dirname, '../../../storage/data.pdf'); - const doc = new PDFDocument(); - doc.pipe(fs.createWriteStream(pdfPath)); - - doc.text('ID\tSubmission ID\tTemplate ID\tForm URL\tUser ID\tStatus\tSubmission Timestamp\tCreated At\t' + - 'Question ID\tQuestion Title\tQuestion Description\tDisplay Code\tResponse Type\tScore\t' + - 'Correct Answer\tHint\tQuestion Template ID\tQuestion Section ID\tQuestion Created At\t' + - 'Question Updated At\tQuery Response Type\tInteger Value\tFloat Value\tBoolean Value\t' + - 'Date Time Value\tURL\tFile Resource ID\tText Value\tSubmission Timestamp\tLast Save Timestamp'); - - data.forEach(row => { - doc.text(`${row.id}\t${row.FormSubmission.id}\t${row.FormSubmission.TemplateId}\t${row.FormSubmission.FormUrl}\t${row.FormSubmission.UserId}\t${row.FormSubmission.Status}\t${row.FormSubmission.SubmissionTimestamp}\t${row.FormSubmission.CreatedAt}\t` + - `${row.Question.id}\t${row.Question.Title}\t${row.Question.Description}\t${row.Question.DisplayCode}\t${row.Question.ResponseType}\t${row.Question.Score}\t${row.Question.CorrectAnswer}\t${row.Question.Hint}\t` + - `${row.Question.TemplateId}\t${row.Question.SectionId}\t${row.Question.CreatedAt}\t${row.Question.UpdatedAt}\t${row.ResponseType}\t${row.IntegerValue}\t${row.FloatValue}\t${row.BooleanValue}\t` + - `${row.DateTimeValue}\t${row.Url}\t${row.FileResourceId}\t${row.TextValue}\t${row.SubmissionTimestamp}\t${row.LastSaveTimestamp}`); - }); - - doc.end(); - return pdfPath; - }; - - - public search = async (filters: QuestionResponseSearchFilters) => { - try { - // const { search: prismaSearch, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination({}, filters); - const search = this.getSearchModel(filters); - const list = await this.prisma.questionResponse.findMany(search); - - const count = await this.prisma.questionResponse.count({ - where: search.where, - }); - - const searchResults = { - TotalCount: count, - RetrievedCount: list.length, - PageIndex: filters.PageIndex, - ItemsPerPage: filters.ItemsPerPage, - Order: filters.Order, - OrderedBy: filters.OrderBy, - Items: list.map((x) => ResponseMapper.toDto(x)), - }; - - return searchResults; - } catch (error) { - ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); - } - }; - - - - - - private getSearchModel = (filters: QuestionResponseSearchFilters) => { - const searchFilter = { - where : {}, - include: { - FormSubmission: true, - Question: true, - }, - }; - - if (filters.FormSubmissionId) { - searchFilter.where['FormSubmissionId'] = filters.FormSubmissionId; - } - - if (filters.QuestionId) { - searchFilter.where['QuestionId'] = filters.QuestionId; - } - - if (filters.ResponseType) { - searchFilter.where['ResponseType'] = filters.ResponseType; - } - - if (filters.IntegerValue) { - searchFilter.where['IntegerValue'] = filters.IntegerValue; - } - - if (filters.FloatValue) { - searchFilter.where['FloatValue'] = filters.FloatValue; - } - - if (filters.BooleanValue) { - searchFilter.where['BooleanValue'] = filters.BooleanValue; - } - if (filters.Url) { - searchFilter.where['Url'] = filters.Url; - } - if (filters.FileResourceId) { - searchFilter.where['FileResourceId'] = filters.FileResourceId; - } - if (filters.TextValue) { - searchFilter.where['TextValue'] = filters.TextValue; - } - - let limit = 25; - if (filters.ItemsPerPage) { - limit = filters.ItemsPerPage; - } - - let order = 'asc'; - if (filters.Order === 'descending') { - order = 'desc'; - } - - let orderByColum = 'CreatedAt'; - if (filters.OrderBy) { - searchFilter['orderBy'] = { - [orderByColum]: order - } - } - - let offset = 0; - let pageIndex = 0; - if (filters.PageIndex) { - pageIndex = filters.PageIndex < 0 ? 0 : filters.PageIndex; - offset = pageIndex * limit; - } - searchFilter['take'] = limit; - searchFilter['skip'] = offset; - return searchFilter; - - }; - -} diff --git a/src/services/question.response/question.response.service.ts b/src/services/question.response/question.response.service.ts new file mode 100644 index 0000000..da370a7 --- /dev/null +++ b/src/services/question.response/question.response.service.ts @@ -0,0 +1,112 @@ +import { QuestionResponseCreateModel, QuestionResponseResponseDto, QuestionResponseSearchFilters, QuestionResponseUpdateModel } from "../../domain.types/forms/response.domain.types"; +import { inject, injectable } from "tsyringe"; +import { IResponseRepo } from "../../database/repository.interfaces/question.response/question.response.repo.interface"; +import { QuestionResponseDto } from "../../domain.types/forms/question.domain.types"; + +@injectable() +export class ResponseService { + // prisma: PrismaClient = null; + // private exportDirectory = path.join(__dirname, '../exports'); + + constructor(@inject('IResponseRepo') private _respRepo : IResponseRepo) { + // this.prisma = PrismaClientInit.instance().getPrismaInstance(); + // if (!fs.existsSync(this.exportDirectory)) { + // fs.mkdirSync(this.exportDirectory); + // } + } + + // allResponses = async (): Promise => { + // const response = await this.prisma.questionResponse.findMany({ + // include: { + // FormSubmission: true, + // Question: true + // }, + // where: { + // DeletedAt: null + // } + // }); + // return ResponseMapper.toArrayDto(response); + // }; + + // create = async (model: QuestionResponseCreateModel) => { + // const response = await this.prisma.questionResponse.create({ + // data: { + // Question: { + // connect: { id: model.QuestionId } + // }, + // FormSubmission: { + // connect: { id: model.FormSubmissionId } + // }, + // ResponseType: model.ResponseType as QueryResponseType, + // IntegerValue: model.IntegerValue, + // FloatValue: model.FloatValue, + // BooleanValue: model.BooleanValue, + // DateTimeValue: model.DateTimeValue, + // Url: model.Url, + // FileResourceId: model.FileResourceId, + // TextValue: model.TextValue, + // SubmissionTimestamp: null, + // LastSaveTimestamp: new Date(), + // // DeletedAt : null, + // }, + // include: { + // FormSubmission: true, + // Question: true + // } + // }); + // return ResponseMapper.toDto(response); + // }; + + create = async (model: QuestionResponseCreateModel) : Promise => { + const dto=await this._respRepo.create(model); + return dto; + }; + + save = async (model: any) => { + const dto=await this._respRepo.save(model); + return dto; + }; + + update = async (id: string, model: QuestionResponseUpdateModel) : Promise => { + const dto=await this._respRepo.update(id,model); + return dto; + }; + + getById = async (id: string) : Promise => { + const dto=await this._respRepo.getById(id); + return dto; + }; + + getQuestionById = async (id: string) : Promise => { + const dto=await this._respRepo.getQuestionById(id); + return dto; + }; + + delete = async (id: string) : Promise => { + const dto=await this._respRepo.delete(id); + return dto; + }; + + getAll = async () => { + const dto=await this._respRepo.getAll(); + return dto; + }; + + exportCsv = async (): Promise => { + + const dto=await this._respRepo.exportCsv(); + return dto; + }; + + exportPdf = async (): Promise => { + const dto=await this._respRepo.exportPdf(); + return dto; + }; + + + public search = async (filters: QuestionResponseSearchFilters) => { + const dto=await this._respRepo.search(filters); + return dto; + }; + +} diff --git a/src/services/question.service.ts b/src/services/question.service.ts deleted file mode 100644 index 9d9c5aa..0000000 --- a/src/services/question.service.ts +++ /dev/null @@ -1,412 +0,0 @@ -import { Prisma, PrismaClient, QueryResponseType } from "@prisma/client"; -import { PrismaClientInit } from "../startup/prisma.client.init"; -import { QuestionMapper } from "../mappers/question.mapper"; -import { QuestionCreateModel, QuestionOption, QuestionSearchFilters, QuestionSearchResponseDto, QuestionUpdateModel } from "../domain.types/forms/question.domain.types"; -import { ErrorHandler } from "../common/error.handler"; - - -export class QuestionService { - prisma: PrismaClient = null; - constructor() { - this.prisma = PrismaClientInit.instance().getPrismaInstance(); - } - - // allQuestions = async (): Promise => { - // const response = await this.prisma.question.findMany({ - // include: { - // ParentFormTemplate: true, - // ParentFormSection: true - // }, - // where: { - // DeletedAt: null - // } - // }); - // return QuestionMapper.toArrayDto(response); - // }; - - // create = async (model: QuestionCreateModel) => { - - // const jsonData: Prisma.JsonValue = { - // Sequence: model.Options.Sequence, - // Option: model.Options.Data, - // ImageUrl: model.Options.ImageUrl, - // } as Prisma.JsonObject; - - // const response = await this.prisma.question.create({ - // data: { - // ParentFormTemplate: { - // connect: { id: model.ParentTemplateId } - // }, - // ParentFormSection: { - // connect: { id: model.ParentSectionId } - // }, - // Title: model.Title, - // Description: model.Description, - // DisplayCode: model.DisplayCode, - // ResponseType: model.ResponseType as QueryResponseType, - // Score: model.Score, - // CorrectAnswer: model.CorrectAnswer, - // Hint: model.Hint, - // Sequence: model.Sequence, - // Options: jsonData, - // QuestionImageUrl: model.QuestionImageUrl, - // RangeMax: model.RangeMax, - // RangeMin: model.RangeMin, - // CreatedAt: new Date(), - // // UpdatedAt: new Date(), - // // DeletedAt: null, - // }, - // include: { - // ParentFormTemplate: true, - // ParentFormSection: true - // } - // }); - // return QuestionMapper.toDto(response); - - // }; - - create = async (model: QuestionCreateModel) => { - let jsonData: Prisma.JsonValue | undefined; - - // Map model.Options to the appropriate structure for JSON storage - if (model.Options && model.Options.length > 0) { - jsonData = model.Options.map((option) => ({ - Text: option.Text, - Sequence: option.Sequence, - ImageUrl: option.ImageUrl, - })) as Prisma.JsonArray; - } - - // Create a new question in the database - const response = await this.prisma.question.create({ - data: { - ParentFormTemplate: { - connect: { id: model.ParentTemplateId }, - }, - ParentFormSection: { - connect: { id: model.ParentSectionId }, - }, - Title: model.Title, - Description: model.Description, - DisplayCode: model.DisplayCode, - ResponseType: model.ResponseType as QueryResponseType, - Score: model.Score, - CorrectAnswer: model.CorrectAnswer, - IsRequired: model.IsRequired, - Hint: model.Hint, - Sequence: model.Sequence, - Options: jsonData, // Only assign if jsonData is defined - QuestionImageUrl: model.QuestionImageUrl, - RangeMax: model.RangeMax, - RangeMin: model.RangeMin, - CreatedAt: new Date(), - // UpdatedAt: new Date(), // Uncomment and modify as needed - // DeletedAt: null, // Uncomment and modify as needed - }, - include: { - ParentFormTemplate: true, - ParentFormSection: true, - }, - }); - - // Convert response to DTO - return QuestionMapper.toDto(response); - }; - - update = async (id: string, model: QuestionUpdateModel) => { - let jsonData: Prisma.JsonValue | undefined; - - // Map model.Options to the appropriate structure for JSON storage - if (model.Options && model.Options.length > 0) { - jsonData = model.Options.map((option) => ({ - Text: option.Text, - Sequence: option.Sequence, - ImageUrl: option.ImageUrl, - })) as Prisma.JsonArray; - } - const response = await this.prisma.question.update({ - data: { - Title: model.Title, - Description: model.Description, - // DisplayCode: model.DisplayCode, - // ResponseType: model.ResponseType as QueryResponseType, - Score: model.Score, - CorrectAnswer: model.CorrectAnswer, - IsRequired: model.IsRequired, - Hint: model.Hint, - Options: jsonData, // Only assign if jsonData is defined - QuestionImageUrl: model.QuestionImageUrl, - RangeMax: model.RangeMax, - RangeMin: model.RangeMin, - UpdatedAt: new Date() - }, - include: { - ParentFormSection: true, - ParentFormTemplate: true - }, - where: { - id: id, - DeletedAt: null - } - }); - return QuestionMapper.toDto(response); - }; - - async updateSequence(id: string, data: QuestionUpdateModel) { - return this.prisma.question.update({ - where: { id }, - data:{ - Sequence: data.Sequence - } - }); - } - - async decrementSequenceInRange(start: number, end: number, parentSectionId: string) { - return this.prisma.question.updateMany({ - where: { - Sequence: { - gt: start - 1, - lte: end - }, - ParentSectionId: parentSectionId - }, - data: { - Sequence: { - decrement: 1 - } - } - }); - } - - async incrementSequenceInRange(start: number, end: number, parentSectionId: string) { - return this.prisma.question.updateMany({ - where: { - Sequence: { - gte: start, - lt: end + 1 - }, - ParentSectionId: parentSectionId - }, - data: { - Sequence: { - increment: 1 - } - } - }); - } - - - getById = async (id: string) => { - const response = await this.prisma.question.findUnique({ - where: { - id: id, - DeletedAt: null - }, - include: { - ParentFormSection: true, - ParentFormTemplate: true - }, - }); - return QuestionMapper.toDto(response); - }; - - getByTemplateId = async (id: string) => { - const response = await this.prisma.question.findMany({ - where: { - ParentTemplateId: id, - DeletedAt: null - }, - include: { - ParentFormSection: true, - ParentFormTemplate: true - }, - }); - return QuestionMapper.toArrayDto(response); - }; - - delete = async (id: string) => { - const response = await this.prisma.question.update({ - where: { - id: id, - DeletedAt: null - }, - data: { - DeletedAt: new Date() - }, - include: { - ParentFormSection: true, - ParentFormTemplate: true - }, - }); - return QuestionMapper.toDto(response); - }; - - protected addSortingAndPagination = ( - search: Prisma.QuestionFindManyArgs, - filters: QuestionSearchFilters - ) => { - // Sorting - let orderByColumn: keyof typeof Prisma.QuestionScalarFieldEnum = 'CreatedAt'; - if (filters.OrderBy) { - orderByColumn = filters.OrderBy as keyof typeof Prisma.QuestionScalarFieldEnum; - } - let order: Prisma.SortOrder = 'asc'; - if (filters.Order === 'descending') { - order = 'desc'; - } - - search.orderBy = { - [orderByColumn]: order, - }; - - // Pagination - let limit = 25; - if (filters.ItemsPerPage) { - limit = filters.ItemsPerPage; - } - let offset = 0; - let pageIndex = 1; - if (filters.PageIndex) { - pageIndex = filters.PageIndex < 1 ? 1 : filters.PageIndex; - offset = (pageIndex - 1) * limit; - } - - search.take = limit; - search.skip = offset; - - // Update where clause - const whereClause = this.getSearchModel(filters); - if (Object.keys(whereClause).length > 0) { - search.where = whereClause; - } - - return { search, pageIndex, limit, order, orderByColumn }; - }; - - public search = async (filters: QuestionSearchFilters) => { - try { - const { search: prismaSearch, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination({}, filters); - - const list = await this.prisma.question.findMany({ - where: prismaSearch.where, - include: { - ParentFormSection: true, - ParentFormTemplate: true - }, - take: limit, - skip: (pageIndex - 1) * limit, - orderBy: { - [orderByColumn]: order === 'desc' ? 'desc' : 'asc', - }, - }); - - const count = await this.prisma.question.count({ - where: prismaSearch.where, - }); - - const searchResults = { - TotalCount: count, - RetrievedCount: list.length, - PageIndex: pageIndex, - ItemsPerPage: limit, - Order: order === 'desc' ? 'descending' : 'ascending', - OrderedBy: orderByColumn, - Items: list.map((x) => QuestionMapper.toDto(x)), - }; - - return searchResults; - } catch (error) { - ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); - } - }; - - private getSearchModel = (filters: QuestionSearchFilters): Prisma.QuestionWhereInput => { - const where: Prisma.QuestionWhereInput = { DeletedAt: null }; - - if (filters.id) { - where.id = { - equals: filters.id, - }; - } - if (filters.parentTemplateId) { - where.ParentTemplateId = { - equals: filters.parentTemplateId, - }; - } - if (filters.parentSectionId) { - where.ParentSectionId = { - equals: filters.parentSectionId, - }; - } - - if (filters.title) { - where.Title = { - equals: filters.title, - }; - } - - if (filters.description) { - where.Description = { - equals: filters.description, - }; - } - - if (filters.displayCode) { - where.DisplayCode = { - equals: filters.displayCode, - }; - } - - if (filters.responseType) { - where.ResponseType = { - equals: filters.responseType, - }; - } - - if (filters.score) { - where.Score = { - equals: filters.score, - }; - } - - if (filters.isRequired) { - where.IsRequired = { - equals: filters.isRequired, - }; - } - - if (filters.hint) { - where.Hint = { - equals: filters.hint, - }; - } - // if (filters.options) { - // where.Options = { - // equals: filters.options, - // }; - // } - if (filters.questionImageUrl) { - where.QuestionImageUrl = { - equals: filters.questionImageUrl, - }; - } - if (filters.rangeMin) { - where.RangeMin = { - equals: filters.rangeMin, - }; - } - if (filters.rangeMax) { - where.RangeMax = { - equals: filters.rangeMax, - }; - } - if (filters.correctAnswer) { - where.CorrectAnswer = { - equals: filters.correctAnswer, - }; - } - - return where; - }; - -} diff --git a/src/services/question/question.service.ts b/src/services/question/question.service.ts new file mode 100644 index 0000000..ba304cd --- /dev/null +++ b/src/services/question/question.service.ts @@ -0,0 +1,186 @@ +import { QuestionCreateModel, QuestionOption, QuestionResponseDto, QuestionSearchFilters, QuestionSearchResponseDto, QuestionUpdateModel } from "../../domain.types/forms/question.domain.types"; +import { ErrorHandler } from "../../common/handlers/error.handler"; +import { inject, injectable } from "tsyringe"; +import { IQuestionRepo } from "../../database/repository.interfaces/question/question.repo.interface"; + + +@injectable() +export class QuestionService { + + constructor(@inject('IQuestionRepo') private _questRepo : IQuestionRepo) { + } + + // allQuestions = async (): Promise => { + // const response = await this.prisma.question.findMany({ + // include: { + // ParentFormTemplate: true, + // ParentFormSection: true + // }, + // where: { + // DeletedAt: null + // } + // }); + // return QuestionMapper.toArrayDto(response); + // }; + + // create = async (model: QuestionCreateModel) => { + + // const jsonData: Prisma.JsonValue = { + // Sequence: model.Options.Sequence, + // Option: model.Options.Data, + // ImageUrl: model.Options.ImageUrl, + // } as Prisma.JsonObject; + + // const response = await this.prisma.question.create({ + // data: { + // ParentFormTemplate: { + // connect: { id: model.ParentTemplateId } + // }, + // ParentFormSection: { + // connect: { id: model.ParentSectionId } + // }, + // Title: model.Title, + // Description: model.Description, + // DisplayCode: model.DisplayCode, + // ResponseType: model.ResponseType as QueryResponseType, + // Score: model.Score, + // CorrectAnswer: model.CorrectAnswer, + // Hint: model.Hint, + // Sequence: model.Sequence, + // Options: jsonData, + // QuestionImageUrl: model.QuestionImageUrl, + // RangeMax: model.RangeMax, + // RangeMin: model.RangeMin, + // CreatedAt: new Date(), + // // UpdatedAt: new Date(), + // // DeletedAt: null, + // }, + // include: { + // ParentFormTemplate: true, + // ParentFormSection: true + // } + // }); + // return QuestionMapper.toDto(response); + + // }; + + create = async (model: QuestionCreateModel) : Promise => { + const dto=await this._questRepo.create(model); + return dto; + }; + + update = async (id: string, model: QuestionUpdateModel) : Promise => { + const dto=await this._questRepo.update(id,model); + return dto; + }; + + getById = async (id: string) : Promise => { + const dto=await this._questRepo.getById(id); + return dto; + }; + + getByTemplateId = async (id: string) : Promise => { + const dto=await this._questRepo.getByTemplateId(id); + return dto; + }; + + delete = async (id: string) : Promise => { + const dto=await this._questRepo.delete(id); + return true; + }; + + public search = async (filters: QuestionSearchFilters) : Promise=> { + const dto=await this._questRepo.search(filters); + return dto; + }; + + // private getSearchModel = (filters: QuestionSearchFilters): Prisma.QuestionWhereInput => { + // const where: Prisma.QuestionWhereInput = { DeletedAt: null }; + + // if (filters.id) { + // where.id = { + // equals: filters.id, + // }; + // } + // if (filters.parentTemplateId) { + // where.ParentTemplateId = { + // equals: filters.parentTemplateId, + // }; + // } + // if (filters.parentSectionId) { + // where.ParentSectionId = { + // equals: filters.parentSectionId, + // }; + // } + + // if (filters.title) { + // where.Title = { + // equals: filters.title, + // }; + // } + + // if (filters.description) { + // where.Description = { + // equals: filters.description, + // }; + // } + + // if (filters.displayCode) { + // where.DisplayCode = { + // equals: filters.displayCode, + // }; + // } + + // if (filters.responseType) { + // where.ResponseType = { + // equals: filters.responseType, + // }; + // } + + // if (filters.score) { + // where.Score = { + // equals: filters.score, + // }; + // } + + // if (filters.isRequired) { + // where.IsRequired = { + // equals: filters.isRequired, + // }; + // } + + // if (filters.hint) { + // where.Hint = { + // equals: filters.hint, + // }; + // } + // // if (filters.options) { + // // where.Options = { + // // equals: filters.options, + // // }; + // // } + // if (filters.questionImageUrl) { + // where.QuestionImageUrl = { + // equals: filters.questionImageUrl, + // }; + // } + // if (filters.rangeMin) { + // where.RangeMin = { + // equals: filters.rangeMin, + // }; + // } + // if (filters.rangeMax) { + // where.RangeMax = { + // equals: filters.rangeMax, + // }; + // } + // if (filters.correctAnswer) { + // where.CorrectAnswer = { + // equals: filters.correctAnswer, + // }; + // } + + // return where; + // }; + +} diff --git a/src/services/user.login.session.service.ts b/src/services/user.login.session.service.ts index fae49ae..05c7585 100644 --- a/src/services/user.login.session.service.ts +++ b/src/services/user.login.session.service.ts @@ -1,6 +1,6 @@ import { PrismaClient } from "@prisma/client"; import { PrismaClientInit } from "../startup/prisma.client.init"; -import { UserLoginSessionMapper } from "../mappers/user.login.mapper"; +import { UserLoginSessionMapper } from "../database/sql/typeorm/mappers/user.login.mapper"; import { UserLoginSessionCreateModel, UserLoginSessionUpdateModel } from "../domain.types/forms/user.login.session.domain.types"; diff --git a/src/services/user.service.ts b/src/services/user.service.ts deleted file mode 100644 index f244c72..0000000 --- a/src/services/user.service.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { Prisma, PrismaClient } from "@prisma/client"; -import { PrismaClientInit } from "../startup/prisma.client.init"; -import { UserMapper } from "../mappers/user.mapper"; -import { UserCreateModel, UserSearchFilters, UserSearchResponseDto, UserUpdateModel } from "../domain.types/forms/user.domain.types"; -import { ErrorHandler } from "../common/error.handler"; - - - -export class UserService { - prisma: PrismaClient = null; - constructor() { - this.prisma = PrismaClientInit.instance().getPrismaInstance(); - } - - allUsers = async () => { - const response = await this.prisma.user.findMany({ - where: { - DeletedAt: null - } - }); - return UserMapper.toArrayDto(response); - }; - - create = async (model: UserCreateModel) => { - const response = await this.prisma.user.create({ - data: { - FirstName: model.FirstName, - LastName: model.LastName, - CountryCode: model.CountryCode, - Phone: model.Phone, - Email: model.Email, - Username: model.Username, - Password: model.Password, - }, - }); - return UserMapper.toDto(response); - }; - - update = async (id: string, model: UserUpdateModel) => { - const response = await this.prisma.user.update({ - where: { - id: id, - DeletedAt: null - }, - data: { - FirstName: model.FirstName, - LastName: model.LastName, - CountryCode: model.CountryCode, - Phone: model.Phone, - Email: model.Email, - Username: model.Username, - Password: model.Password, - UpdatedAt: new Date() - }, - }); - return UserMapper.toDto(response); - }; - - getById = async (id: string) => { - const response = await this.prisma.user.findUnique({ - where: { - id: id, - DeletedAt: null - }, - }); - return UserMapper.toDto(response); - }; - - delete = async (id: string) => { - const response = await this.prisma.user.update({ - where: { - id: id, - DeletedAt: null - }, - data: { - DeletedAt: new Date(), - } - }); - return UserMapper.toDto(response); - }; - - protected addSortingAndPagination = ( - search: Prisma.UserFindManyArgs, - filters: UserSearchFilters - ) => { - // Sorting - let orderByColumn: keyof typeof Prisma.UserScalarFieldEnum = 'CreatedAt'; - if (filters.OrderBy) { - orderByColumn = filters.OrderBy as keyof typeof Prisma.UserScalarFieldEnum; - } - let order: Prisma.SortOrder = 'asc'; - if (filters.Order === 'descending') { - order = 'desc'; - } - - search.orderBy = { - [orderByColumn]: order, - }; - - // Pagination - let limit = 25; - if (filters.ItemsPerPage) { - limit = filters.ItemsPerPage; - } - let offset = 0; - let pageIndex = 1; - if (filters.PageIndex) { - pageIndex = filters.PageIndex < 1 ? 1 : filters.PageIndex; - offset = (pageIndex - 1) * limit; - } - - search.take = limit; - search.skip = offset; - - // Update where clause - const whereClause = this.getSearchModel(filters); - if (Object.keys(whereClause).length > 0) { - search.where = whereClause; - } - - return { search, pageIndex, limit, order, orderByColumn }; - }; - - - - - - - - public search = async (filters: UserSearchFilters) => { - try { - const { search: prismaSearch, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination({}, filters); - - const list = await this.prisma.user.findMany({ - where: prismaSearch.where, - take: limit, - skip: (pageIndex - 1) * limit, - orderBy: { - [orderByColumn]: order === 'desc' ? 'desc' : 'asc', - }, - }); - - const count = await this.prisma.user.count({ - where: prismaSearch.where, - }); - - const searchResults = { - TotalCount: count, - RetrievedCount: list.length, - PageIndex: pageIndex, - ItemsPerPage: limit, - Order: order === 'desc' ? 'descending' : 'ascending', - OrderedBy: orderByColumn, - Items: list.map((x) => UserMapper.toDto(x)), - }; - - return searchResults; - } catch (error) { - ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); - } - }; - - - - - - private getSearchModel = (filters: UserSearchFilters): Prisma.UserWhereInput => { - const where: Prisma.UserWhereInput = { DeletedAt: null }; - - if (filters.firstName) { - where.FirstName = { - equals: filters.firstName, - }; - } - - if (filters.lastName) { - where.LastName = { - equals: filters.lastName, - }; - } - - if (filters.countryCode) { - where.CountryCode = { - equals: filters.countryCode, - }; - } - - if (filters.email) { - where.Email = { - equals: filters.email, - }; - } - - if (filters.username) { - where.Username = { - equals: filters.username, - }; - } - - if (filters.password) { - where.Password = { - equals: filters.password, - }; - } - - return where; - }; - - -} diff --git a/src/services/user/user.service.ts b/src/services/user/user.service.ts new file mode 100644 index 0000000..cfb467f --- /dev/null +++ b/src/services/user/user.service.ts @@ -0,0 +1,49 @@ +import { Prisma, PrismaClient } from "@prisma/client"; +import { PrismaClientInit } from "../../startup/prisma.client.init"; +import { UserMapper } from "../../database/sql/typeorm/mappers/user.mapper"; +import { UserCreateModel, UserSearchFilters, UserSearchResponseDto, UserUpdateModel } from "../../domain.types/forms/user.domain.types"; +import { ErrorHandler } from "../../common/handlers/error.handler"; +import { IUserRepo } from "../../database/repository.interfaces/user/user.repo.interface"; +import { inject, injectable } from "tsyringe"; + + +@injectable() +export class UserService { + // prisma: PrismaClient = null; + constructor(@inject('IUserRepo') private _userRepo : IUserRepo) { + // this.prisma = PrismaClientInit.instance().getPrismaInstance(); + } + + allUsers = async () => { + const dto=await this._userRepo.allUsers(); + return dto; + }; + + create = async (model: UserCreateModel) => { + const dto=await this._userRepo.create(model); + return dto; + }; + + update = async (id: string, model: UserUpdateModel) => { + const dto=await this._userRepo.update(id,model); + return dto; + }; + + getById = async (id: string) => { + const dto=await this._userRepo.getById(id); + return dto; + }; + + delete = async (id: string) => { + const dto=await this._userRepo.delete(id); + return dto; + }; + + + public search = async (filters: UserSearchFilters) => { + const dto=await this._userRepo.search(filters); + return dto; + }; + + +} diff --git a/src/startup/injector.ts b/src/startup/injector.ts new file mode 100644 index 0000000..d641224 --- /dev/null +++ b/src/startup/injector.ts @@ -0,0 +1,23 @@ +import 'reflect-metadata'; +import { ModuleInjector } from '../modules/module.injector'; +import { DependencyContainer, container } from 'tsyringe'; +// import { AuthInjector } from '../auth/auth.injector'; +import { DatabaseInjector } from '../database/database.injector'; + +////////////////////////////////////////////////////////////////////////////////////////////////// + +export class Injector { + + private static _container: DependencyContainer = container; + + public static get Container() { + return Injector._container; + } + + static registerInjections() { + // AuthInjector.registerInjections(Injector.Container); + DatabaseInjector.registerInjections(Injector.Container); + ModuleInjector.registerInjections(Injector.Container); + } + +} diff --git a/src/startup/loader.ts b/src/startup/loader.ts new file mode 100644 index 0000000..1d7f18b --- /dev/null +++ b/src/startup/loader.ts @@ -0,0 +1,77 @@ +import 'reflect-metadata'; +// import { CareplanHandler } from '../modules/careplan/careplan.handler'; +import { container } from 'tsyringe'; +import { Logger } from '../common/logger'; +// import { MessagingService } from '../modules/communication/messaging.service/messaging.service'; +// import { NotificationService } from '../modules/communication/notification.service/notification.service'; +// import { StorageService } from '../modules/ehr/services/storage.service'; +import { Injector } from './injector'; +// import { Scheduler } from './scheduler'; +// import { Seeder } from './seeder'; +import { ConfigurationManager } from '../config/configuration.manager'; + +////////////////////////////////////////////////////////////////////////////////////////////////// + +export class Loader { + + // private static _seeder: Seeder = null; + + // private static _scheduler: Scheduler = Scheduler.instance(); + + // private static _messagingService: MessagingService = null; + + // private static _notificationService: NotificationService = null; + + // private static _ehrStore: StorageService = null; + + // public static get seeder() { + // return Loader._seeder; + // } + + // public static get scheduler() { + // return Loader._scheduler; + // } + + // public static get storage() { + // return Loader._ehrStore; + // } + + // public static get messagingService() { + // return Loader._messagingService; + // } + + // public static get notificationService() { + // return Loader._notificationService; + // } + + public static init = async (): Promise => { + try { + + //Register injections here... + Injector.registerInjections(); + + // Loader._seeder = container.resolve(Seeder); + + const ehrEnabled = ConfigurationManager.EhrEnabled(); + // if (ehrEnabled) { + // Loader._ehrStore = container.resolve(StorageService); + // await Loader._ehrStore.init(); + // } + + // Loader._notificationService = container.resolve(NotificationService); + // Loader._notificationService.init(); + + // Loader._messagingService = container.resolve(MessagingService); + // Loader._messagingService.init(); + + // await CareplanHandler.init(); + + return true; + + } catch (error) { + Logger.instance().log(error.message); + return false; + } + }; + +} diff --git a/src/startup/logger.ts b/src/startup/logger.ts index c8d6285..c05ed24 100644 --- a/src/startup/logger.ts +++ b/src/startup/logger.ts @@ -34,4 +34,24 @@ export class Logger { console.log(temp_str); }; + logQuery(query: string, parameters?: any[]) { + console.log(`Query: ${query}`, parameters); + } + + logQueryError(error: string, query: string, parameters?: any[]) { + console.error(`Error: ${error}`, query, parameters); + } + + logQuerySlow(time: number, query: string, parameters?: any[]) { + console.warn(`Slow query (${time}ms): ${query}`, parameters); + } + + logSchemaBuild(message: string) { + console.log(message); + } + + logMigration(message: string) { + console.log(message); + } + } diff --git a/tsconfig.json b/tsconfig.json index 4581d21..acba11d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,111 +1,36 @@ { - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "dist/", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": false, /* Enable all strict type-checking options. */ - "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - "strictNullChecks": false, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, - "include": ["src/**/*"], - "exclude": ["node_modules"] + "compilerOptions": { + "declaration": true, + "incremental": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "module": "commonjs", + "esModuleInterop": true, + "target": "es6", + "listFiles":true, + "noImplicitAny": false, + "moduleResolution": "node", + "sourceMap": true, + "noEmitHelpers":false, + "alwaysStrict": true, + "suppressImplicitAnyIndexErrors": false, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule" : true, + "outDir": "dist/", + "diagnostics": true, + "baseUrl": ".", + "paths": { + "*": [ + "node_modules/*" + ] + }, + "typeRoots": ["@types", "node_modules/@types"] + }, + "include": [ + "src/**/*", + "tests/api-tests/tests/*" + ], + "exclude":[ + "node_modules" + ], } From 9be14454467997ad2363378b323ea72bc7f7d539 Mon Sep 17 00:00:00 2001 From: Shubham109879 Date: Sat, 21 Jun 2025 11:09:03 +0530 Subject: [PATCH 02/29] forms_service_typeorm_initial_setup_commit_2025_06_21 --- config.json | 2 +- config.local.json | 2 +- .../20250410130816_init/migration.sql | 157 ----- prisma/migrations/migration_lock.toml | 3 - prisma/schema.prisma | 220 ------- src/api/form.section/form.section.auth.ts | 0 .../form.section/form.section.controller.ts | 28 +- .../form.section/form.section.validator.ts | 4 +- src/api/form.submission/form.controller.ts | 5 + src/api/form.submission/form.validator.ts | 6 + .../form.template/form.template.controller.ts | 3 +- .../question.response.controller.ts | 2 +- src/app.ts | 103 +-- src/common/database.utils/database.config.ts | 205 +++--- .../database.client.interface.ts | 14 +- .../dialect.clients/database.client.ts | 88 ++- .../dialect.clients/mysql.client.ts | 215 ++++-- .../dialect.clients/postgresql.client.ts | 85 --- .../dialect.clients/sqlite.client.ts | 57 -- src/database/sql/sql.injector.ts | 2 +- .../sql/typeorm/database.connector.typeorm.ts | 621 ++++++++++++------ .../typeorm/mappers/form.section.mapper.ts | 24 +- .../typeorm/mappers/form.submission.mapper.ts | 1 + .../mappers/question.response.mapper.ts | 46 +- .../sql/typeorm/models/base.entity.ts | 4 +- .../models/form.section/form.section.model.ts | 2 +- .../form.submission/form.submission.model.ts | 9 +- .../form.template/form.template.model.ts | 5 +- .../question.response.model.ts | 12 +- .../typeorm/models/question/question.model.ts | 45 +- .../sql/typeorm/models/user/user.model.ts | 52 +- .../form.section/form.section.repo.ts | 7 +- .../form.submission/form.submission.repo.ts | 3 +- .../form.template/form.template.repo.ts | 68 +- .../question.response.repo.ts | 4 +- .../repositories/question/question.repo.ts | 6 +- .../typeorm/repositories/user/user.repo.ts | 6 +- src/database/sql/typeorm/typeorm.injector.ts | 4 +- .../forms/form.section.domain.types.ts | 26 +- .../forms/form.submission.domain.types.ts | 3 + .../forms/query.response.types.ts | 17 + .../forms/question.domain.types.ts | 2 +- .../forms/response.domain.types.ts | 4 +- .../miscellaneous/system.types.ts | 4 +- src/index.ts | 2 +- .../form.template/form.template.service.ts | 4 +- src/startup/loader.ts | 2 +- src/startup/router.ts | 4 +- 48 files changed, 1078 insertions(+), 1110 deletions(-) delete mode 100644 prisma/migrations/20250410130816_init/migration.sql delete mode 100644 prisma/migrations/migration_lock.toml delete mode 100644 prisma/schema.prisma delete mode 100644 src/api/form.section/form.section.auth.ts delete mode 100644 src/common/database.utils/dialect.clients/postgresql.client.ts delete mode 100644 src/common/database.utils/dialect.clients/sqlite.client.ts create mode 100644 src/domain.types/forms/query.response.types.ts diff --git a/config.json b/config.json index b198729..ff8c234 100644 --- a/config.json +++ b/config.json @@ -9,7 +9,7 @@ }, "Database" : { "Type": "SQL", - "ORM": "Sequelize" + "ORM": "TypeORM" }, "Ehr" : { "Enabled": false, diff --git a/config.local.json b/config.local.json index 6d813c2..2abe647 100644 --- a/config.local.json +++ b/config.local.json @@ -9,7 +9,7 @@ }, "Database" : { "Type": "SQL", - "ORM": "Sequelize" + "ORM": "TypeORM" }, "Ehr" : { "Enabled": false, diff --git a/prisma/migrations/20250410130816_init/migration.sql b/prisma/migrations/20250410130816_init/migration.sql deleted file mode 100644 index e302c16..0000000 --- a/prisma/migrations/20250410130816_init/migration.sql +++ /dev/null @@ -1,157 +0,0 @@ --- CreateTable -CREATE TABLE `form_templates` ( - `id` VARCHAR(191) NOT NULL, - `Title` VARCHAR(191) NOT NULL, - `Description` VARCHAR(191) NULL, - `TenantId` VARCHAR(191) NULL, - `ItemsPerPage` ENUM('OneQuestion', 'OneSection', 'FiveQuestions', 'TenQuestions', 'AllQuestions', 'AllSections') NOT NULL, - `CurrentVersion` INTEGER NULL, - `Type` ENUM('Survey', 'Questionnaire', 'TestPaper', 'DataCollection') NOT NULL, - `DisplayCode` VARCHAR(191) NULL, - `OwnerUserId` VARCHAR(191) NOT NULL, - `RootSectionId` VARCHAR(191) NULL, - `DefaultSectionNumbering` BOOLEAN NOT NULL, - `CreatedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - `UpdatedAt` DATETIME(3) NULL, - `DeletedAt` DATETIME(3) NULL, - - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `form_sections` ( - `id` VARCHAR(191) NOT NULL, - `ParentFormTemplateId` VARCHAR(191) NOT NULL, - `SectionIdentifier` VARCHAR(191) NULL, - `Title` VARCHAR(191) NULL, - `Description` VARCHAR(191) NULL, - `DisplayCode` VARCHAR(191) NOT NULL, - `Sequence` VARCHAR(191) NULL, - `ParentSectionId` VARCHAR(191) NULL, - `CreatedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - `UpdatedAt` DATETIME(3) NULL, - `DeletedAt` DATETIME(3) NULL, - - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `questions` ( - `id` VARCHAR(191) NOT NULL, - `ParentTemplateId` VARCHAR(191) NOT NULL, - `ParentSectionId` VARCHAR(191) NOT NULL, - `Title` VARCHAR(191) NULL, - `Description` VARCHAR(191) NULL, - `DisplayCode` VARCHAR(191) NULL, - `ResponseType` ENUM('Text', 'Float', 'Integer', 'Boolean', 'Object', 'TextArray', 'SingleChoiceSelection', 'MultiChoiceSelection', 'File', 'Date', 'DateTime', 'Rating', 'Location', 'Range', 'None', 'Temperature', 'BloodPressure', 'Glucose', 'BloodOxygenSaturation', 'PulseRate', 'Hematocrit', 'Cholesterol', 'Lipoprotein', 'CReactiveProtein', 'Sleep', 'HemoglobinA1C', 'KidneyFunction', 'WaistCircumference', 'Electrolytes', 'RespiratoryRate', 'Weight', 'Height') NOT NULL, - `Score` INTEGER NULL, - `Sequence` INTEGER NULL, - `CorrectAnswer` VARCHAR(191) NULL, - `IsRequired` BOOLEAN NULL, - `Hint` VARCHAR(191) NULL, - `Options` JSON NULL, - `QuestionImageUrl` VARCHAR(191) NULL, - `RangeMin` INTEGER NULL, - `RangeMax` INTEGER NULL, - `CreatedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - `UpdatedAt` DATETIME(3) NULL, - `DeletedAt` DATETIME(3) NULL, - - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `form_submissions` ( - `id` VARCHAR(191) NOT NULL, - `FormTemplateId` VARCHAR(191) NOT NULL, - `UserId` VARCHAR(191) NULL, - `Encrypted` VARCHAR(191) NULL, - `Unencrypted` VARCHAR(191) NULL, - `Link` VARCHAR(191) NULL, - `LinkQueryParams` JSON NULL, - `Status` ENUM('LinkShared', 'Presented', 'InProgress', 'Submitted') NOT NULL, - `ValidTill` DATETIME(3) NOT NULL, - `SubmittedAt` DATETIME(3) NULL, - `CreatedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - `UpdatedAt` DATETIME(3) NULL, - `DeletedAt` DATETIME(3) NULL, - - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `question_responses` ( - `id` VARCHAR(191) NOT NULL, - `FormSubmissionId` VARCHAR(191) NOT NULL, - `QuestionId` VARCHAR(191) NOT NULL, - `ResponseType` ENUM('Text', 'Float', 'Integer', 'Boolean', 'Object', 'TextArray', 'SingleChoiceSelection', 'MultiChoiceSelection', 'File', 'Date', 'DateTime', 'Rating', 'Location', 'Range', 'None', 'Temperature', 'BloodPressure', 'Glucose', 'BloodOxygenSaturation', 'PulseRate', 'Hematocrit', 'Cholesterol', 'Lipoprotein', 'CReactiveProtein', 'Sleep', 'HemoglobinA1C', 'KidneyFunction', 'WaistCircumference', 'Electrolytes', 'RespiratoryRate', 'Weight', 'Height') NOT NULL, - `IntegerValue` INTEGER NULL, - `FloatValue` DOUBLE NULL, - `BooleanValue` VARCHAR(191) NULL, - `DateTimeValue` DATETIME(3) NULL, - `Url` VARCHAR(191) NULL, - `FileResourceId` VARCHAR(191) NULL, - `TextValue` VARCHAR(191) NULL, - `SubmissionTimestamp` DATETIME(3) NULL, - `LastSaveTimestamp` DATETIME(3) NOT NULL, - `CreatedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - `UpdatedAt` DATETIME(3) NULL, - `DeletedAt` DATETIME(3) NULL, - - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `users` ( - `id` VARCHAR(191) NOT NULL, - `FirstName` VARCHAR(191) NOT NULL, - `LastName` VARCHAR(191) NOT NULL, - `CountryCode` INTEGER NOT NULL, - `Phone` VARCHAR(191) NOT NULL, - `Email` VARCHAR(191) NOT NULL, - `Username` VARCHAR(191) NOT NULL, - `Password` VARCHAR(191) NOT NULL, - `CreatedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - `UpdatedAt` DATETIME(3) NULL, - `DeletedAt` DATETIME(3) NULL, - - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `user_login_sessions` ( - `id` VARCHAR(191) NOT NULL, - `UserId` VARCHAR(191) NOT NULL, - `IsActiveSession` BOOLEAN NOT NULL, - `StartedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - `ValidTill` DATETIME(3) NOT NULL, - `CreatedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - `UpdatedAt` DATETIME(3) NULL, - `DeletedAt` DATETIME(3) NULL, - - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- AddForeignKey -ALTER TABLE `form_templates` ADD CONSTRAINT `form_templates_OwnerUserId_fkey` FOREIGN KEY (`OwnerUserId`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE `form_sections` ADD CONSTRAINT `form_sections_ParentFormTemplateId_fkey` FOREIGN KEY (`ParentFormTemplateId`) REFERENCES `form_templates`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE `questions` ADD CONSTRAINT `questions_ParentTemplateId_fkey` FOREIGN KEY (`ParentTemplateId`) REFERENCES `form_templates`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE `questions` ADD CONSTRAINT `questions_ParentSectionId_fkey` FOREIGN KEY (`ParentSectionId`) REFERENCES `form_sections`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE `form_submissions` ADD CONSTRAINT `form_submissions_FormTemplateId_fkey` FOREIGN KEY (`FormTemplateId`) REFERENCES `form_templates`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE `question_responses` ADD CONSTRAINT `question_responses_FormSubmissionId_fkey` FOREIGN KEY (`FormSubmissionId`) REFERENCES `form_submissions`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE `question_responses` ADD CONSTRAINT `question_responses_QuestionId_fkey` FOREIGN KEY (`QuestionId`) REFERENCES `questions`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE `user_login_sessions` ADD CONSTRAINT `user_login_sessions_UserId_fkey` FOREIGN KEY (`UserId`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml deleted file mode 100644 index 592fc0b..0000000 --- a/prisma/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (e.g., Git) -provider = "mysql" diff --git a/prisma/schema.prisma b/prisma/schema.prisma deleted file mode 100644 index 68ba805..0000000 --- a/prisma/schema.prisma +++ /dev/null @@ -1,220 +0,0 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "mysql" - url = env("DATABASE_URL") -} - -model FormTemplate { - id String @id @default(uuid()) - Title String - Description String? - TenantId String? - ItemsPerPage ItemsPerPage - CurrentVersion Int? - Type FormType - DisplayCode String? - OwnerUserId String - RootSectionId String? - DefaultSectionNumbering Boolean - FormSubmissions FormSubmission[] - FormSections FormSection[] - Questions Question[] - User User @relation(fields: [OwnerUserId], references: [id]) - CreatedAt DateTime @default(now()) - UpdatedAt DateTime? - DeletedAt DateTime? - - @@map("form_templates") -} - -model FormSection { - id String @id @default(uuid()) - ParentFormTemplate FormTemplate @relation(fields: [ParentFormTemplateId], references: [id]) - ParentFormTemplateId String //one-to-many relation with form_templates @map("ParentTemplateId") - SectionIdentifier String? - Title String? - Description String? - DisplayCode String - Sequence String? - ParentSectionId String? - Questions Question[] - CreatedAt DateTime @default(now()) - UpdatedAt DateTime? - DeletedAt DateTime? - - @@map("form_sections") -} - -model Question { - id String @id @default(uuid()) - ParentFormTemplate FormTemplate @relation(fields: [ParentTemplateId], references: [id]) - ParentTemplateId String //one-to-many relation with form_templates - ParentFormSection FormSection @relation(fields: [ParentSectionId], references: [id]) - ParentSectionId String //one-to-many relation with form_section - Title String? - Description String? - DisplayCode String? - ResponseType QueryResponseType - Score Int? - Sequence Int? - CorrectAnswer String? - IsRequired Boolean? - Hint String? - Options Json? - QuestionImageUrl String? - RangeMin Int? - RangeMax Int? - Responses QuestionResponse[] - CreatedAt DateTime @default(now()) - UpdatedAt DateTime? - DeletedAt DateTime? - - @@map("questions") -} - -model FormSubmission { - id String @id @default(uuid()) - FormTemplateId String //one-to-many relation with form_templates - UserId String? // one-to-many relation with user - Encrypted String? - Unencrypted String? - Link String? - LinkQueryParams Json? - Status FormStatus - ValidTill DateTime - SubmittedAt DateTime? - QuestionResponses QuestionResponse[] - - FormTemplate FormTemplate @relation(fields: [FormTemplateId], references: [id]) - - CreatedAt DateTime @default(now()) - UpdatedAt DateTime? - DeletedAt DateTime? - - @@map("form_submissions") -} - -model QuestionResponse { - id String @id @default(uuid()) - FormSubmission FormSubmission @relation(fields: [FormSubmissionId], references: [id]) - FormSubmissionId String - Question Question @relation(fields: [QuestionId], references: [id]) - QuestionId String // one-to-many relation with question - ResponseType QueryResponseType - IntegerValue Int? - FloatValue Float? - // BooleanValue Boolean? - BooleanValue String? - DateTimeValue DateTime? - Url String? - FileResourceId String? - TextValue String? - SubmissionTimestamp DateTime? - LastSaveTimestamp DateTime - CreatedAt DateTime @default(now()) - UpdatedAt DateTime? - DeletedAt DateTime? - - @@map("question_responses") -} - -model User { - id String @id @default(uuid()) - FirstName String - LastName String - CountryCode Int - Phone String - Email String - Username String - Password String - CreatedAt DateTime @default(now()) - UpdatedAt DateTime? - DeletedAt DateTime? - LoginSessions UserLoginSession[] - Template FormTemplate[] - - @@map("users") -} - -model UserLoginSession { - id String @id @default(uuid()) - User User @relation(fields: [UserId], references: [id]) - UserId String //one-to-one relation with users - IsActiveSession Boolean - StartedAt DateTime @default(now()) - ValidTill DateTime - CreatedAt DateTime @default(now()) - UpdatedAt DateTime? - DeletedAt DateTime? - - @@map("user_login_sessions") -} - -enum FormType { - Survey - Questionnaire - TestPaper - DataCollection -} - -enum ItemsPerPage { - OneQuestion - OneSection - FiveQuestions - TenQuestions - AllQuestions - AllSections -} - -enum QueryResponseType { - Text - Float - Integer - Boolean - Object - TextArray - // FloatArray - // IntegerArray - // BooleanArray - // ObjectArray - SingleChoiceSelection - MultiChoiceSelection - File - Date - DateTime - Rating - Location - Range - //Ok //Acknowledgement - None //Not expecting response - Temperature - BloodPressure - Glucose - BloodOxygenSaturation - PulseRate - Hematocrit - Cholesterol - Lipoprotein - CReactiveProtein - Sleep - HemoglobinA1C - KidneyFunction - WaistCircumference - Electrolytes - RespiratoryRate - Weight - Height -} - -enum FormStatus { - LinkShared - Presented - InProgress - Submitted -} diff --git a/src/api/form.section/form.section.auth.ts b/src/api/form.section/form.section.auth.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/api/form.section/form.section.controller.ts b/src/api/form.section/form.section.controller.ts index 5edfde5..81cd97f 100644 --- a/src/api/form.section/form.section.controller.ts +++ b/src/api/form.section/form.section.controller.ts @@ -8,6 +8,7 @@ import { FormSectionValidator } from './form.section.validator'; import { FormSectionService } from '../../services/form.section/form.section.service'; import { FormSectionCreateModel, FormSectionSearchFilters, FormSectionUpdateModel } from '../../domain.types/forms/form.section.domain.types'; import { Injector } from '../../startup/injector'; +import { FormTemplateService } from '../../services/form.template/form.template.service'; /////////////////////////////////////////////////////////////////////////////////////// @@ -19,6 +20,8 @@ export class FormSectionController extends BaseController { _service: FormSectionService = Injector.Container.resolve(FormSectionService); + _templService: FormTemplateService=Injector.Container.resolve(FormTemplateService); + _validator: FormSectionValidator = new FormSectionValidator(); constructor() { @@ -125,14 +128,25 @@ export class FormSectionController extends BaseController { const sectionsByTemplateId = await this._service.getByTemplateId(parentTemplateId); let sequence; - sectionsByTemplateId.forEach(element => { - if (element.ParentFormTemplate.DefaultSectionNumbering === true) { - sequence = "A" + (sectionsByTemplateId.length + 1); - } else { - sequence = request.body.Sequence; - } - }); + // sectionsByTemplateId.forEach(element => { + // if (element.ParentFormTemplate.DefaultSectionNumbering === true) { + // sequence = (sectionsByTemplateId.length + 1); + // } else { + // sequence = request.body.Sequence; + // } + // }); + + const templateData=await this._templService.getById(parentTemplateId); + + if(templateData.DefaultSectionNumbering === true) + { + sequence = (Object.keys(sectionsByTemplateId).length + 1); + } + else{ + sequence = request.body.Sequence; + } + model.Sequence = sequence; const record = await this._service.create(model); if (record === null) { diff --git a/src/api/form.section/form.section.validator.ts b/src/api/form.section/form.section.validator.ts index dd57a4c..dada3fa 100644 --- a/src/api/form.section/form.section.validator.ts +++ b/src/api/form.section/form.section.validator.ts @@ -18,9 +18,9 @@ export class FormSectionValidator extends BaseValidator { ParentFormTemplateId: joi.string().uuid().optional(), Title: joi.string().optional(), Description: joi.string().optional(), - SectionIdentifier: joi.string().optional(), + // SectionIdentifier: joi.string().optional(), DisplayCode: joi.string().optional(), - Sequence: joi.string().optional(), + Sequence: joi.number().optional(), ParentSectionId: joi.string().uuid().optional(), }); await schema.validateAsync(request.body); diff --git a/src/api/form.submission/form.controller.ts b/src/api/form.submission/form.controller.ts index 95d9b92..d3695c6 100644 --- a/src/api/form.submission/form.controller.ts +++ b/src/api/form.submission/form.controller.ts @@ -39,6 +39,11 @@ export class FormController extends BaseController { ErrorHandler.throwNotFoundError('Template not found!'); } + if(model.Title == null) + { + model.Title=template.Title; + } + const record = await this._service.create(model); if (record === null) { diff --git a/src/api/form.submission/form.validator.ts b/src/api/form.submission/form.validator.ts index 8913f50..22f5295 100644 --- a/src/api/form.submission/form.validator.ts +++ b/src/api/form.submission/form.validator.ts @@ -37,6 +37,7 @@ export class FormValidator extends BaseValidator { try { const schema = joi.object({ UserId: joi.string().uuid().optional(), + FormTemplateId: joi.string().uuid().optional(), Encrypted: joi.string().optional(), Unencrypted: joi.string().optional(), Link: joi.string().optional(), @@ -164,6 +165,7 @@ export class FormValidator extends BaseValidator { private getFormSubmissionCreateModel = (request: express.Request): FormSubmissionCreateModel => { const model: FormSubmissionCreateModel = { FormTemplateId: request.body.FormTemplateId, + Title: request.body.Title ?? null, UserId: request.body.UserId ?? null, Status: request.body.Status ?? FormStatus.LinkShared, Category: request.body.FormCategory as FormType ?? FormType.Survey, @@ -182,6 +184,10 @@ export class FormValidator extends BaseValidator { model.UserId = request.body.UserId; } + if (request.body.FormTemplateId) { + model.FormTemplateId = request.body.FormTemplateId; + } + if (request.body.Encrypted) { model.Encrypted = request.body.Encrypted; } diff --git a/src/api/form.template/form.template.controller.ts b/src/api/form.template/form.template.controller.ts index 26c9a77..72943a5 100644 --- a/src/api/form.template/form.template.controller.ts +++ b/src/api/form.template/form.template.controller.ts @@ -59,7 +59,8 @@ export class FormTemplateController extends BaseController { Title: "Assessment Root Section", Description: "This is root section for this template Description", DisplayCode: displayCode, - Sequence: 'A1' + // Sequence: 'A1' + Sequence: 1 } const section = await this._section.create(sectionModel) const message = 'Form template added successfully!'; diff --git a/src/api/question.response/question.response.controller.ts b/src/api/question.response/question.response.controller.ts index c9ac20a..bea8fad 100644 --- a/src/api/question.response/question.response.controller.ts +++ b/src/api/question.response/question.response.controller.ts @@ -8,7 +8,7 @@ import { QuestionResponseValidator } from './question.response.validator'; import { ResponseService } from '../../services/question.response/question.response.service'; import { QuestionResponseCreateModel, QuestionResponseSaveModel, QuestionResponseSearchFilters, QuestionResponseUpdateModel } from '../../domain.types/forms/response.domain.types'; // import { QueryResponseType } from '@prisma/client'; -import { QueryResponseType } from '../../database/sql/typeorm/models/question/question.model'; +import { QueryResponseType } from '../../domain.types/forms/query.response.types'; import * as path from 'path'; import * as fs from 'fs'; import { container } from 'tsyringe'; diff --git a/src/app.ts b/src/app.ts index f3a26a8..c023b33 100644 --- a/src/app.ts +++ b/src/app.ts @@ -11,8 +11,9 @@ import { ConfigurationManager } from './config/configuration.manager'; import { Loader } from './startup/loader'; import { Injector } from './startup/injector'; import { DatabaseClient } from './common/database.utils/dialect.clients/database.client'; -import { DatabaseSchemaType } from './common/database.utils/database.config'; +// import { DatabaseSchemaType } from './common/database.utils/database.config'; import { PrimaryDatabaseConnector } from './database/database.connector'; +import { DBConnector } from './database/sql/typeorm/database.connector.typeorm'; // import ErrsoleMySQL from 'errsole-mysql'; // import errsole from 'errsole'; @@ -59,11 +60,13 @@ export default class Application { //Load the modules await Loader.init(); + //Connect databases + await connectDatabase_Primary(); + //Set-up middlewares await this.setupMiddlewares(); - //Connect databases - await connectDatabase_Primary(); + @@ -80,7 +83,7 @@ export default class Application { await this.listen(); } catch (error) { - Logger.instance().log('An error occurred while starting reancare-api service.' + error.message); + Logger.instance().log('An error occurred while starting Forms Service.' + error.message); } } @@ -133,59 +136,61 @@ export default class Application { // }; - public migrate = async () => { - const databaseUrl = process.env.DATABASE_URL; - - if (!databaseUrl) { - throw new Error('DATABASE_URL is not defined in the .env file'); - } + // public migrate = async () => { + // const databaseUrl = process.env.DATABASE_URL; - // Parse the database URL to extract connection parameters - const regex = /mysql:\/\/(.*?):(.*?)@(.*?):(.*?)\/(.*?)$/; - const matches = databaseUrl.match(regex); - if (!matches) { - throw new Error('DATABASE_URL format is incorrect'); - } - const [_, user, password, host, port, database] = matches; + // if (!databaseUrl) { + // throw new Error('DATABASE_URL is not defined in the .env file'); + // } - try { - const connection = await mysql.createConnection({ - host, - port: parseInt(port), - user, - password - }); - // Directly construct the query string without placeholders - const query = `SHOW DATABASES LIKE '${database}'`; - const [rows]: [mysql.RowDataPacket[], mysql.FieldPacket[]] = await connection.execute(query); - if (rows.length > 0) { - Logger.instance().log(`Database ${database} already exists. Connecting and syncing...`); - // Here you would add code to sync with the existing database if needed - } else { - Logger.instance().log(`Database ${database} does not exist. Migrating and syncing...`); - execSync('npx prisma migrate dev --name init'); - Logger.instance().log('Database migrated and synced successfully!'); - } + // // Parse the database URL to extract connection parameters + // const regex = /mysql:\/\/(.*?):(.*?)@(.*?):(.*?)\/(.*?)$/; + // const matches = databaseUrl.match(regex); + // if (!matches) { + // throw new Error('DATABASE_URL format is incorrect'); + // } + // const [_, user, password, host, port, database] = matches; - await connection.end(); - return true; - } catch (error) { - Logger.instance().error('Migration failed:', 500, error.message); - Logger.instance().error('Migration failed:', 500, error.stack); - // Logger.instance().log(error.message); - // Logger.instance().log(error.stack); // Log stack trace for debugging purposes - return false; - } - }; + // try { + // const connection = await mysql.createConnection({ + // host, + // port: parseInt(port), + // user, + // password + // }); + // // Directly construct the query string without placeholders + // const query = `SHOW DATABASES LIKE '${database}'`; + // const [rows]: [mysql.RowDataPacket[], mysql.FieldPacket[]] = await connection.execute(query); + // if (rows.length > 0) { + // Logger.instance().log(`Database ${database} already exists. Connecting and syncing...`); + // // Here you would add code to sync with the existing database if needed + // } else { + // Logger.instance().log(`Database ${database} does not exist. Migrating and syncing...`); + // execSync('npx prisma migrate dev --name init'); + // Logger.instance().log('Database migrated and synced successfully!'); + // } + + // await connection.end(); + // return true; + // } catch (error) { + // Logger.instance().error('Migration failed:', 500, error.message); + // Logger.instance().error('Migration failed:', 500, error.stack); + // // Logger.instance().log(error.message); + // // Logger.instance().log(error.stack); // Log stack trace for debugging purposes + // return false; + // } + // }; } async function connectDatabase_Primary() { if (process.env.NODE_ENV === 'test') { - const databaseClient = Injector.Container.resolve(DatabaseClient); - await databaseClient.dropDb(DatabaseSchemaType.Primary); + // const databaseClient = Injector.Container.resolve(DatabaseClient); + // await databaseClient.dropDb(DatabaseSchemaType.Primary); + await DatabaseClient.dropDatabase(); } - const primaryDatabaseConnector = Injector.Container.resolve(PrimaryDatabaseConnector); - await primaryDatabaseConnector.init(); + // const primaryDatabaseConnector = Injector.Container.resolve(PrimaryDatabaseConnector); + DatabaseClient.createDatabase(); + await DBConnector.initialize(); } diff --git a/src/common/database.utils/database.config.ts b/src/common/database.utils/database.config.ts index e72602d..276dd56 100644 --- a/src/common/database.utils/database.config.ts +++ b/src/common/database.utils/database.config.ts @@ -1,92 +1,145 @@ -import { DatabaseDialect } from "../../domain.types/miscellaneous/system.types"; +// import { DatabaseDialect } from "../../domain.types/miscellaneous/system.types"; +// import * as dotenv from 'dotenv'; +// import { Logger } from "../../common/logger"; +// import { ConfigurationManager } from "../../config/configuration.manager"; + +// ///////////////////////////////////////////////////////////////////////////// + +// export enum DatabaseSchemaType { +// Primary = 'Primary', +// EHRInsights = 'EHRInsights', +// AwardsFacts = 'AwardsFacts' +// } + +// export interface DatabaseConfiguration { +// Dialect : DatabaseDialect; +// DatabaseName : string; +// Host : string; +// Port : number; +// Username : string; +// Password : string; +// ConnectionString: string; +// Pool : { +// Max : number, +// Min : number, +// Acquire: number, +// Idle : number, +// }, +// Cache : boolean; +// Synchronize: boolean; +// Logging : boolean; +// } + +// export const databaseConfig = (schemaType: DatabaseSchemaType) +// : DatabaseConfiguration => { + +// const dialect = process.env.DB_DIALECT as DatabaseDialect; +// const config: DatabaseConfiguration = { +// Dialect : dialect, +// DatabaseName : process.env.DB_NAME, +// Host : process.env.DB_HOST, +// Port : parseInt(process.env.DB_PORT), +// Username : process.env.DB_USER_NAME, +// Password : process.env.DB_USER_PASSWORD, +// ConnectionString : process.env.CONNECTION_STRING ?? null, +// Pool : { +// Max : 20, +// Min : 0, +// Acquire : 30000, +// Idle : 10000, +// }, +// Cache : true, +// Logging : true, +// Synchronize : true +// }; + +// if (schemaType === DatabaseSchemaType.EHRInsights && +// ConfigurationManager.EHRAnalyticsEnabled()) { +// config.DatabaseName = process.env.DB_NAME_EHR_INSIGHTS; +// } +// else if (schemaType === DatabaseSchemaType.AwardsFacts && +// ConfigurationManager.GamificationEnabled()) { +// config.DatabaseName = process.env.DB_NAME_AWARDS_FACTS; +// } + +// return config; +// }; + +// ////////////////////////////////////////////////////////////////////////////////// + +// if (typeof process.env.NODE_ENV === 'undefined') { +// dotenv.config(); +// } + +// Logger.instance().log('================================================'); +// Logger.instance().log('Environment : ' + process.env.NODE_ENV); +// Logger.instance().log('Database dialect : ' + process.env.DB_DIALECT); +// Logger.instance().log('Database host : ' + process.env.DB_HOST); +// Logger.instance().log('Database port : ' + process.env.DB_PORT); +// Logger.instance().log('Database user-name : ' + process.env.DB_USER_NAME); +// Logger.instance().log('Primary database name : ' + process.env.DB_NAME); + +// if (ConfigurationManager.EHRAnalyticsEnabled()) { +// Logger.instance().log('EHR insights database name : ' + process.env.DB_NAME_EHR_INSIGHTS); +// } +// if (ConfigurationManager.GamificationEnabled()) { +// Logger.instance().log('Awards facts database name : ' + process.env.DB_NAME_AWARDS_FACTS); +// } + +// Logger.instance().log('================================================'); + +// ////////////////////////////////////////////////////////////////////////////////// + import * as dotenv from 'dotenv'; -import { Logger } from "../../common/logger"; -import { ConfigurationManager } from "../../config/configuration.manager"; +import { Logger } from '../logger'; ///////////////////////////////////////////////////////////////////////////// -export enum DatabaseSchemaType { - Primary = 'Primary', - EHRInsights = 'EHRInsights', - AwardsFacts = 'AwardsFacts' -} +export type databaseDialect = 'mysql'; -export interface DatabaseConfiguration { - Dialect : DatabaseDialect; - DatabaseName : string; - Host : string; - Port : number; - Username : string; - Password : string; - ConnectionString: string; - Pool : { - Max : number, - Min : number, - Acquire: number, - Idle : number, +export interface DatabaseConfig { + username: string; + password: string; + database: string; + host : string; + port : number; + dialect : databaseDialect, + pool : { + max : 20, + min : 0, + acquire: 30000, + idle : 10000, }, - Cache : boolean; - Synchronize: boolean; - Logging : boolean; } -export const databaseConfig = (schemaType: DatabaseSchemaType) - : DatabaseConfiguration => { - - const dialect = process.env.DB_DIALECT as DatabaseDialect; - const config: DatabaseConfiguration = { - Dialect : dialect, - DatabaseName : process.env.DB_NAME, - Host : process.env.DB_HOST, - Port : parseInt(process.env.DB_PORT), - Username : process.env.DB_USER_NAME, - Password : process.env.DB_USER_PASSWORD, - ConnectionString : process.env.CONNECTION_STRING ?? null, - Pool : { - Max : 20, - Min : 0, - Acquire : 30000, - Idle : 10000, - }, - Cache : true, - Logging : true, - Synchronize : true - }; - - if (schemaType === DatabaseSchemaType.EHRInsights && - ConfigurationManager.EHRAnalyticsEnabled()) { - config.DatabaseName = process.env.DB_NAME_EHR_INSIGHTS; - } - else if (schemaType === DatabaseSchemaType.AwardsFacts && - ConfigurationManager.GamificationEnabled()) { - config.DatabaseName = process.env.DB_NAME_AWARDS_FACTS; - } - - return config; -}; - -////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// if (typeof process.env.NODE_ENV === 'undefined') { dotenv.config(); } -Logger.instance().log('================================================'); -Logger.instance().log('Environment : ' + process.env.NODE_ENV); -Logger.instance().log('Database dialect : ' + process.env.DB_DIALECT); -Logger.instance().log('Database host : ' + process.env.DB_HOST); -Logger.instance().log('Database port : ' + process.env.DB_PORT); -Logger.instance().log('Database user-name : ' + process.env.DB_USER_NAME); -Logger.instance().log('Primary database name : ' + process.env.DB_NAME); - -if (ConfigurationManager.EHRAnalyticsEnabled()) { - Logger.instance().log('EHR insights database name : ' + process.env.DB_NAME_EHR_INSIGHTS); -} -if (ConfigurationManager.GamificationEnabled()) { - Logger.instance().log('Awards facts database name : ' + process.env.DB_NAME_AWARDS_FACTS); +if (process.env.NODE_ENV === 'test') { + Logger.instance().log('================================================'); + Logger.instance().log('Environment : ' + process.env.NODE_ENV); + Logger.instance().log('Database Dialect : ' + process.env.DB_DIALECT); + Logger.instance().log('Database name : ' + process.env.DB_NAME); + Logger.instance().log('Database user : ' + process.env.DB_USER_NAME); + Logger.instance().log('Database host : ' + process.env.DB_HOST); + Logger.instance().log('================================================'); } -Logger.instance().log('================================================'); - -////////////////////////////////////////////////////////////////////////////////// +export const Config : DatabaseConfig = { + username : process.env.DB_USER_NAME, + password : process.env.DB_USER_PASSWORD, + database : process.env.DB_NAME, + host : process.env.DB_HOST, + port : parseInt(process.env.DB_PORT), + dialect : process.env.DB_DIALECT as databaseDialect, + pool : { + max : 20, + min : 0, + acquire : 30000, + idle : 10000, + }, +}; diff --git a/src/common/database.utils/dialect.clients/database.client.interface.ts b/src/common/database.utils/dialect.clients/database.client.interface.ts index cdcd5cf..860a17a 100644 --- a/src/common/database.utils/dialect.clients/database.client.interface.ts +++ b/src/common/database.utils/dialect.clients/database.client.interface.ts @@ -1,11 +1,17 @@ -import { DatabaseSchemaType } from "../database.config"; +// import { DatabaseSchemaType } from "../database.config"; export interface IDatabaseClient { - createDb(schemaType: DatabaseSchemaType): Promise; + // createDb(schemaType: DatabaseSchemaType): Promise; - dropDb(schemaType: DatabaseSchemaType): Promise; + // dropDb(schemaType: DatabaseSchemaType): Promise; - execute(schemaType: DatabaseSchemaType, query: string): Promise; + // execute(schemaType: DatabaseSchemaType, query: string): Promise; + + createDb(): Promise; + + dropDb(): Promise; + + executeQuery(query: string): Promise; } diff --git a/src/common/database.utils/dialect.clients/database.client.ts b/src/common/database.utils/dialect.clients/database.client.ts index 69b0484..6d4d47d 100644 --- a/src/common/database.utils/dialect.clients/database.client.ts +++ b/src/common/database.utils/dialect.clients/database.client.ts @@ -1,36 +1,86 @@ -import { DatabaseSchemaType } from "../database.config"; +// import { DatabaseSchemaType } from "../database.config"; +import { Logger } from '../../../common/logger'; import { DatabaseDialect } from '../../../domain.types/miscellaneous/system.types'; +import { IDatabaseClient } from './database.client.interface'; import { MysqlClient } from "./mysql.client"; -import { PostgresqlClient } from "./postgresql.client"; +// import { PostgresqlClient } from "./postgresql.client"; ////////////////////////////////////////////////////////////////////////////// export class DatabaseClient { - _client = null; + // _client = null; - constructor() { - const dialect = process.env.DB_DIALECT as DatabaseDialect; - if (dialect === 'mysql') { - this._client = MysqlClient.getInstance(); - } else if (dialect === 'postgres') { - this._client = PostgresqlClient.getInstance(); - } else { - // this._client = SQLiteClient.getInstance(); - } + // constructor() { + // const dialect = process.env.DB_DIALECT as DatabaseDialect; + // if (dialect === 'mysql') { + // this._client = MysqlClient.getInstance(); + // } else if (dialect === 'postgres') { + // this._client = PostgresqlClient.getInstance(); + // } else { + // // this._client = SQLiteClient.getInstance(); + // } + + // } + + // public createDb = async (schemaType: DatabaseSchemaType): Promise => { + // return await this._client.createDb(schemaType); + // }; + + // public dropDb = async (schemaType: DatabaseSchemaType): Promise => { + // return await this._client.dropDb(schemaType); + // }; + + // public executeQuery = async (schemaType: DatabaseSchemaType, query: string): Promise => { + // return await this._client.executeQuery(schemaType, query); + // }; - } + static _client: IDatabaseClient = new MysqlClient(); - public createDb = async (schemaType: DatabaseSchemaType): Promise => { - return await this._client.createDb(schemaType); + //Creates DB if does not exist + public static createDatabase = async () => { + try { + await this._client.createDb(); + return true; + } catch (error) { + Logger.instance().log(error.message); + } + return false; }; - public dropDb = async (schemaType: DatabaseSchemaType): Promise => { - return await this._client.dropDb(schemaType); + //Drops DB if exists + public static dropDatabase = async () => { + try { + await this._client.dropDb(); + return true; + } catch (error) { + Logger.instance().log(error.message); + } + return false; }; - public executeQuery = async (schemaType: DatabaseSchemaType, query: string): Promise => { - return await this._client.executeQuery(schemaType, query); + //Drops DB if exists + public static executeQuery = async (query: string) => { + try { + await this._client.executeQuery(query); + return true; + } catch (error) { + Logger.instance().log(error.message); + } + return false; }; + // public static migrate = async () => { + // try { + // const output = execSync('npx sequelize-cli db:migrate'); + // const str = output.toString(); + // logger.info('Database migrated successfully!'); + // logger.info(str); + // return true; + // } catch (error) { + // logger.error(error.message); + // } + // return false; + // }; + } diff --git a/src/common/database.utils/dialect.clients/mysql.client.ts b/src/common/database.utils/dialect.clients/mysql.client.ts index c137ce0..2efd74c 100644 --- a/src/common/database.utils/dialect.clients/mysql.client.ts +++ b/src/common/database.utils/dialect.clients/mysql.client.ts @@ -1,96 +1,171 @@ -// eslint-disable-next-line @typescript-eslint/no-var-requires -import mysql, { Connection } from 'mysql2/promise'; -import { Logger } from '../../logger'; -import { DatabaseSchemaType, databaseConfig } from '../database.config'; -import { IDatabaseClient } from './database.client.interface'; +// // eslint-disable-next-line @typescript-eslint/no-var-requires +// import mysql, { Connection } from 'mysql2/promise'; +// import { Logger } from '../../logger'; +// import { DatabaseSchemaType, databaseConfig } from '../database.config'; +// import { IDatabaseClient } from './database.client.interface'; -////////////////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////////////////// -export class MysqlClient implements IDatabaseClient { +// export class MysqlClient implements IDatabaseClient { - private connection: Connection = null; +// private connection: Connection = null; - private static instance: MysqlClient = null; +// private static instance: MysqlClient = null; - private constructor() {} +// private constructor() {} + +// public static getInstance() { +// return this.instance || (this.instance = new MysqlClient()); +// } + +// public connect = async (schemaType: DatabaseSchemaType): Promise => { +// try { +// const config = databaseConfig(schemaType); +// this.connection = await mysql.createConnection({ +// database : config.DatabaseName, +// host : config.Host, +// user : config.Username, +// password : config.Password, +// }); + +// } catch (error) { +// Logger.instance().log(error.message); +// Logger.instance().log(`Error trace: ${error.stack}`); +// } +// }; + +// public createDb = async (schemaType: DatabaseSchemaType): Promise => { +// try { +// const config = databaseConfig(schemaType); +// //var query = `CREATE DATABASE ${config.database} CHARACTER SET utf8 COLLATE utf8_general_ci;`; +// const query = `CREATE DATABASE ${config.DatabaseName}`; +// return await this.execute(schemaType, query); +// } catch (error) { +// Logger.instance().log(error.message); +// } +// }; + +// public dropDb = async (schemaType: DatabaseSchemaType): Promise => { +// try { +// const config = databaseConfig(schemaType); +// const query = `DROP DATABASE IF EXISTS ${config.DatabaseName}`; +// return await this.execute(schemaType, query); +// } catch (error) { +// Logger.instance().log(error.message); +// } +// }; + +// public executeQuery = async (query: string): Promise => { +// try { +// const result = await this.connection.query(query); +// return result; +// } catch (error) { +// Logger.instance().log(error.message); +// Logger.instance().log(`Error trace: ${error.stack}`); +// } +// return null; +// }; + +// public execute = async (schemaType: DatabaseSchemaType, query: string): Promise => { + +// try { +// const config = databaseConfig(schemaType); + +// const connection = await mysql.createConnection({ +// host : config.Host, +// user : config.Username, +// password : config.Password, +// }); + +// await connection.query(query); +// return true; +// } catch (error) { +// Logger.instance().log(error.message); +// } +// }; + +// public closeDbConnection = async () => { +// try { +// await this.connection.end(); +// } catch (error) { +// Logger.instance().log(error.message); +// } +// }; + +// } - public static getInstance() { - return this.instance || (this.instance = new MysqlClient()); - } +// eslint-disable-next-line @typescript-eslint/no-var-requires +// const mysql = require('mysql2'); +import mysql from 'mysql2'; +import { Logger } from '../../../startup/logger'; +// import { Logger } from '../../../common/logger'; +import { Config } from '../database.config'; - public connect = async (schemaType: DatabaseSchemaType): Promise => { - try { - const config = databaseConfig(schemaType); - this.connection = await mysql.createConnection({ - database : config.DatabaseName, - host : config.Host, - user : config.Username, - password : config.Password, - }); +//////////////////////////////////////////////////////////////// - } catch (error) { - Logger.instance().log(error.message); - Logger.instance().log(`Error trace: ${error.stack}`); - } - }; - - public createDb = async (schemaType: DatabaseSchemaType): Promise => { - try { - const config = databaseConfig(schemaType); - //var query = `CREATE DATABASE ${config.database} CHARACTER SET utf8 COLLATE utf8_general_ci;`; - const query = `CREATE DATABASE ${config.DatabaseName}`; - return await this.execute(schemaType, query); - } catch (error) { - Logger.instance().log(error.message); - } - }; +export class MysqlClient { - public dropDb = async (schemaType: DatabaseSchemaType): Promise => { + public createDb = async () => { try { - const config = databaseConfig(schemaType); - const query = `DROP DATABASE IF EXISTS ${config.DatabaseName}`; - return await this.execute(schemaType, query); + const query = `CREATE DATABASE ${Config.database}`; + await this.executeQuery(query); + Logger.instance().log(`Database ${Config.database} created successfully!`); } catch (error) { Logger.instance().log(error.message); } }; - public executeQuery = async (query: string): Promise => { + public dropDb = async () => { try { - const result = await this.connection.query(query); - return result; + const query = `DROP DATABASE IF EXISTS ${Config.database}`; + await this.executeQuery(query); } catch (error) { Logger.instance().log(error.message); - Logger.instance().log(`Error trace: ${error.stack}`); } - return null; }; - public execute = async (schemaType: DatabaseSchemaType, query: string): Promise => { + public executeQuery = (query): Promise => { + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + return new Promise((resolve, reject) => { + try { + const connection = mysql.createConnection({ + host : Config.host, + user : Config.username, + password : Config.password, + }); + + connection.connect(function (err) { + if (err) { + Logger.instance().log(err.message); + reject(err); + } + + connection.query(query, function (err, result) { + if (err) { + Logger.instance().log(err.message); + + var str = (result !== undefined && result !== null) ? result.toString() : null; + if (str != null) { + Logger.instance().log(str); + } + else { + Logger.instance().log(`Query: ${query}`); + } + reject(err); + } + resolve(true); + }); + }); + + } + catch (error) { + Logger.instance().log(error.message); + } + }); - try { - const config = databaseConfig(schemaType); - - const connection = await mysql.createConnection({ - host : config.Host, - user : config.Username, - password : config.Password, - }); - - await connection.query(query); - return true; - } catch (error) { - Logger.instance().log(error.message); - } - }; - - public closeDbConnection = async () => { - try { - await this.connection.end(); - } catch (error) { - Logger.instance().log(error.message); - } }; } + diff --git a/src/common/database.utils/dialect.clients/postgresql.client.ts b/src/common/database.utils/dialect.clients/postgresql.client.ts deleted file mode 100644 index 0a3fa67..0000000 --- a/src/common/database.utils/dialect.clients/postgresql.client.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Client } from 'pg'; -import { Logger } from '../../logger'; -import { IDatabaseClient } from './database.client.interface'; -import { DatabaseSchemaType, databaseConfig } from '../database.config'; - -//////////////////////////////////////////////////////////////// - -export class PostgresqlClient implements IDatabaseClient { - - private connection: Client = null; - - private static instance: PostgresqlClient = null; - - private constructor() {} - - public static getInstance() { - return this.instance || (this.instance = new PostgresqlClient()); - } - - public connect = async (schemaType: DatabaseSchemaType): Promise => { - try { - const config = databaseConfig(schemaType); - this.connection = new Client({ - database : config.DatabaseName, - host : config.Host, - user : config.Username, - password : config.Password, - }); - await this.connection.connect(); - } catch (error) { - Logger.instance().log(error.message); - Logger.instance().log(`Error trace: ${error.stack}`); - } - }; - - public createDb = async (schemaType: DatabaseSchemaType): Promise => { - try { - const config = databaseConfig(schemaType); - const query = `CREATE DATABASE ${config.DatabaseName}`; - return await this.execute(schemaType, query); - } catch (error) { - Logger.instance().log(error.message); - } - }; - - public executeQuery = async (query: string): Promise => { - try { - const result = await this.connection.query(query); - return result; - } catch (error) { - Logger.instance().log(error.message); - Logger.instance().log(`Error trace: ${error.stack}`); - } - return null; - }; - - public dropDb = async (schemaType: DatabaseSchemaType): Promise => { - try { - const config = databaseConfig(schemaType); - const query = `DROP DATABASE IF EXISTS ${config.DatabaseName}`; - return await this.execute(schemaType, query); - } catch (error) { - Logger.instance().log(error.message); - } - }; - - public execute = async (schemaType: DatabaseSchemaType, query: string): Promise => { - try { - const config = databaseConfig(schemaType); - const client = new Client({ - user : config.Username, - host : config.Host, - password : config.Password, - port : 5432, - }); - await client.connect(); - await client.query(query); - await client.end(); - return true; - } catch (error) { - Logger.instance().log(error.message); - } - }; - -} diff --git a/src/common/database.utils/dialect.clients/sqlite.client.ts b/src/common/database.utils/dialect.clients/sqlite.client.ts deleted file mode 100644 index 03c2b63..0000000 --- a/src/common/database.utils/dialect.clients/sqlite.client.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* -import sqlite3 from 'sqlite3'; -import * as fs from 'fs'; -import { Logger } from '../../logger'; -import { IDatabaseClient } from './database.client.interface'; -import { DatabaseSchemaType, databaseConfig } from '../database.config'; - -//////////////////////////////////////////////////////////////// - -export class SQLiteClient implements IDatabaseClient { - - public createDb = async (schemaType: DatabaseSchemaType): Promise => { - try { - const config = databaseConfig(schemaType); - const db = new sqlite3.Database(config.DatabaseName, (err) => { - if (err) { - // eslint-disable-next-line no-console - console.log('Error connecting to the database:', err.message); - } else { - // eslint-disable-next-line no-console - console.log('Connected to the SQLite database.'); - } - }); - return db != null; - } catch (error) { - Logger.instance().log(error.message); - } - }; - - public dropDb = async (schemaType: DatabaseSchemaType): Promise => { - try { - const config = databaseConfig(schemaType); - const databaseName = config.DatabaseName; - fs.unlinkSync(databaseName); - return true; - } catch (error) { - Logger.instance().log(error.message); - } - }; - - public executeQuery = async (schemaType: DatabaseSchemaType, query: string): Promise => { - try { - const config = databaseConfig(schemaType); - const db = new sqlite3.Database(config.DatabaseName); - db.run(query, (err) => { - Logger.instance().log(err.message); - return false; - }); - return true; - } catch (error) { - Logger.instance().log(error.message); - } - }; - -} - -*/ diff --git a/src/database/sql/sql.injector.ts b/src/database/sql/sql.injector.ts index 2cf3783..c87a207 100644 --- a/src/database/sql/sql.injector.ts +++ b/src/database/sql/sql.injector.ts @@ -11,7 +11,7 @@ export class SQLInjector static registerInjections(container: DependencyContainer) { const databaseORM = ConfigurationManager.DatabaseORM(); - if (databaseORM === 'Sequelize') { + if (databaseORM === 'TypeORM') { TypeOrmInjector.registerInjections(container); } diff --git a/src/database/sql/typeorm/database.connector.typeorm.ts b/src/database/sql/typeorm/database.connector.typeorm.ts index e13fcba..459c2f6 100644 --- a/src/database/sql/typeorm/database.connector.typeorm.ts +++ b/src/database/sql/typeorm/database.connector.typeorm.ts @@ -1,173 +1,430 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import { execSync } from 'child_process'; -// import { Dialect } from 'sequelize'; -// import { Sequelize } from 'sequelize-typescript'; -// import { Logger } from '../../../common/logger'; -import { IPrimaryDatabaseConnector } from '../../database.connector.interface'; -import { DatabaseSchemaType, databaseConfig } from '../../../common/database.utils/database.config'; -import { DataSource } from 'typeorm'; -import { FavoriteTemplate } from './models/favorite.template/favorite.template.model'; -import { FormSection } from './models/form.section/form.section.model'; -import { FormSubmission } from './models/form.submission/form.submission.model'; -import { FormTemplate } from './models/form.template/form.template.model'; -import { FormTemplateApproval } from './models/form.template.approval/form.template.approval.model'; -import { InputUnitList } from './models/input.unit.list/input.unit.list.model'; -import { Question } from './models/question/question.model'; -import { QuestionResponse } from './models/question.response/question.response.model'; -import { TemplateFolder } from './models/template.folder/template.folder.model'; -import { User } from './models/user/user.model'; -import { Logger } from '../../../startup/logger'; - -const Config = databaseConfig(DatabaseSchemaType.Primary); - -////////////////////////////////////////////////////////////// - -export class DatabaseConnector_TypeOrm implements IPrimaryDatabaseConnector { - // private _sequelize: Sequelize = null; - - // public static db: Sequelize = null; - - public _source = new DataSource({ - name: Config.Dialect, - type: Config.Dialect as any, - host: Config.Host, - port: Config.Port, - username: Config.Username, - password: Config.Password, - database: Config.DatabaseName, - synchronize: true, - entities: [ - FavoriteTemplate, - FormSection, - FormSubmission, - FormTemplate, - FormTemplateApproval, - InputUnitList, - Question, - QuestionResponse, - TemplateFolder, - User, - ], - migrations: [], - subscribers: [], - // logger: new DBLogger(), - logger: Logger.instance(), - logging: true, - poolSize: Config.Pool.Max, - cache: true, - }); - - // public connect = async (): Promise => { - // try { - - // const config = databaseConfig(DatabaseSchemaType.Primary); - // const modelsFolder = path.join(__dirname, '/models'); - // const modelsPath = getFoldersRecursively(modelsFolder); - // const options = { - // host : config.Host, - // dialect : config.Dialect, - // models : modelsPath, - // pool : { - // max : config.Pool.Max, - // min : config.Pool.Min, - // acquire : config.Pool.Acquire, - // idle : config.Pool.Idle, - // }, - // logging : false, //TODO: Please provide a function here to handle logging... - // }; - - // const sequelize = new Sequelize(config.DatabaseName, config.Username, config.Password, options); - // this._sequelize = sequelize; - - // Logger.instance().log(`Connecting to database '${config.DatabaseName}' ...`); - - // const databaseClient = Injector.Container.resolve(DatabaseClient); - // await databaseClient.createDb(DatabaseSchemaType.Primary); - - // await this._sequelize.authenticate(); - // await this._sequelize.sync({ force: false, alter: true }); - - // Logger.instance().log(`Connected to database '${config.DatabaseName}'.`); +// import "reflect-metadata"; +// import * as fs from 'fs'; +// import * as path from 'path'; +// import { execSync } from 'child_process'; +// // import { Dialect } from 'sequelize'; +// // import { Sequelize } from 'sequelize-typescript'; +// // import { Logger } from '../../../common/logger'; +// import { IPrimaryDatabaseConnector } from '../../database.connector.interface'; +// import { DatabaseSchemaType, databaseConfig } from '../../../common/database.utils/database.config'; +// import { DataSource, DataSourceOptions } from 'typeorm'; +// import { FavoriteTemplate } from './models/favorite.template/favorite.template.model'; +// import { FormSection } from './models/form.section/form.section.model'; +// import { FormSubmission } from './models/form.submission/form.submission.model'; +// import { FormTemplate } from './models/form.template/form.template.model'; +// import { FormTemplateApproval } from './models/form.template.approval/form.template.approval.model'; +// import { InputUnitList } from './models/input.unit.list/input.unit.list.model'; +// import { Question } from './models/question/question.model'; +// import { QuestionResponse } from './models/question.response/question.response.model'; +// import { TemplateFolder } from './models/template.folder/template.folder.model'; +// import { User } from './models/user/user.model'; +// import { Logger } from '../../../startup/logger'; +// import { Injector } from "../../../startup/injector"; +// import { DatabaseClient } from "../../../common/database.utils/dialect.clients/database.client"; + +// const Config = databaseConfig(DatabaseSchemaType.Primary); + +// ////////////////////////////////////////////////////////////// + +// export class DatabaseConnector_TypeOrm implements IPrimaryDatabaseConnector { +// // private _sequelize: Sequelize = null; + +// // public static db: Sequelize = null; + +// public static _source = new DataSource({ +// name: Config.Dialect, +// type: Config.Dialect, +// host: Config.Host, +// port: Config.Port, +// username: Config.Username, +// password: Config.Password, +// database: Config.DatabaseName, +// synchronize: true, +// entities: [ +// // FavoriteTemplate, +// // FormSection, +// // FormSubmission, +// // FormTemplate, +// // FormTemplateApproval, +// // InputUnitList, +// // Question, +// // QuestionResponse, +// // TemplateFolder, +// User, +// ], +// migrations: [], +// subscribers: [], +// // logger: new DBLogger(), +// logger: Logger.instance(), +// logging: true, +// // poolSize: Config.Pool.Max, +// cache: true, +// }); + +// // public connect = async (): Promise => { +// // try { + +// // const config = databaseConfig(DatabaseSchemaType.Primary); +// // const modelsFolder = path.join(__dirname, '/models'); +// // const modelsPath = getFoldersRecursively(modelsFolder); +// // const options = { +// // host : config.Host, +// // dialect : config.Dialect, +// // models : modelsPath, +// // pool : { +// // max : config.Pool.Max, +// // min : config.Pool.Min, +// // acquire : config.Pool.Acquire, +// // idle : config.Pool.Idle, +// // }, +// // logging : false, //TODO: Please provide a function here to handle logging... +// // }; + +// // const sequelize = new Sequelize(config.DatabaseName, config.Username, config.Password, options); +// // this._sequelize = sequelize; + +// // Logger.instance().log(`Connecting to database '${config.DatabaseName}' ...`); + +// // const databaseClient = Injector.Container.resolve(DatabaseClient); +// // await databaseClient.createDb(DatabaseSchemaType.Primary); + +// // await this._sequelize.authenticate(); +// // await this._sequelize.sync({ force: false, alter: true }); + +// // Logger.instance().log(`Connected to database '${config.DatabaseName}'.`); + +// // DatabaseConnector_Sequelize.db = this._sequelize; + +// // return true; + +// // } catch (error) { +// // Logger.instance().log(error.message); +// // return false; +// // } +// // }; + +// public connect = async (): Promise => { +// try { +// const config = databaseConfig(DatabaseSchemaType.Primary); +// // const entitiesFolder = path.join(__dirname, "/models"); +// // const entitiesPath = getFoldersRecursively(entitiesFolder); + +// Logger.instance().log( +// `Connecting to database '${config.DatabaseName}' ...` +// ); + +// // const options : DataSourceOptions= { +// // name: Config.Dialect, +// // type: config.Dialect, +// // host: config.Host, +// // port: config.Port || 3306, +// // username: config.Username, +// // password: config.Password, +// // database: config.DatabaseName, +// // entities: [ +// // // FormTemplate, +// // // FavoriteTemplate, +// // // FormSection, +// // // FormSubmission, +// // // FormTemplateApproval, +// // // InputUnitList, +// // // Question, +// // // QuestionResponse, +// // // TemplateFolder, +// // User, +// // ], +// // synchronize: true, +// // logging: true, +// // migrations: [], +// // subscribers: [], +// // // logger: new DBLogger(), +// // logger: Logger.instance(), +// // cache: true +// // // poolSize: config.Pool?.Max || 10, +// // // extra: { +// // // connectionLimit: config.Pool?.Max || 10, +// // // idleTimeout: config.Pool?.Idle || 10000, +// // // // acquireTimeout: config.Pool?.Acquire || 30000, +// // // }, +// // }; + +// // this._source = new DataSource(options); + +// const databaseClient = Injector.Container.resolve(DatabaseClient); +// await databaseClient.createDb(DatabaseSchemaType.Primary); + +// await this.initialize(); + +// // await this._source.initialize(); + +// Logger.instance().log( +// `${FormTemplate}` +// ); + +// // Logger.instance().log(`Connected to database '${config.DatabaseName}'.`); + +// return true; +// } catch (error) { +// Logger.instance().log( +// error instanceof Error +// ? error.message +// : "Unknown database connection error" +// ); +// return false; +// } +// }; - // DatabaseConnector_Sequelize.db = this._sequelize; +// private initialize = (): Promise => { +// return new Promise((resolve, reject) => { +// this._source +// .initialize() +// .then(() => { +// Logger.instance().log('Database connection has been established successfully.'); +// resolve(true); +// }) +// .catch((error) => { +// Logger.instance().log('Unable to connect to the database:' + error.message); +// reject(false); +// }); +// }); +// }; + +// // public db = (): Sequelize => { +// // return this._sequelize; +// // }; + +// // public sync = async () => { +// // try { +// // await this._sequelize.sync({ alter: true }); +// // return true; +// // } catch (error) { +// // Logger.instance().log(error.message); +// // } +// // return false; +// // }; + +// public sync = async () => { +// try { +// if (!this._source.isInitialized) { +// await this._source.initialize(); +// } - // return true; +// await this._source.synchronize(); - // } catch (error) { - // Logger.instance().log(error.message); - // return false; - // } - // }; +// return true; +// } catch (error) { +// Logger.instance().log( +// error instanceof Error ? error.message : "Unknown synchronization error" +// ); +// return false; +// } +// }; - public connect = async (): Promise => { - try { - const config = databaseConfig(DatabaseSchemaType.Primary); - const entitiesFolder = path.join(__dirname, "/models"); - const entitiesPath = getFoldersRecursively(entitiesFolder); +// // public migrate = async () => { +// // try { +// // const output = execSync('npx sequelize-cli db:migrate'); + +// // const str = output.toString(); +// // Logger.instance().log('Database migrated successfully!'); +// // Logger.instance().log(str); + +// // return true; +// // } catch (error) { +// // Logger.instance().log(error.message); +// // } +// // return false; +// // }; +// public migrate = async (): Promise => { +// try { +// if (!this._source.isInitialized) { +// await this._source.initialize(); +// } + +// // Run pending migrations +// const migrations = await this._source.runMigrations(); + +// if (migrations.length > 0) { +// Logger.instance().log("Database migrated successfully!"); +// migrations.forEach((migration) => { +// Logger.instance().log(`Executed migration: ${migration.name}`); +// }); +// } else { +// Logger.instance().log("No pending migrations found."); +// } + +// return true; +// } catch (error) { +// Logger.instance().log( +// error instanceof Error ? error.message : "Migration error" +// ); +// return false; +// } +// }; - Logger.instance().log( - `Connecting to database '${config.DatabaseName}' ...` - ); +// // public executeQuery = async (query: string): Promise => { +// // try { +// // const result = await this._sequelize.query(query); +// // return result; +// // } catch (error) { +// // Logger.instance().log(error.message); +// // Logger.instance().log(`Error trace: ${error.stack}`); +// // } +// // return null; +// // }; + +// public executeQuery = async (query: string): Promise => { +// try { +// if (!this._source.isInitialized) { +// await this._source.initialize(); +// } - const options = { - type: config.Dialect as any, - host: config.Host, - port: config.Port || 3306, - username: config.Username, - password: config.Password, - database: config.DatabaseName, - entity: entitiesPath, - entities: [ - path.join(entitiesFolder, '**/*.model{.ts,.js}') // Common TypeORM entity pattern +// // For SELECT queries that return data +// const result = await this._source.query(query); +// return result; + +// } catch (error) { +// Logger.instance().log(error instanceof Error ? error.message : 'Query execution error'); +// Logger.instance().log(`Error trace: ${error instanceof Error ? error.stack : 'No stack trace'}`); +// throw error; // Consider throwing instead of returning null +// } +// }; +// } + +// /////////////////////////////////////////////////////////////////////////////////////////// + +// function getFoldersRecursively(location: string) { +// const items = fs.readdirSync(location, { withFileTypes: true }); +// let paths = []; +// for (const item of items) { +// if (item.isDirectory()) { +// const fullPath = path.join(location, item.name); +// const childrenPaths = getFoldersRecursively(fullPath); +// paths = [ +// ...paths, +// fullPath, +// ...childrenPaths, +// ]; +// } +// } +// return paths; +// } + +// // const dbTypeOrm= new DatabaseConnector_TypeOrm(); + +// // export const Source = dbTypeOrm._source; + +// const Source = DatabaseConnector_TypeOrm._source; + +// export { DatabaseConnector as DBConnector, Source }; + +/* eslint-disable @typescript-eslint/no-unused-vars */ +import "reflect-metadata"; +import { Config } from "../../../common/database.utils/database.config"; +import { Logger } from "../../../startup/logger"; +import { DataSource } from "typeorm"; +import path from "path"; +import fs from 'fs'; +import { User } from "./models/user/user.model"; +import { IPrimaryDatabaseConnector } from "../../../database/database.connector.interface"; +import { FormTemplate } from "./models/form.template/form.template.model"; +import { FavoriteTemplate } from "./models/favorite.template/favorite.template.model"; +import { FormSection } from "./models/form.section/form.section.model"; +import { FormSubmission } from "./models/form.submission/form.submission.model"; +import { FormTemplateApproval } from "./models/form.template.approval/form.template.approval.model"; +import { InputUnitList } from "./models/input.unit.list/input.unit.list.model"; +import { Question } from "./models/question/question.model"; +import { QuestionResponse } from "./models/question.response/question.response.model"; +import { TemplateFolder } from "./models/template.folder/template.folder.model"; + +/////////////////////////////////////////////////////////////////////////////////// + + Logger.instance().log(`environment : ${process.env.NODE_ENV}`); + Logger.instance().log(`db name : ${Config.database}`); + Logger.instance().log(`db username : ${Config.username}`); + Logger.instance().log(`db host : ${Config.host}`); + +/////////////////////////////////////////////////////////////////////////////////// + +class DatabaseConnector implements IPrimaryDatabaseConnector { + + static _source = new DataSource({ + name : Config.dialect, + type : Config.dialect, + host : Config.host, + port : Config.port, + username : Config.username, + password : Config.password, + database : Config.database, + synchronize : true, + entities : [ + FormTemplate, + FavoriteTemplate, + FormSection, + FormSubmission, + FormTemplateApproval, + InputUnitList, + Question, + QuestionResponse, + TemplateFolder, + User ], - synchronize: true, - logging: false, - poolSize: config.Pool?.Max || 10, - extra: { - connectionLimit: config.Pool?.Max || 10, - idleTimeout: config.Pool?.Idle || 10000, - acquireTimeout: config.Pool?.Acquire || 30000, - }, - }; - - this._source = new DataSource(options); - await this._source.initialize(); + migrations : [], + subscribers : [], + logger : Logger.instance(), + logging : true, + poolSize : Config.pool.max, + cache : true, + }); + + static getFoldersRecursively(location: string) { + const items = fs.readdirSync(location, { withFileTypes: true }); + let paths = []; + for (const item of items) { + if (item.isDirectory()) { + const fullPath = path.join(location, item.name); + const childrenPaths = this.getFoldersRecursively(fullPath); + paths = [ + ...paths, + fullPath, + ...childrenPaths, + ]; + } + } + return paths; + } - Logger.instance().log(`Connected to database '${config.DatabaseName}'.`); + public connect=async (): Promise => { + + DatabaseConnector.initialize(); - return true; - } catch (error) { - Logger.instance().log( - error instanceof Error - ? error.message - : "Unknown database connection error" - ); - return false; + return true; } - }; - // public db = (): Sequelize => { - // return this._sequelize; - // }; + static initialize = (): Promise => { + return new Promise((resolve, reject) => { + this._source + .initialize() + .then(() => { + Logger.instance().log('Database connection has been established successfully.'); + resolve(true); + }) + .catch(error => { + Logger.instance().log('Unable to connect to the database:' + error.message); + reject(false); + }); + }); - // public sync = async () => { - // try { - // await this._sequelize.sync({ alter: true }); - // return true; - // } catch (error) { - // Logger.instance().log(error.message); - // } - // return false; - // }; + }; - public sync = async () => { + + + public sync = async () => { try { - if (!this._source.isInitialized) { - await this._source.initialize(); + if (!DatabaseConnector._source.isInitialized) { + await DatabaseConnector._source.initialize(); } - await this._source.synchronize(); + await DatabaseConnector._source.synchronize(); return true; } catch (error) { @@ -194,12 +451,12 @@ export class DatabaseConnector_TypeOrm implements IPrimaryDatabaseConnector { // }; public migrate = async (): Promise => { try { - if (!this._source.isInitialized) { - await this._source.initialize(); + if (!DatabaseConnector._source.isInitialized) { + await DatabaseConnector._source.initialize(); } // Run pending migrations - const migrations = await this._source.runMigrations(); + const migrations = await DatabaseConnector._source.runMigrations(); if (migrations.length > 0) { Logger.instance().log("Database migrated successfully!"); @@ -219,54 +476,10 @@ export class DatabaseConnector_TypeOrm implements IPrimaryDatabaseConnector { } }; -// public executeQuery = async (query: string): Promise => { -// try { -// const result = await this._sequelize.query(query); -// return result; -// } catch (error) { -// Logger.instance().log(error.message); -// Logger.instance().log(`Error trace: ${error.stack}`); -// } -// return null; -// }; - - public executeQuery = async (query: string): Promise => { - try { - if (!this._source.isInitialized) { - await this._source.initialize(); - } - - // For SELECT queries that return data - const result = await this._source.query(query); - return result; - - } catch (error) { - Logger.instance().log(error instanceof Error ? error.message : 'Query execution error'); - Logger.instance().log(`Error trace: ${error instanceof Error ? error.stack : 'No stack trace'}`); - throw error; // Consider throwing instead of returning null - } -}; } -/////////////////////////////////////////////////////////////////////////////////////////// - -function getFoldersRecursively(location: string) { - const items = fs.readdirSync(location, { withFileTypes: true }); - let paths = []; - for (const item of items) { - if (item.isDirectory()) { - const fullPath = path.join(location, item.name); - const childrenPaths = getFoldersRecursively(fullPath); - paths = [ - ...paths, - fullPath, - ...childrenPaths, - ]; - } - } - return paths; -} +/////////////////////////////////////////////////////////////////////////////////// -const dbTypeOrm= new DatabaseConnector_TypeOrm(); +const Source = DatabaseConnector._source; -export const Source = dbTypeOrm._source; +export { DatabaseConnector as DBConnector, Source }; diff --git a/src/database/sql/typeorm/mappers/form.section.mapper.ts b/src/database/sql/typeorm/mappers/form.section.mapper.ts index 459c919..eee5e95 100644 --- a/src/database/sql/typeorm/mappers/form.section.mapper.ts +++ b/src/database/sql/typeorm/mappers/form.section.mapper.ts @@ -7,18 +7,20 @@ export class FormSectionMapper { } const dto: FormSectionResponseDto = { + // id: record.id, + // ParentFormTemplate: { + // id: record.ParentFormTemplate.id, + // Title: record.ParentFormTemplate.Title, + // Description: record.ParentFormTemplate.Description, + // CurrentVersion: record.ParentFormTemplate.CurrentVersion, + // Type: record.ParentFormTemplate.Type, + // DisplayCode: record.ParentFormTemplate.DisplayCode, + // OwnerUserId: record.ParentFormTemplate.OwnerUserId, + // RootSectionId: record.ParentFormTemplate.RootSectionId, + // DefaultSectionNumbering: record.ParentFormTemplate.DefaultSectionNumbering + // }, id: record.id, - ParentFormTemplate: { - id: record.ParentFormTemplate.id, - Title: record.ParentFormTemplate.Title, - Description: record.ParentFormTemplate.Description, - CurrentVersion: record.ParentFormTemplate.CurrentVersion, - Type: record.ParentFormTemplate.Type, - DisplayCode: record.ParentFormTemplate.DisplayCode, - OwnerUserId: record.ParentFormTemplate.OwnerUserId, - RootSectionId: record.ParentFormTemplate.RootSectionId, - DefaultSectionNumbering: record.ParentFormTemplate.DefaultSectionNumbering - }, + FormTemplateId: record.FormTemplateId, SectionIdentifier: record.SectionIdentifier, Title: record.Title, Description: record.Description, diff --git a/src/database/sql/typeorm/mappers/form.submission.mapper.ts b/src/database/sql/typeorm/mappers/form.submission.mapper.ts index 0fd4ee9..116ae36 100644 --- a/src/database/sql/typeorm/mappers/form.submission.mapper.ts +++ b/src/database/sql/typeorm/mappers/form.submission.mapper.ts @@ -9,6 +9,7 @@ export class FormMapper { const dto: FormSubmissionDto = { id: record.id, FormTemplateId: record.FormTemplateId, + Title: record.Title, UserId: record.UserId, Encrypted: record.Encrypted, Unencrypted: record.Unencrypted, diff --git a/src/database/sql/typeorm/mappers/question.response.mapper.ts b/src/database/sql/typeorm/mappers/question.response.mapper.ts index 3f8656c..ddbf89d 100644 --- a/src/database/sql/typeorm/mappers/question.response.mapper.ts +++ b/src/database/sql/typeorm/mappers/question.response.mapper.ts @@ -8,29 +8,29 @@ export class ResponseMapper { const dto: QuestionResponseResponseDto = { id : record.id, - FormSubmission: { - id : record.FormSubmission.id, - TemplateId : record.FormSubmission.TemplateId, - FormUrl : record.FormSubmission.FormUrl, - UserId : record.FormSubmission.UserId, - Status : record.FormSubmission.Status, - SubmissionTimestamp: record.FormSubmission.SubmissionTimestamp, - CreatedAt : record.FormSubmission.CreatedAt - }, - Question: { - id : record.Question.id, - Title : record.Question.Title, - Description : record.Question.Description, - DisplayCode : record.Question.DisplayCode, - ResponseType : record.Question.ResponseType, - Score : record.Question.Score, - CorrectAnswer: record.Question.CorrectAnswer, - Hint : record.Question.Hint, - TemplateId : record.Question.TemplateId, - SectionId : record.Question.SectionId, - CreatedAt : record.Question.CreatedAt, - UpdatedAt : record.Question.UpdatedAt - }, + // FormSubmission: { + // id : record.FormSubmission.id, + // TemplateId : record.FormSubmission.TemplateId, + // FormUrl : record.FormSubmission.FormUrl, + // UserId : record.FormSubmission.UserId, + // Status : record.FormSubmission.Status, + // SubmissionTimestamp: record.FormSubmission.SubmissionTimestamp, + // CreatedAt : record.FormSubmission.CreatedAt + // }, + // Question: { + // id : record.Question.id, + // Title : record.Question.Title, + // Description : record.Question.Description, + // DisplayCode : record.Question.DisplayCode, + // ResponseType : record.Question.ResponseType, + // Score : record.Question.Score, + // CorrectAnswer: record.Question.CorrectAnswer, + // Hint : record.Question.Hint, + // TemplateId : record.Question.TemplateId, + // SectionId : record.Question.SectionId, + // CreatedAt : record.Question.CreatedAt, + // UpdatedAt : record.Question.UpdatedAt + // }, ResponseType : record.ResponseType, IntegerValue : record.IntegerValue, FloatValue : record.FloatValue, diff --git a/src/database/sql/typeorm/models/base.entity.ts b/src/database/sql/typeorm/models/base.entity.ts index c66dd17..fdf0f46 100644 --- a/src/database/sql/typeorm/models/base.entity.ts +++ b/src/database/sql/typeorm/models/base.entity.ts @@ -1,6 +1,6 @@ -import { PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm'; +import { BaseEntity as TypeOrmBaseEntity, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm'; -export abstract class BaseEntity { +export abstract class BaseEntity extends TypeOrmBaseEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/src/database/sql/typeorm/models/form.section/form.section.model.ts b/src/database/sql/typeorm/models/form.section/form.section.model.ts index 8103224..7842ebd 100644 --- a/src/database/sql/typeorm/models/form.section/form.section.model.ts +++ b/src/database/sql/typeorm/models/form.section/form.section.model.ts @@ -26,7 +26,7 @@ export class FormSection extends BaseEntity { Description?: string; //Represent sequence within parent section - @Column({type: 'number', default: 0, nullable: false }) + @Column({type: 'int', default: 0, nullable: false }) Sequence?: number; // @Column({ type : 'enum', enum: SectionNodeType, nullable: false }) diff --git a/src/database/sql/typeorm/models/form.submission/form.submission.model.ts b/src/database/sql/typeorm/models/form.submission/form.submission.model.ts index dcd9474..b913762 100644 --- a/src/database/sql/typeorm/models/form.submission/form.submission.model.ts +++ b/src/database/sql/typeorm/models/form.submission/form.submission.model.ts @@ -18,7 +18,7 @@ export class FormSubmission extends BaseEntity { FormTemplateId: string; @Column({type: 'varchar', length: 128, nullable: false }) - Title: string; + Title?: string; @Column({ type: 'varchar', length: 128, default: 'General', nullable: false }) Type: string; @@ -29,7 +29,10 @@ export class FormSubmission extends BaseEntity { @Column({type: 'uuid', nullable: true }) UserId?: string; - @Column({type: 'json', length: 2048, nullable: true }) + // @Column({type: 'json', length: 2048, nullable: true }) + // UserMetaData?: string; + + @Column({type: 'json', nullable: true }) UserMetaData?: string; @Column({type: 'varchar', length: 1024, nullable: true }) @@ -53,7 +56,7 @@ export class FormSubmission extends BaseEntity { @Column({type: 'timestamp', nullable: true }) SubmittedAt?: Date; - @Column({type: 'number', nullable: true }) + @Column({type: 'int', nullable: true }) Score?: number; @ManyToOne(() => FormTemplate, (template) => template.FormSubmissions) diff --git a/src/database/sql/typeorm/models/form.template/form.template.model.ts b/src/database/sql/typeorm/models/form.template/form.template.model.ts index 3f39437..0364be3 100644 --- a/src/database/sql/typeorm/models/form.template/form.template.model.ts +++ b/src/database/sql/typeorm/models/form.template/form.template.model.ts @@ -18,7 +18,7 @@ export enum NavigationStrategy { Paginated = 'Paginated' } -@Entity('form_templates') +@Entity({name: 'form_templates'}) export class FormTemplate extends BaseEntity { @Column({type: 'uuid', nullable: true }) TenantId?: string; @@ -32,6 +32,9 @@ export class FormTemplate extends BaseEntity { @Column({ type: 'varchar', length: 128, default: 'Survey', nullable: false }) Type: string; + @Column({type: 'uuid', nullable: true }) + OwnerUserId?: string; + @Column({type: 'varchar', length: 128, nullable: false }) Title: string; diff --git a/src/database/sql/typeorm/models/question.response/question.response.model.ts b/src/database/sql/typeorm/models/question.response/question.response.model.ts index c0eae92..bfbab64 100644 --- a/src/database/sql/typeorm/models/question.response/question.response.model.ts +++ b/src/database/sql/typeorm/models/question.response/question.response.model.ts @@ -1,7 +1,8 @@ import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseEntity } from '../base.entity'; import { FormSubmission } from '../form.submission/form.submission.model'; -import { QueryResponseType, Question } from '../question/question.model'; +import { QueryResponseType } from '../../../../../domain.types/forms/query.response.types'; +import { Question } from '../question/question.model'; @Entity('question_responses') @@ -15,10 +16,10 @@ export class QuestionResponse extends BaseEntity { @Column({ type: 'enum', enum: QueryResponseType, nullable: false, default: QueryResponseType.SingleChoiceSelection }) ResponseType: QueryResponseType; - @Column({type: 'number', nullable: true }) + @Column({type: 'int', nullable: true }) Sequence: number; - @Column({type: 'number', nullable: true }) + @Column({type: 'int', nullable: true }) IntegerValue?: number; @Column({ nullable: true, type: 'float' }) @@ -43,7 +44,10 @@ export class QuestionResponse extends BaseEntity { @Column({type: 'text', nullable: true }) UserResponse: string; - @Column() + // @Column() + // SubmissionTimestamp: Date; + + @Column({nullable: true}) SubmissionTimestamp: Date; @Column() diff --git a/src/database/sql/typeorm/models/question/question.model.ts b/src/database/sql/typeorm/models/question/question.model.ts index 3f70696..a45ecf5 100644 --- a/src/database/sql/typeorm/models/question/question.model.ts +++ b/src/database/sql/typeorm/models/question/question.model.ts @@ -4,24 +4,25 @@ import { FormSection } from '../form.section/form.section.model'; import { BaseEntity } from '../base.entity'; import { QuestionResponse } from '../question.response/question.response.model'; import { InputUnitList } from '../input.unit.list/input.unit.list.model'; - -export enum QueryResponseType { - Text = "Text", - Float = "Float", - Integer = "Integer", - Boolean = "Boolean", - Object = "Object", - TextArray = "TextArray", - SingleChoiceSelection = "SingleChoiceSelection", - MultiChoiceSelection = "MultiChoiceSelection", - File = "File", - Date = "Date", - DateTime = "DateTime", - Rating = "Rating", - Location = "Location", - Url = "Url", - Range = "Range" - } +import { QueryResponseType } from '../../../../../domain.types/forms/query.response.types'; + +// export enum QueryResponseType { +// Text = "Text", +// Float = "Float", +// Integer = "Integer", +// Boolean = "Boolean", +// Object = "Object", +// TextArray = "TextArray", +// SingleChoiceSelection = "SingleChoiceSelection", +// MultiChoiceSelection = "MultiChoiceSelection", +// File = "File", +// Date = "Date", +// DateTime = "DateTime", +// Rating = "Rating", +// Location = "Location", +// Url = "Url", +// Range = "Range" +// } @Entity({ name: 'questions' }) export class Question extends BaseEntity { @@ -44,10 +45,10 @@ export class Question extends BaseEntity { @Column({ type: 'enum', enum: QueryResponseType }) ResponseType: QueryResponseType; - @Column({type: 'number', nullable: true }) + @Column({type: 'int', nullable: true }) Score: number; - @Column({type: 'number', nullable: true }) + @Column({type: 'int', nullable: true }) Sequence: number; @Column({type: 'varchar', nullable: true }) @@ -65,10 +66,10 @@ export class Question extends BaseEntity { @Column({type: 'uuid', nullable: true }) ImageResourceId?: string; - @Column({type: 'number', nullable: true }) + @Column({type: 'int', nullable: true }) RangeMin: number; - @Column({type: 'number', nullable: true }) + @Column({type: 'int', nullable: true }) RangeMax: number; @Column({type: 'varchar', nullable: true }) diff --git a/src/database/sql/typeorm/models/user/user.model.ts b/src/database/sql/typeorm/models/user/user.model.ts index 0282347..727dbee 100644 --- a/src/database/sql/typeorm/models/user/user.model.ts +++ b/src/database/sql/typeorm/models/user/user.model.ts @@ -1,31 +1,41 @@ -import { Entity, Column, OneToMany } from 'typeorm'; -import { BaseEntity } from '../base.entity'; -import { FormTemplate } from '../form.template/form.template.model'; +import { Entity, Column, OneToMany, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm'; -@Entity('users') -export class User extends BaseEntity { - @Column() - FirstName: string; +@Entity({ name: 'users' }) +export class User { + @PrimaryGeneratedColumn("uuid") + id: string; - @Column() - LastName: string; + @CreateDateColumn() + CreatedAt: Date; - @Column() - CountryCode: number; + @UpdateDateColumn() + UpdatedAt?: Date; - @Column() - Phone: string; + @DeleteDateColumn() + DeletedAt?: Date; - @Column() - Email: string; + @Column() + FirstName: string; - @Column() - Username: string; + @Column() + LastName: string; - @Column() - Password: string; + @Column() + CountryCode: number; - // @OneToMany(() => FormTemplate, (template) => template.User) - // Templates: FormTemplate[]; + @Column() + Phone: string; + + @Column() + Email: string; + + @Column() + Username: string; + + @Column() + Password: string; + + // @OneToMany(() => FormTemplate, (template) => template.User) + // Templates: FormTemplate[]; } diff --git a/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts b/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts index a54841c..b547111 100644 --- a/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts +++ b/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts @@ -5,14 +5,14 @@ import { FormSectionMapper } from "../../mappers/form.section.mapper"; import { Source } from "../../database.connector.typeorm"; import { ErrorHandler } from "../../../../../common/handlers/error.handler"; import { Logger } from "../../../../../common/logger"; -import { FindManyOptions } from "typeorm"; +import { FindManyOptions, Repository } from "typeorm"; import { BaseRepo } from "../base.repo"; export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ - _formSectionRepo = Source.getRepository(FormSection); + _formSectionRepo : Repository = Source.getRepository(FormSection); create= async(model: FormSectionCreateModel): Promise =>{ @@ -34,10 +34,11 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ // connect: { id: model.ParentFormTemplateId } // }, // SectionIdentifier: model.SectionIdentifier, + FormTemplateId: model.ParentFormTemplateId, Title: model.Title, Description: model.Description, DisplayCode: model.DisplayCode, - Sequence: parseInt(model.Sequence), + Sequence: model.Sequence, ParentSectionId: model.ParentSectionId, }); const record = await this._formSectionRepo.save(data); diff --git a/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts b/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts index 723f2bb..16d55f2 100644 --- a/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts +++ b/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts @@ -24,7 +24,8 @@ export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo{ // FormTemplate: { // connect: { id: model.FormTemplateId } // }, - + FormTemplateId: model.FormTemplateId, + Title: model.Title, UserId: model.UserId, Status: model.Status, ValidTill: model.ValidTill, diff --git a/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts b/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts index 59d34cd..90b8fd4 100644 --- a/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts +++ b/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts @@ -10,46 +10,52 @@ import { FormTemplateMapper } from "../../mappers/form.template.mapper"; import { ErrorHandler } from "../../../../../common/handlers/error.handler"; import { Logger } from "../../../../../common/logger"; import { BaseRepo } from "../base.repo"; -import { FindManyOptions, IsNull } from "typeorm"; +import { FindManyOptions, IsNull, Repository } from "typeorm"; import { Question } from "../../models/question/question.model"; import { QuestionMapper } from "../../mappers/question.mapper"; import { FormSection } from "../../models/form.section/form.section.model"; export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { - _formTemplateRepo = Source.getRepository(FormTemplate); - _question = Source.getRepository(Question); + _formTemplateRepo : Repository = Source.getRepository(FormTemplate); - _formSection = Source.getRepository(FormSection); + _question: Repository = Source.getRepository(Question); - create = async ( - model: FormTemplateCreateModel - ): Promise => { + _formSection : Repository = Source.getRepository(FormSection); + + create = async ( model: FormTemplateCreateModel): Promise => { + + Logger.instance().log( + `${FormTemplate}` + ); + + Logger.instance().log( + `${FormType}` + ); + try { - const data = await this._formTemplateRepo.create({ - // FormTemplate: { - // connect: { id: model.ParentFormTemplateId } - // }, - // SectionIdentifier: model.SectionIdentifier, - // FormTemplate: { - // connect: { id: model.FormTemplateId } - // }, - Title: model.Title, - Description: model.Description, - // CurrentVersion: model.CurrentVersion, - // TenantCode: model.TenantCode, - Type: model.Type as FormType, - // ItemsPerPage: model.ItemsPerPage, - DisplayCode: model.DisplayCode, - // OwnerUserId: model.OwnerUserId, - RootSectionId: model.RootSectionId, - DefaultSectionNumbering: model.DefaultSectionNumbering, - }); - const record = await this._formTemplateRepo.save(data); - return FormTemplateMapper.toDto(record); - } catch (error) { - ErrorHandler.throwInternalServerError(error.message, 500); - } + + const data = this._formTemplateRepo.create({ + Title: model.Title, + Description: model.Description, + // CurrentVersion: model.CurrentVersion, + // TenantCode: model.TenantCode, + Type: model.Type as FormType, + // ItemsPerPage: model.ItemsPerPage, + DisplayCode: model.DisplayCode, + // OwnerUserId: model.OwnerUserId, + RootSectionId: model.RootSectionId, + DefaultSectionNumbering: model.DefaultSectionNumbering, + }); + + const record = await this._formTemplateRepo.save(data); + return FormTemplateMapper.toDto(record); + } + + catch (error) + { + ErrorHandler.throwInternalServerError(error.message, 500); + } }; update = async ( diff --git a/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts b/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts index 800f135..3e2c320 100644 --- a/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts +++ b/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts @@ -1,5 +1,5 @@ import { QuestionResponseCreateModel } from "../../../../../domain.types/forms/response.domain.types"; -import { QueryResponseType } from "../../models/question/question.model"; +import { QueryResponseType } from "../../../../../domain.types/forms/query.response.types"; import { IResponseRepo } from "../../../../repository.interfaces/question.response/question.response.repo.interface"; import { QuestionResponseUpdateModel } from "../../../../../domain.types/forms/response.domain.types"; import { QuestionResponseResponseDto } from "../../../../../domain.types/forms/response.domain.types"; @@ -28,6 +28,8 @@ export class ResponseRepo extends BaseRepo implements IResponseRepo{ create=async (model: QuestionResponseCreateModel) : Promise => { try { const data = await this._responseRepo.create({ + FormSubmissionId: model.FormSubmissionId, + QuestionId: model.QuestionId, ResponseType: model.ResponseType as QueryResponseType, IntegerValue: model.IntegerValue, FloatValue: model.FloatValue, diff --git a/src/database/sql/typeorm/repositories/question/question.repo.ts b/src/database/sql/typeorm/repositories/question/question.repo.ts index 2dc9055..e60b2a6 100644 --- a/src/database/sql/typeorm/repositories/question/question.repo.ts +++ b/src/database/sql/typeorm/repositories/question/question.repo.ts @@ -10,7 +10,7 @@ import { Logger } from "../../../../../common/logger"; import { ErrorHandler } from "../../../../../common/handlers/error.handler"; import { FindManyOptions } from "typeorm"; import { BaseRepo } from "../base.repo"; -import { QueryResponseType } from "../../models/question/question.model"; +import { QueryResponseType } from "../../../../../domain.types/forms/query.response.types"; export class QuestionRepo extends BaseRepo implements IQuestionRepo{ @@ -35,6 +35,8 @@ export class QuestionRepo extends BaseRepo implements IQuestionRepo{ const data = await this._questionRepo.create({ Title: model.Title, Description: model.Description, + TemplateId: model.ParentTemplateId, + ParentSectionId: model.ParentSectionId, DisplayCode: model.DisplayCode, ResponseType: model.ResponseType as QueryResponseType, Score: model.Score, @@ -224,7 +226,7 @@ export class QuestionRepo extends BaseRepo implements IQuestionRepo{ } if (filters.parentTemplateId) { - search.where['ParentTemplateId'] = filters.parentTemplateId + search.where['TemplateId'] = filters.parentTemplateId } if (filters.parentSectionId) { diff --git a/src/database/sql/typeorm/repositories/user/user.repo.ts b/src/database/sql/typeorm/repositories/user/user.repo.ts index 9ac5fab..6afba8a 100644 --- a/src/database/sql/typeorm/repositories/user/user.repo.ts +++ b/src/database/sql/typeorm/repositories/user/user.repo.ts @@ -8,12 +8,12 @@ import { User } from "../../models/user/user.model"; import { UserMapper } from "../../mappers/user.mapper"; import { Logger } from "../../../../../common/logger"; import { ErrorHandler } from "../../../../../common/handlers/error.handler"; -import { FindManyOptions } from "typeorm"; +import { FindManyOptions, Repository } from "typeorm"; import { BaseRepo } from "../base.repo"; export class UserRepo extends BaseRepo implements IUserRepo{ - _userRepo = Source.getRepository(User); + _userRepo : Repository = Source.getRepository(User); allUsers = async () => { const response = await this._userRepo.find({ @@ -27,7 +27,7 @@ export class UserRepo extends BaseRepo implements IUserRepo{ create=async (model: UserCreateModel) : Promise => { try { - const data = await this._userRepo.create({ + const data = this._userRepo.create({ FirstName: model.FirstName, LastName: model.LastName, CountryCode: model.CountryCode, diff --git a/src/database/sql/typeorm/typeorm.injector.ts b/src/database/sql/typeorm/typeorm.injector.ts index f343875..4f8fc53 100644 --- a/src/database/sql/typeorm/typeorm.injector.ts +++ b/src/database/sql/typeorm/typeorm.injector.ts @@ -1,6 +1,6 @@ import 'reflect-metadata'; import { DependencyContainer } from 'tsyringe'; -import { DatabaseConnector_TypeOrm } from './database.connector.typeorm'; +// import { DatabaseConnector_TypeOrm } from './database.connector.typeorm'; import { FormSectionRepo } from './repositories/form.section/form.section.repo'; import { FormSubmissionRepo } from './repositories/form.submission/form.submission.repo'; import { FormTemplateRepo } from './repositories/form.template/form.template.repo'; @@ -16,7 +16,7 @@ export class TypeOrmInjector { static registerInjections(container: DependencyContainer) { - container.register('IPrimaryDatabaseConnector', DatabaseConnector_TypeOrm); + // container.register('IPrimaryDatabaseConnector', DatabaseConnector_TypeOrm); container.register('IFormSectionRepo', FormSectionRepo); container.register('IFormSubmissionRepo', FormSubmissionRepo); diff --git a/src/domain.types/forms/form.section.domain.types.ts b/src/domain.types/forms/form.section.domain.types.ts index 2262644..7df732a 100644 --- a/src/domain.types/forms/form.section.domain.types.ts +++ b/src/domain.types/forms/form.section.domain.types.ts @@ -6,7 +6,8 @@ export interface FormSectionCreateModel { Title?: string; Description?: string; DisplayCode: string; - Sequence?: string; + // Sequence?: string; + Sequence?: number; ParentSectionId?: string; } @@ -21,17 +22,18 @@ export interface FormSectionUpdateModel { export interface FormSectionResponseDto { id: string; - ParentFormTemplate?: { - id: string; - Title: string; - Description: string; - CurrentVersion: number; - Type: string; - DisplayCode: string; - OwnerUserId: string; - RootSectionId: string; - DefaultSectionNumbering: Boolean - } + FormTemplateId: string; + // ParentFormTemplate?: { + // id: string; + // Title: string; + // Description: string; + // CurrentVersion: number; + // Type: string; + // DisplayCode: string; + // OwnerUserId: string; + // RootSectionId: string; + // DefaultSectionNumbering: Boolean + // } SectionIdentifier: string; Title: string; Description: string; diff --git a/src/domain.types/forms/form.submission.domain.types.ts b/src/domain.types/forms/form.submission.domain.types.ts index a38746b..5b548a3 100644 --- a/src/domain.types/forms/form.submission.domain.types.ts +++ b/src/domain.types/forms/form.submission.domain.types.ts @@ -4,6 +4,7 @@ import { FormStatus } from "../../database/sql/typeorm/models/form.submission/fo export interface FormSubmissionCreateModel { FormTemplateId: string; UserId?: string; + Title?: string; Encrypted? : string; Unencrypted? : string; Link? : string; @@ -23,6 +24,7 @@ export interface LinkQueryParams { export interface FormSubmissionUpdateModel { UserId?: string; + FormTemplateId?: string; Encrypted? : string; Unencrypted? : string; Link? : string; @@ -38,6 +40,7 @@ export interface FormSubmissionDto { id?: string; FormTemplateId?: string; UserId?: string; + Title?: string; Encrypted? : string; Unencrypted? : string; Link? : string; diff --git a/src/domain.types/forms/query.response.types.ts b/src/domain.types/forms/query.response.types.ts new file mode 100644 index 0000000..c34e553 --- /dev/null +++ b/src/domain.types/forms/query.response.types.ts @@ -0,0 +1,17 @@ +export enum QueryResponseType { + Text = "Text", + Float = "Float", + Integer = "Integer", + Boolean = "Boolean", + Object = "Object", + TextArray = "TextArray", + SingleChoiceSelection = "SingleChoiceSelection", + MultiChoiceSelection = "MultiChoiceSelection", + File = "File", + Date = "Date", + DateTime = "DateTime", + Rating = "Rating", + Location = "Location", + Url = "Url", + Range = "Range" + } \ No newline at end of file diff --git a/src/domain.types/forms/question.domain.types.ts b/src/domain.types/forms/question.domain.types.ts index 9e3aac2..2e2ee7a 100644 --- a/src/domain.types/forms/question.domain.types.ts +++ b/src/domain.types/forms/question.domain.types.ts @@ -1,7 +1,7 @@ // import { QueryResponseType } from "../miscellaneous/system.types"; // import { QueryResponseType } from "@prisma/client"; -import { QueryResponseType } from "../../database/sql/typeorm/models/question/question.model"; +import { QueryResponseType } from "./query.response.types"; import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; // export interface QuestionOption { diff --git a/src/domain.types/forms/response.domain.types.ts b/src/domain.types/forms/response.domain.types.ts index 181ab31..b881446 100644 --- a/src/domain.types/forms/response.domain.types.ts +++ b/src/domain.types/forms/response.domain.types.ts @@ -1,7 +1,7 @@ // import { FormStatus, QueryResponseType } from "../miscellaneous/system.types" -import { FormStatus} from "@prisma/client"; -import { QueryResponseType } from "../../database/sql/typeorm/models/question/question.model"; +import { FormStatus } from "../../database/sql/typeorm/models/form.submission/form.submission.model"; +import { QueryResponseType } from "./query.response.types"; import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; export interface QuestionResponseCreateModel { diff --git a/src/domain.types/miscellaneous/system.types.ts b/src/domain.types/miscellaneous/system.types.ts index 823d52d..d29874b 100644 --- a/src/domain.types/miscellaneous/system.types.ts +++ b/src/domain.types/miscellaneous/system.types.ts @@ -1,5 +1,7 @@ export type uuid = string | undefined | null; -export type DatabaseDialect = 'postgres' | 'mysql' | 'sqlite'; +// export type DatabaseDialect = 'postgres' | 'mysql' | 'sqlite'; + +export type DatabaseDialect = 'mysql'; export type integer = number | undefined | null; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 14a6e10..d551aab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ dotenv.config(); (async () => { const app = Application.instance(); - app.start(); + await app.start(); })(); diff --git a/src/services/form.template/form.template.service.ts b/src/services/form.template/form.template.service.ts index 93549fe..6f86209 100644 --- a/src/services/form.template/form.template.service.ts +++ b/src/services/form.template/form.template.service.ts @@ -1,5 +1,5 @@ -import { FormType, ItemsPerPage, Prisma, PrismaClient } from "@prisma/client"; -import { PrismaClientInit } from "../../startup/prisma.client.init"; +// import { FormType, ItemsPerPage, Prisma, PrismaClient } from "@prisma/client"; +// import { PrismaClientInit } from "../../startup/prisma.client.init"; import { ExportFormTemplateDto, FormTemplateCreateModel, diff --git a/src/startup/loader.ts b/src/startup/loader.ts index 1d7f18b..e766319 100644 --- a/src/startup/loader.ts +++ b/src/startup/loader.ts @@ -52,7 +52,7 @@ export class Loader { // Loader._seeder = container.resolve(Seeder); - const ehrEnabled = ConfigurationManager.EhrEnabled(); + // const ehrEnabled = ConfigurationManager.EhrEnabled(); // if (ehrEnabled) { // Loader._ehrStore = container.resolve(StorageService); // await Loader._ehrStore.init(); diff --git a/src/startup/router.ts b/src/startup/router.ts index e362792..28a56c0 100644 --- a/src/startup/router.ts +++ b/src/startup/router.ts @@ -19,7 +19,7 @@ export class Router { return new Promise((resolve, reject) => { try { this._app.get("/api/v1", (req, res) => { - res.send({ message: `Form service is running successfully on port ${process.env.PORT}` }) + res.send({ message: `Form service is running successfully on port ${process.env.PORT}` }); }) form(this._app); formTemplate(this._app); @@ -27,8 +27,10 @@ export class Router { formSection(this._app); question(this._app); Response(this._app); + resolve(true); } catch (error) { console.log("Error initilizing the routes") + reject(false); } }); } From 693b3cf4dd92fb6077e7b98efa3bcfc886ba7a31 Mon Sep 17 00:00:00 2001 From: Shubham109879 Date: Sat, 21 Jun 2025 18:28:55 +0530 Subject: [PATCH 03/29] form_service_typeorm_user_search_fix_2025_06_21 --- Postman/form-service.postman_collection.json | 189 +++++++++++++----- .../forms-service.postman_environment.json | 63 ++++++ src/app.ts | 2 +- .../form.template/form.template.repo.ts | 9 +- .../typeorm/repositories/user/user.repo.ts | 20 +- 5 files changed, 213 insertions(+), 70 deletions(-) create mode 100644 Postman/forms-service.postman_environment.json diff --git a/Postman/form-service.postman_collection.json b/Postman/form-service.postman_collection.json index bf3be87..44c7059 100644 --- a/Postman/form-service.postman_collection.json +++ b/Postman/form-service.postman_collection.json @@ -1,10 +1,10 @@ { "info": { - "_postman_id": "bc9d63d1-c937-475d-acda-8aa4d240fbaa", + "_postman_id": "303c4b99-04f5-484e-a92b-79abd1651bbc", "name": "form-service", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "24347822", - "_collection_link": "https://inflectionzone-order-management-typescript.postman.co/workspace/assessment~6c10fd4f-9447-4878-a4d7-88df8016665b/collection/24347822-bc9d63d1-c937-475d-acda-8aa4d240fbaa?action=share&source=collection_link&creator=24347822" + "_exporter_id": "37785554", + "_collection_link": "https://sense-software.postman.co/workspace/sense-software-Workspace~4bf846a4-41d5-49ae-b537-9dcb3386e0ba/collection/37785554-303c4b99-04f5-484e-a92b-79abd1651bbc?action=share&source=collection_link&creator=37785554" }, "item": [ { @@ -210,7 +210,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/users/search?username=Username&password=Password", + "raw": "{{BASE_URL}}/users/search?username=\"Username\"&password=\"Password\"", "host": [ "{{BASE_URL}}" ], @@ -221,11 +221,11 @@ "query": [ { "key": "username", - "value": "Username" + "value": "\"Username\"" }, { "key": "password", - "value": "Password" + "value": "\"Password\"" } ] } @@ -270,6 +270,24 @@ } }, "response": [] + }, + { + "name": "Get All Users", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/users/all", + "host": [ + "{{BASE_URL}}" + ], + "path": [ + "users", + "all" + ] + } + }, + "response": [] } ] }, @@ -321,7 +339,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"Title\": \"Assessment First\",\r\n \"Description\": \"This is description\",\r\n \"CurrentVersion\": 1,\r\n \"Type\": \"Questionnaire\",\r\n // \"DisplayCode\": \"xyz1234\",\r\n \"OwnerUserId\": \"16377833-8e6f-41b4-944a-98a91815a4d5\",\r\n // \"RootSectionId\": \"9618c6a8-0555-4a14-95ec-1946ec09c8e0\",\r\n \"DefaultSectionNumbering\": true,\r\n \"ItemsPerPage\":\"OneQuestion\"\r\n}", + "raw": "{\r\n \"Title\": \"Assessment First\",\r\n \"Description\": \"This is description\",\r\n \"CurrentVersion\": 1,\r\n \"Type\": \"Questionnaire\",\r\n // \"DisplayCode\": \"xyz1234\",\r\n \"OwnerUserId\": \"ad693c12-3141-45e7-ab3a-59d03306757f\",\r\n // \"RootSectionId\": \"9618c6a8-0555-4a14-95ec-1946ec09c8e0\",\r\n \"DefaultSectionNumbering\": true,\r\n \"ItemsPerPage\":\"OneQuestion\"\r\n}", "options": { "raw": { "language": "json" @@ -435,7 +453,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"Title\": \"Work Load Capacity of company\",\r\n \"Description\": \"This is description\",\r\n \"CurrentVersion\": 1,\r\n \"Type\": \"Questionnaire\",\r\n \"DisplayCode\": \"xyz1234asssssdfghj\",\r\n \"OwnerUserId\": \"16377833-8e6f-41b4-944a-98a91815a4d5\",\r\n \"RootSectionId\": \"d04ca675-d4eb-4c6a-9820-6ba1cce3e625\",\r\n \"DefaultSectionNumbering\": true,\r\n \"ItemsPerPage\":\"OneQuestion\"\r\n}", + "raw": "{\r\n \"Title\": \"Work Load Capacity of company\",\r\n \"Description\": \"This is description\",\r\n \"CurrentVersion\": 1,\r\n \"Type\": \"Questionnaire\",\r\n \"DisplayCode\": \"xyz1234asssssdfghj\",\r\n \"OwnerUserId\": \"16377833-8e6f-41b4-944a-98a91815a4d5\",\r\n // \"RootSectionId\": \"d04ca675-d4eb-4c6a-9820-6ba1cce3e625\",\r\n \"DefaultSectionNumbering\": true,\r\n \"ItemsPerPage\":\"OneQuestion\"\r\n}", "options": { "raw": { "language": "json" @@ -482,7 +500,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/form-templates/search?id=210939f1-c6c4-4274-843d-72f44f77da65", + "raw": "{{BASE_URL}}/form-templates/search?id={{TEMPLATE_ID}}", "host": [ "{{BASE_URL}}" ], @@ -503,7 +521,7 @@ }, { "key": "id", - "value": "210939f1-c6c4-4274-843d-72f44f77da65" + "value": "{{TEMPLATE_ID}}" } ] } @@ -516,13 +534,13 @@ "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/form-templates/d71cc1a7-abdb-4196-bfef-19ba4e7cd1be/details", + "raw": "{{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/details", "host": [ "{{BASE_URL}}" ], "path": [ "form-templates", - "d71cc1a7-abdb-4196-bfef-19ba4e7cd1be", + "{{TEMPLATE_ID}}", "details" ] } @@ -567,6 +585,41 @@ } }, "response": [] + }, + { + "name": "Get Form Submissions", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/submissions", + "host": [ + "{{BASE_URL}}" + ], + "path": [ + "form-templates", + "{{TEMPLATE_ID}}", + "submissions" + ] + } + }, + "response": [] + }, + { + "name": "Export Form Template", + "request": { + "method": "GET", + "header": [] + }, + "response": [] + }, + { + "name": "Preview Form Template", + "request": { + "method": "GET", + "header": [] + }, + "response": [] } ] }, @@ -617,7 +670,7 @@ "header": [], "body": { "mode": "raw", - "raw": "// {\r\n// \"ParentFormTemplateId\": \"64888ed6-7452-48ad-8f4e-48448f21259f\", //Form template id\r\n// \"SectionIdentifier\": \"SectionIdentifier\",\r\n// \"Title\": \"Title.................\",\r\n// \"Description\": \"Description\",\r\n// // \"DisplayCode\": \"DisplayCode\",\r\n// \"Sequence\": \"Sequence three\",\r\n// \"ParentSectionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\" //any uuid value\r\n// }\r\n{\r\n \"ParentFormTemplateId\": \"b78158bd-7496-48f1-b625-2c489b9c65a9\",\r\n // \"SectionIdentifier\": \"new form section 1\"\r\n // \"Title\": \"New form section \",\r\n // \"Description\": \"sf esdrg\",\r\n // \"Sequence\":\"abc\"\r\n \"ParentSectionId\": \"d04ca675-d4eb-4c6a-9820-6ba1cce3e625\"\r\n}", + "raw": "// {\r\n// \"ParentFormTemplateId\": \"64888ed6-7452-48ad-8f4e-48448f21259f\", //Form template id\r\n// \"SectionIdentifier\": \"SectionIdentifier\",\r\n// \"Title\": \"Title.................\",\r\n// \"Description\": \"Description\",\r\n// // \"DisplayCode\": \"DisplayCode\",\r\n// \"Sequence\": \"Sequence three\",\r\n// \"ParentSectionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\" //any uuid value\r\n// }\r\n{\r\n \"ParentFormTemplateId\": \"{{TEMPLATE_ID}}\",\r\n // \"SectionIdentifier\": \"new form section 1\"\r\n \"Title\": \"New form section \",\r\n \"Description\": \"sf esdrg\",\r\n \"Sequence\": 12,\r\n \"ParentSectionId\": \"{{PARENT_SECTION_ID}}\"\r\n}", "options": { "raw": { "language": "json" @@ -730,7 +783,7 @@ "header": [], "body": { "mode": "raw", - "raw": "// {\r\n// // \"TemplateId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"SectionIdentifier\": \"SectionIdentifier\",\r\n// \"Title\": \"Title section \",\r\n// \"Description\": \"Description\",\r\n// \"DisplayCode\": \"DisplayCode\",\r\n// \"Sequence\": \"Sequence three\",\r\n// \"ParentSectionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\"\r\n// }\r\n{\r\n \"SectionIdentifier\": \"new form section\",\r\n \"Title\": \"first section\",\r\n \"Description\": \"description\",\r\n // \"Sequence\": \"1\",\r\n \"ParentSectionId\": \"6eb7e68d-608b-4076-8a9d-bbc467ab9211\"\r\n}", + "raw": "// {\r\n// // \"TemplateId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"SectionIdentifier\": \"SectionIdentifier\",\r\n// \"Title\": \"Title section \",\r\n// \"Description\": \"Description\",\r\n// \"DisplayCode\": \"DisplayCode\",\r\n// \"Sequence\": \"Sequence three\",\r\n// \"ParentSectionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\"\r\n// }\r\n{\r\n \"SectionIdentifier\": \"new form section\",\r\n \"Title\": \"first section\",\r\n \"Description\": \"description\",\r\n // \"Sequence\": \"1\",\r\n \"ParentSectionId\": \"{{PARENT_SECTION_ID}}\"\r\n}", "options": { "raw": { "language": "json" @@ -777,7 +830,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/form-sections/search?parentFormTemplateId=51688651-458d-4b8f-922e-28ce072cfa15", + "raw": "{{BASE_URL}}/form-sections/search?parentFormTemplateId={{TEMPLATE_ID}}", "host": [ "{{BASE_URL}}" ], @@ -788,7 +841,7 @@ "query": [ { "key": "parentFormTemplateId", - "value": "51688651-458d-4b8f-922e-28ce072cfa15" + "value": "{{TEMPLATE_ID}}" } ] } @@ -833,6 +886,25 @@ } }, "response": [] + }, + { + "name": "Get form section by template id", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/form-sections/templateId/{{TEMPLATE_ID}}", + "host": [ + "{{BASE_URL}}" + ], + "path": [ + "form-sections", + "templateId", + "{{TEMPLATE_ID}}" + ] + } + }, + "response": [] } ] }, @@ -1080,8 +1152,11 @@ { "listen": "prerequest", "script": { - "exec": [], - "type": "text/javascript" + "exec": [ + "" + ], + "type": "text/javascript", + "packages": {} } } ], @@ -1090,7 +1165,7 @@ "header": [], "body": { "mode": "raw", - "raw": "// {\r\n// \"ParentTemplateId\": \"d9e0416b-b142-4492-a522-a7fc57f9c224\", //Form Template id\r\n// \"ParentSectionId\": \"36869bba-551f-4aaf-a150-ccafd9567254\", //Form Section Id\r\n// \"Title\": \"Question Two\",\r\n// \"Description\": \"Question Two description\",\r\n// \"DisplayCode\": \"Question Two Display code\",\r\n// \"ResponseType\": \"Integer\",\r\n// \"Score\": 11,\r\n// \"CorrectAnswer\": \"Question Two correct answer\",\r\n// \"Hint\": \"Question Two hint\",\r\n// \"Options\":\"option1,option2\",\r\n// \"QuestionImageUrl\":\"this.is.image\",\r\n// \"RangeMin\":2,\r\n// \"RangeMax\":4\r\n// },\r\n// {\r\n// \"ParentTemplateId\": \"d9e0416b-b142-4492-a522-a7fc57f9c224\",\r\n// \"ParentSectionId\": \"36869bba-551f-4aaf-a150-ccafd9567254\",\r\n// \"Title\": \"range ........!\",\r\n// \"Description\": \"Birth Day\",\r\n// \"ResponseType\": \"Boolean\",\r\n// \"Score\": 5,\r\n// // \"DisplayCode\": \"2b3b3ea7-d55f-46fb-901f-380a92be0059\",\r\n// \"CorrectAnswer\": \"1234\",\r\n// \"Hint\": \"date\",\r\n// \"QuestionImageUrl\": \"a\",\r\n// \"RangeMin\": 1,\r\n// \"RangeMax\": 2,\r\n// \"Options\": \"option1, option2\"\r\n// }\r\n// {\r\n// \"ParentTemplateId\": \"5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a\",\r\n// \"ParentSectionId\": \"a1b31378-59b0-4ca8-8d54-323798bf924e\",\r\n// \"Title\": \"qqqqqqqqqqqqqqqq\",\r\n// \"Description\": \"qqqqqqqqq\",\r\n// \"ResponseType\": \"Text\",\r\n// \"Score\": 1,\r\n// \"CorrectAnswer\": \"qqqqqqqqqqqqqqq\",\r\n// \"Hint\": \"qqqqqqqqqqqqq\",\r\n// \"QuestionImageUrl\": \"qqqqqqqqqqqq\",\r\n// \"RangeMin\": null,\r\n// \"RangeMax\": null,\r\n// \"Options\": [\r\n// \"option1\",\r\n// \"option2\"\r\n// ]\r\n// }\r\n// {\r\n// \"ParentTemplateId\": \"5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a\",\r\n// \"ParentSectionId\": \"a1b31378-59b0-4ca8-8d54-323798bf924e\",\r\n// \"Title\": \"qqqqqqqqqqqqqqqq\",\r\n// \"Description\": \"qqqqqqqqqq\",\r\n// \"ResponseType\": \"SinglehoiceSelection\",\r\n// \"Score\": 1,\r\n// \"CorrectAnswer\": \"qqqqqqqqqqqqqqq\",\r\n// \"Hint\": \"qqqqqqqqqqqqq\",\r\n// \"QuestionImageUrl\": \"qqqqqqqqqqqq\",\r\n// \"RangeMin\": null,\r\n// \"RangeMax\": null,\r\n// \"Options\": [\r\n// {\r\n// \"Text\": \"qqqqq\",\r\n// \"Sequence\": 1\r\n// },\r\n// {\r\n// \"Text\": \"qqqq\",\r\n// \"Sequence\": 2\r\n// },\r\n// {\r\n// \"Text\": \"qq\",\r\n// \"Sequence\": 3\r\n// }\r\n// ]\r\n// }\r\n{\r\n \"ParentTemplateId\": \"4fabef36-5992-4f9c-aa7b-2539ae24aa7e\",\r\n \"ParentSectionId\": \"b8243963-7999-4623-b357-d1d16549d619\",\r\n \"ResponseType\": \"SingleChoiceSelection\",\r\n \"Options\": [\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"1\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"2\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"3\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"1\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"2\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"3\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n }\r\n ]\r\n}\r\n\r\n\r\n\r\n{\r\n \"Title\": \"What is your full name\",\r\n \"Description\": \"description\",\r\n \"ResponseType\": \"SingleChoiceSelection\",\r\n \"Options\": [\r\n {\r\n \"Text\": \"sdfg\",\r\n \"Sequence\": \"a1\",\r\n \"ImageUrl\": \"wererterytryur\"\r\n },\r\n {\r\n \"Text\": \"sdagdf\",\r\n \"Sequence\": \"a2\",\r\n \"ImageUrl\": \"asdf\"\r\n },\r\n {\r\n \"Text\": \"dzfg\",\r\n \"Sequence\": \"a3\",\r\n \"ImageUrl\": \"zsdfg\"\r\n },\r\n {\r\n \"Text\": \"zdfgdfg\",\r\n \"Sequence\": \"a4\",\r\n \"ImageUrl\": \"dfg\"\r\n }\r\n ]\r\n}", + "raw": "// {\r\n// \"ParentTemplateId\": \"d9e0416b-b142-4492-a522-a7fc57f9c224\", //Form Template id\r\n// \"ParentSectionId\": \"36869bba-551f-4aaf-a150-ccafd9567254\", //Form Section Id\r\n// \"Title\": \"Question Two\",\r\n// \"Description\": \"Question Two description\",\r\n// \"DisplayCode\": \"Question Two Display code\",\r\n// \"ResponseType\": \"Integer\",\r\n// \"Score\": 11,\r\n// \"CorrectAnswer\": \"Question Two correct answer\",\r\n// \"Hint\": \"Question Two hint\",\r\n// \"Options\":\"option1,option2\",\r\n// \"QuestionImageUrl\":\"this.is.image\",\r\n// \"RangeMin\":2,\r\n// \"RangeMax\":4\r\n// },\r\n// {\r\n// \"ParentTemplateId\": \"d9e0416b-b142-4492-a522-a7fc57f9c224\",\r\n// \"ParentSectionId\": \"36869bba-551f-4aaf-a150-ccafd9567254\",\r\n// \"Title\": \"range ........!\",\r\n// \"Description\": \"Birth Day\",\r\n// \"ResponseType\": \"Boolean\",\r\n// \"Score\": 5,\r\n// // \"DisplayCode\": \"2b3b3ea7-d55f-46fb-901f-380a92be0059\",\r\n// \"CorrectAnswer\": \"1234\",\r\n// \"Hint\": \"date\",\r\n// \"QuestionImageUrl\": \"a\",\r\n// \"RangeMin\": 1,\r\n// \"RangeMax\": 2,\r\n// \"Options\": \"option1, option2\"\r\n// }\r\n// {\r\n// \"ParentTemplateId\": \"5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a\",\r\n// \"ParentSectionId\": \"a1b31378-59b0-4ca8-8d54-323798bf924e\",\r\n// \"Title\": \"qqqqqqqqqqqqqqqq\",\r\n// \"Description\": \"qqqqqqqqq\",\r\n// \"ResponseType\": \"Text\",\r\n// \"Score\": 1,\r\n// \"CorrectAnswer\": \"qqqqqqqqqqqqqqq\",\r\n// \"Hint\": \"qqqqqqqqqqqqq\",\r\n// \"QuestionImageUrl\": \"qqqqqqqqqqqq\",\r\n// \"RangeMin\": null,\r\n// \"RangeMax\": null,\r\n// \"Options\": [\r\n// \"option1\",\r\n// \"option2\"\r\n// ]\r\n// }\r\n// {\r\n// \"ParentTemplateId\": \"5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a\",\r\n// \"ParentSectionId\": \"a1b31378-59b0-4ca8-8d54-323798bf924e\",\r\n// \"Title\": \"qqqqqqqqqqqqqqqq\",\r\n// \"Description\": \"qqqqqqqqqq\",\r\n// \"ResponseType\": \"SinglehoiceSelection\",\r\n// \"Score\": 1,\r\n// \"CorrectAnswer\": \"qqqqqqqqqqqqqqq\",\r\n// \"Hint\": \"qqqqqqqqqqqqq\",\r\n// \"QuestionImageUrl\": \"qqqqqqqqqqqq\",\r\n// \"RangeMin\": null,\r\n// \"RangeMax\": null,\r\n// \"Options\": [\r\n// {\r\n// \"Text\": \"qqqqq\",\r\n// \"Sequence\": 1\r\n// },\r\n// {\r\n// \"Text\": \"qqqq\",\r\n// \"Sequence\": 2\r\n// },\r\n// {\r\n// \"Text\": \"qq\",\r\n// \"Sequence\": 3\r\n// }\r\n// ]\r\n// }\r\n{\r\n \"ParentTemplateId\": \"{{TEMPLATE_ID}}\",\r\n \"ParentSectionId\": \"{{PARENT_SECTION_ID}}\",\r\n \"ResponseType\": \"SingleChoiceSelection\",\r\n \"Options\": [\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"1\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"2\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"3\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"1\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"2\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"3\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n }\r\n ]\r\n}\r\n\r\n\r\n\r\n// {\r\n// \"Title\": \"What is your full name\",\r\n// \"Description\": \"description\",\r\n// \"ResponseType\": \"SingleChoiceSelection\",\r\n// \"Options\": [\r\n// {\r\n// \"Text\": \"sdfg\",\r\n// \"Sequence\": \"a1\",\r\n// \"ImageUrl\": \"wererterytryur\"\r\n// },\r\n// {\r\n// \"Text\": \"sdagdf\",\r\n// \"Sequence\": \"a2\",\r\n// \"ImageUrl\": \"asdf\"\r\n// },\r\n// {\r\n// \"Text\": \"dzfg\",\r\n// \"Sequence\": \"a3\",\r\n// \"ImageUrl\": \"zsdfg\"\r\n// },\r\n// {\r\n// \"Text\": \"zdfgdfg\",\r\n// \"Sequence\": \"a4\",\r\n// \"ImageUrl\": \"dfg\"\r\n// }\r\n// ]\r\n// }", "options": { "raw": { "language": "json" @@ -1240,7 +1315,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"Title\": \"Current (referral) cardiac diagnosis/diagnoses?\",\r\n \"ResponseType\": \"MultiChoiceSelection\",\r\n \"RangeMin\": 1,\r\n \"RangeMax\": 2,\r\n \"Options\": [\r\n \"Acute Coronary Syndrome - STEMI,Acute Coronary Syndrome - NSTEMI,Acute Coronary Syndrome - Unstable Angina,Stable Coronary Artery Disease or Stable Angina,Heart Failure,Atrial Fibrillation/Flutter/SVT,Peripheral Arterial Disease,Percutaneous Cardiac Intervention (TAVI/ASD Repair/Valvotomies),Heart Valve replacement,High cardiovascular risk patient (High CVD risk score, Diabetes, Metabolic syndrome, Chronic Kidney Disease, Multiple risk factors),Other\"\r\n ]\r\n}", + "raw": "{\r\n \"Title\": \"Current (referral) cardiac diagnosis/diagnoses?\",\r\n \"ResponseType\": \"MultiChoiceSelection\",\r\n \"RangeMin\": 1,\r\n \"RangeMax\": 2,\r\n // \"Options\": [\r\n // \"Acute Coronary Syndrome - STEMI,Acute Coronary Syndrome - NSTEMI,Acute Coronary Syndrome - Unstable Angina,Stable Coronary Artery Disease or Stable Angina,Heart Failure,Atrial Fibrillation/Flutter/SVT,Peripheral Arterial Disease,Percutaneous Cardiac Intervention (TAVI/ASD Repair/Valvotomies),Heart Valve replacement,High cardiovascular risk patient (High CVD risk score, Diabetes, Metabolic syndrome, Chronic Kidney Disease, Multiple risk factors),Other\"\r\n // ]\r\n \"Options\": [\r\n {\r\n \"Text\": \"pppp\",\r\n \"Sequence\": \"1\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n }\r\n ]\r\n}", "options": { "raw": { "language": "json" @@ -1287,7 +1362,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/questions/search?parentTemplateId=210939f1-c6c4-4274-843d-72f44f77da65", + "raw": "{{BASE_URL}}/questions/search?parentTemplateId={{TEMPLATE_ID}}", "host": [ "{{BASE_URL}}" ], @@ -1298,7 +1373,7 @@ "query": [ { "key": "parentTemplateId", - "value": "210939f1-c6c4-4274-843d-72f44f77da65" + "value": "{{TEMPLATE_ID}}" } ] } @@ -1343,6 +1418,25 @@ } }, "response": [] + }, + { + "name": "Get Question By Template Id", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/questions/by-template-id/{{TEMPLATE_ID}}", + "host": [ + "{{BASE_URL}}" + ], + "path": [ + "questions", + "by-template-id", + "{{TEMPLATE_ID}}" + ] + } + }, + "response": [] } ] }, @@ -1389,7 +1483,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"FormTemplateId\":\"163bac66-4af0-40dc-a09f-bfbe866e7e89\" //Form Template Id\r\n // \"FormUrl\": \"This is Third form\",\r\n // \"AnsweredByUserId\": \"c9e5b365-a0ff-4f73-b168-f3a4263b777e\", //Submitter / User Id\r\n // \"Status\": \"InProgress\"\r\n}", + "raw": "{\r\n \"FormTemplateId\":\"{{TEMPLATE_ID}}\" //Form Template Id\r\n // \"FormUrl\": \"This is Third form\"\r\n // \"AnsweredByUserId\": \"c9e5b365-a0ff-4f73-b168-f3a4263b777e\", //Submitter / User Id\r\n // \"Status\": \"InProgress\"\r\n}", "options": { "raw": { "language": "json" @@ -1466,7 +1560,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"FormTemplateId\":\"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n \"FormUrl\": \"This is Third form\",\r\n \"AnsweredByUserId\": \"05b04a84-ce2d-4c5a-9528-4cc168e0ad0a\",\r\n \"Status\": \"LinkShared\"\r\n}", + "raw": "{\r\n \"FormTemplateId\":\"{{TEMPLATE_ID}}\",\r\n // \"FormUrl\": \"This is Third form\",\r\n // \"AnsweredByUserId\": \"05b04a84-ce2d-4c5a-9528-4cc168e0ad0a\",\r\n \"UserId\": \"6e0793de-49f0-4bce-b4e0-de0c808f3037\",\r\n \"Status\": \"LinkShared\"\r\n}", "options": { "raw": { "language": "json" @@ -1486,25 +1580,6 @@ }, "response": [] }, - { - "name": "get submission by template id", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{BASE_URL}}/form-submissions/by-template/5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-submissions", - "by-template", - "5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a" - ] - } - }, - "response": [] - }, { "name": "Submit the instance of template", "request": { @@ -1551,7 +1626,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/form-submissions/search?id=b0ba4554-ffc0-496f-9985-45325e37de09", + "raw": "{{BASE_URL}}/form-submissions/search?id={{SUBMISSION_ID}}", "host": [ "{{BASE_URL}}" ], @@ -1562,7 +1637,7 @@ "query": [ { "key": "id", - "value": "b0ba4554-ffc0-496f-9985-45325e37de09" + "value": "{{SUBMISSION_ID}}" } ] } @@ -1747,7 +1822,7 @@ "header": [], "body": { "mode": "raw", - "raw": "// {\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// \"IntegerValue\": 1,\r\n// \"FloatValue\": 1.2,\r\n// \"BooleanValue\": true,\r\n// \"DateTimeValue\": \"2024-03-08 18:25:30.843\",\r\n// \"Url\": \"https://meet.google.com/zrb-tyni-rdz\",\r\n// \"FileResourceId\": \"0c9ea637-cb8d-468e-b4c7-303d2604c1b5\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// \"SubmissionTimestamp\": \"2024-03-08 18:25:30.843\",\r\n// \"LastSaveTimestamp\": \"2024-03-08 18:25:30.843\"\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// // \"IntegerValue\": 1,\r\n// // \"FloatValue\": 1.2,\r\n// // \"BooleanValue\": true,\r\n// // \"DateTimeValue\": \"2024-03-08 18:25:30.843\",\r\n// // \"Url\": \"https://meet.google.com/zrb-tyni-rdz\",\r\n// // \"FileResourceId\": \"0c9ea637-cb8d-468e-b4c7-303d2604c1b5\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// \"SubmissionTimestamp\": \"2024-03-08 18:25:30.843\",\r\n// \"LastSaveTimestamp\": \"2024-03-08 18:25:30.843\"\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// \"SubmissionTimestamp\": \"2024-03-08 18:25:30.843\",\r\n// \"LastSaveTimestamp\": \"2024-03-08 18:25:30.843\"\r\n// }\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"533c53a0-c7bd-45a8-a557-441dab0ca650\", //Form submission id\r\n// \"Data\": [\r\n// {\r\n// \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// \"value\": \"Answer\"\r\n// },\r\n// {\r\n// \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// \"value\": \"Answer\"\r\n// }\r\n// ]\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"533c53a0-c7bd-45a8-a557-441dab0ca650\", //Form submission id\r\n// // \"Data\": [\r\n// // {\r\n// // \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// // \"value\": \"Answer\"\r\n// // },\r\n// // {\r\n// // \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// // \"value\": \"Answer\"\r\n// // }\r\n// // ]\r\n// \"Data\":{\r\n// \"dce55a42-f0c4-49ed-b84c-d34893397f4a\":45646\r\n// // \"dce55a42-f0c4-49ed-b84c-d34893397f5a\":\"Answer\"\r\n// }\r\n// }\r\n// {\r\n// \"Data\": {\r\n// \"3fcfd54d-b91e-4a3b-8867-92799cddc99e\": \"Prashant Pandurang Kharade\",\r\n// \"c522ff62-7767-4d93-803b-14c935c3d96f\": \"kharade\",\r\n// \"7716400e-240f-475e-bfc5-ee2b4afec14c\": \"pk\",\r\n// \"d397554c-3ac5-4a25-8824-92bffb33e644\": \"Kiran\",\r\n// \"541217fa-46a8-42f4-9151-1c9c66166cb3\": \"kharade\"\r\n// },\r\n// \"FormSubmissionId\": \"a4728261-1b9a-47a4-aabe-7a3437429639\"\r\n// }\r\n\r\n{\r\n \"Data\": {\r\n \"2255a13c-f4a3-4bcf-9125-e947adc262af\": \"Answer of first question\",\r\n // \"f242a2f0-ab75-4423-bb3d-b08f496a66b9\": 123456,\r\n // \"39b788f6-fbb2-4b77-bf38-d9c2b779bb26\": 6,\r\n // \"d397554c-3ac5-4a25-8824-92bffb33e644\": [\"Prashant\", \"Pandurang\", \"Kharade\"],\r\n \"02f21e5d-787f-409b-8456-f2ed691bc2b1\": \"true\"\r\n },\r\n \"FormSubmissionId\": \"8df8d6a5-5f35-46d8-8cae-4838a7a43d41\"\r\n}", + "raw": "// {\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// \"IntegerValue\": 1,\r\n// \"FloatValue\": 1.2,\r\n// \"BooleanValue\": true,\r\n// \"DateTimeValue\": \"2024-03-08 18:25:30.843\",\r\n// \"Url\": \"https://meet.google.com/zrb-tyni-rdz\",\r\n// \"FileResourceId\": \"0c9ea637-cb8d-468e-b4c7-303d2604c1b5\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// \"SubmissionTimestamp\": \"2024-03-08 18:25:30.843\",\r\n// \"LastSaveTimestamp\": \"2024-03-08 18:25:30.843\"\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// // \"IntegerValue\": 1,\r\n// // \"FloatValue\": 1.2,\r\n// // \"BooleanValue\": true,\r\n// // \"DateTimeValue\": \"2024-03-08 18:25:30.843\",\r\n// // \"Url\": \"https://meet.google.com/zrb-tyni-rdz\",\r\n// // \"FileResourceId\": \"0c9ea637-cb8d-468e-b4c7-303d2604c1b5\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// \"SubmissionTimestamp\": \"2024-03-08 18:25:30.843\",\r\n// \"LastSaveTimestamp\": \"2024-03-08 18:25:30.843\"\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// \"SubmissionTimestamp\": \"2024-03-08 18:25:30.843\",\r\n// \"LastSaveTimestamp\": \"2024-03-08 18:25:30.843\"\r\n// }\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"533c53a0-c7bd-45a8-a557-441dab0ca650\", //Form submission id\r\n// \"Data\": [\r\n// {\r\n// \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// \"value\": \"Answer\"\r\n// },\r\n// {\r\n// \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// \"value\": \"Answer\"\r\n// }\r\n// ]\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"533c53a0-c7bd-45a8-a557-441dab0ca650\", //Form submission id\r\n// // \"Data\": [\r\n// // {\r\n// // \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// // \"value\": \"Answer\"\r\n// // },\r\n// // {\r\n// // \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// // \"value\": \"Answer\"\r\n// // }\r\n// // ]\r\n// \"Data\":{\r\n// \"dce55a42-f0c4-49ed-b84c-d34893397f4a\":45646\r\n// // \"dce55a42-f0c4-49ed-b84c-d34893397f5a\":\"Answer\"\r\n// }\r\n// }\r\n// {\r\n// \"Data\": {\r\n// \"3fcfd54d-b91e-4a3b-8867-92799cddc99e\": \"Prashant Pandurang Kharade\",\r\n// \"c522ff62-7767-4d93-803b-14c935c3d96f\": \"kharade\",\r\n// \"7716400e-240f-475e-bfc5-ee2b4afec14c\": \"pk\",\r\n// \"d397554c-3ac5-4a25-8824-92bffb33e644\": \"Kiran\",\r\n// \"541217fa-46a8-42f4-9151-1c9c66166cb3\": \"kharade\"\r\n// },\r\n// \"FormSubmissionId\": \"a4728261-1b9a-47a4-aabe-7a3437429639\"\r\n// }\r\n\r\n{\r\n \"Data\": {\r\n \"2255a13c-f4a3-4bcf-9125-e947adc262af\": \"Answer of first question\",\r\n // \"f242a2f0-ab75-4423-bb3d-b08f496a66b9\": 123456,\r\n // \"39b788f6-fbb2-4b77-bf38-d9c2b779bb26\": 6,\r\n // \"d397554c-3ac5-4a25-8824-92bffb33e644\": [\"Prashant\", \"Pandurang\", \"Kharade\"],\r\n \"02f21e5d-787f-409b-8456-f2ed691bc2b1\": \"true\"\r\n },\r\n \"FormSubmissionId\": \"19dd2f7e-3001-432d-99b8-93d0495bc542\"\r\n}", "options": { "raw": { "language": "json" @@ -1989,6 +2064,28 @@ } }, "response": [] + }, + { + "name": "Save Question Response", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"QuestionResponses\": [{\r\n \"id\": \"a9ee5e15-cb9a-4989-8709-293c33563045\",\r\n \"FormSubmissionId\": \"{{SUBMISSION_ID}}\",\r\n \"QuestionId\": \"{{QUESTION_ID}}\",\r\n \"ResponseType\": \"Text\",\r\n \"IntegerValue\": 1,\r\n \"FloatValue\": 1.2,\r\n \"BooleanValue\": \"1\",\r\n \"DateTimeValue\": \"2024-03-08T12:55:31.000Z\",\r\n \"Url\": \"https://meet.google.com/zrb-tyni-rdz\",\r\n \"FileResourceId\": \"0c9ea637-cb8d-468e-b4c7-303d2604c1b5\",\r\n \"SubmissionTimestamp\": null,\r\n \"LastSaveTimestamp\": \"2025-06-21T11:43:57.000Z\"\r\n }],\r\n \"FormSubmissionKey\": \"{{SUBMISSION_KEY}}\"\r\n}" + }, + "url": { + "raw": "{{BASE_URL}}/question-responses/save", + "host": [ + "{{BASE_URL}}" + ], + "path": [ + "question-responses", + "save" + ] + } + }, + "response": [] } ] }, diff --git a/Postman/forms-service.postman_environment.json b/Postman/forms-service.postman_environment.json new file mode 100644 index 0000000..2583e6b --- /dev/null +++ b/Postman/forms-service.postman_environment.json @@ -0,0 +1,63 @@ +{ + "id": "c625adeb-50e0-4ee6-832b-152b32530ce2", + "name": "forms-service", + "values": [ + { + "key": "BASE_URL", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "USER_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "TEMPLATE_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "SECTION_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "QUESTION_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "SUBMISSION_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "RESPONSE_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "PARENT_SECTION_ID", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "SUBMISSION_KEY", + "value": "", + "type": "default", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2025-06-21T12:41:29.517Z", + "_postman_exported_using": "Postman/11.50.6" +} \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index c023b33..c3f6174 100644 --- a/src/app.ts +++ b/src/app.ts @@ -191,6 +191,6 @@ async function connectDatabase_Primary() { await DatabaseClient.dropDatabase(); } // const primaryDatabaseConnector = Injector.Container.resolve(PrimaryDatabaseConnector); - DatabaseClient.createDatabase(); + await DatabaseClient.createDatabase(); await DBConnector.initialize(); } diff --git a/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts b/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts index 90b8fd4..14fb577 100644 --- a/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts +++ b/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts @@ -25,14 +25,7 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { create = async ( model: FormTemplateCreateModel): Promise => { - Logger.instance().log( - `${FormTemplate}` - ); - - Logger.instance().log( - `${FormType}` - ); - + try { const data = this._formTemplateRepo.create({ diff --git a/src/database/sql/typeorm/repositories/user/user.repo.ts b/src/database/sql/typeorm/repositories/user/user.repo.ts index 6afba8a..10844e0 100644 --- a/src/database/sql/typeorm/repositories/user/user.repo.ts +++ b/src/database/sql/typeorm/repositories/user/user.repo.ts @@ -173,33 +173,23 @@ export class UserRepo extends BaseRepo implements IUserRepo{ }; if (filters.lastName) { - search.where["LastName"] = { - equals: filters.lastName, - }; + search.where["LastName"] = filters.lastName; } if (filters.countryCode) { - search.where["CountryCode"] = { - equals: filters.countryCode, - }; + search.where["CountryCode"] = filters.countryCode; } if (filters.email) { - search.where["Email"] = { - equals: filters.email, - }; + search.where["Email"] = filters.email; } if (filters.username) { - search.where["Username"] = { - equals: filters.username, - }; + search.where["Username"] = filters.username; } if (filters.password) { - search.where["Password"] = { - equals: filters.password, - }; + search.where["Password"] = filters.password; } return search; From 1c120e061f5e2a5a1e63bebcaf8ade679e2ac896 Mon Sep 17 00:00:00 2001 From: inflection-prashant Date: Wed, 9 Jul 2025 17:29:04 +0530 Subject: [PATCH 04/29] Refactors form service files and updates request handling Renames and reorganizes form service files for improved structure and consistency. Updates request handling to utilize dynamic template IDs and enhances response management. Changes include transitioning from static to variable identifiers, allowing more flexible integration across services. Improves test structure by replacing deprecated syntax with a standardized post-response script pattern. Enhances overall maintainability and clarity of the form service codebase. --- .../Form Submission/Create new form.bru | 6 +- .../Form Submission/Delete a form record.bru | 4 +- .../Form Submission/Generic search.bru | 8 +- .../Form Submission/Get form by id.bru | 0 .../Submit the instance of template.bru | 2 +- .../Form Submission/Update a form record.bru | 9 +- bruno/form-service/Form Submission/folder.bru | 8 + .../Form section/Create new form section.bru | 12 +- .../Delete the form section record.bru | 2 +- .../Form section}/Generic search.bru | 6 +- .../Form section/Get form section by id.bru | 2 +- .../Get form section by template id.bru | 11 + .../Form section/Update form section.bru | 4 +- bruno/form-service/Form section/folder.bru | 8 + .../Delete form template record.bru | 2 +- .../Form template/Export Form Template.bru | 11 + .../Form template/Generic search.bru | 6 +- .../Get Details by template id.bru | 2 +- .../Form template/Get Form Submissions.bru | 11 + .../Form template/Get form template by id.bru | 2 +- .../Form template/Preview Form Template.bru | 11 + .../Update form template record.bru | 4 +- .../Form template/create a form template.bru | 4 +- bruno/form-service/Form template/folder.bru | 8 + .../form-service}/Health Check Request.bru | 2 +- .../Create a new response.bru | 51 +- .../Question Response/Delete a response.bru | 2 +- .../Export Responses in CSV.bru | 0 .../Export responses in PDF.bru | 0 .../Question Response/Generic search.bru | 2 +- .../Question Response/Get response by id.bru | 2 +- .../Save Question Response.bru | 31 + .../Update a existing response.bru | 2 +- .../form-service/Question Response/folder.bru | 8 + .../Question/Create a new question.bru | 384 ++++++++++ .../Question/Delete a question record.bru | 2 +- .../form-service/Question}/Generic search.bru | 7 +- .../Question/Get Question By Template Id.bru | 11 + .../Question/Get question by id.bru | 4 +- .../Question/Update a question record.bru | 13 +- bruno/form-service/Question/folder.bru | 8 + .../Create a new session.bru | 0 .../Delete a existing session.bru | 0 .../Get a login session by id.bru | 0 .../User Login Session/Update a session.bru | 0 .../User Login Session/folder.bru | 8 + .../form-service}/User/Delete user.bru | 2 +- .../form-service/User}/Generic search.bru | 7 +- bruno/form-service/User/Get All Users.bru | 11 + .../form-service}/User/Get user by id.bru | 2 +- .../form-service}/User/New user.bru | 2 +- .../form-service}/User/Update user record.bru | 2 +- bruno/form-service/User/folder.bru | 8 + .../form-service}/bruno.json | 0 bruno/form-service/collection.bru | 8 + .../environments/forms-service.bru | 11 + .../get submission by template id.bru | 11 - .../Question/Create a new question.bru | 160 ----- form-service/collection.bru | 0 schema.json | 115 +++ .../favorite.template.controller.ts | 79 +++ .../favorite.template.router.ts | 16 + .../favorite.template.validator.ts | 102 +++ .../form.section/form.section.controller.ts | 13 +- .../form.section/form.section.validator.ts | 2 +- src/api/form.submission/form.controller.ts | 27 +- .../form.template.approval.controller.ts | 89 +++ .../form.template.approval.router.ts | 16 + .../form.template.approval.validator.ts | 116 +++ .../input.unit.list.controller.ts | 77 ++ .../input.unit.list/input.unit.list.router.ts | 15 + .../input.unit.list.validator.ts | 91 +++ .../question.response.controller.ts | 3 +- .../question.response.validator.ts | 2 + .../template.folder.controller.ts | 77 ++ .../template.folder/template.folder.router.ts | 15 + .../template.folder.validator.ts | 96 +++ .../favorite.template.repo.interface.ts | 13 + .../form.section.repo.interface.ts | 11 +- .../form.template.approval.repo.interface.ts | 16 + .../input.unit.list.repo.interface.ts | 14 + .../template.folder.repo.interface.ts | 14 + .../mappers/favorite.template.mapper.ts | 25 + .../mappers/form.template.approval.mapper.ts | 27 + .../typeorm/mappers/input.unit.list.mapper.ts | 31 + .../typeorm/mappers/template.folder.mapper.ts | 25 + .../favorite.template.model.ts | 2 +- .../models/form.section/form.section.model.ts | 22 +- .../input.unit.list/input.unit.list.model.ts | 5 +- .../typeorm/models/logic/base.logic.model.ts | 36 + .../question.response.model.ts | 24 +- .../typeorm/models/question/question.model.ts | 30 +- .../favorite.template.repo.ts | 141 ++++ .../form.template.approval.repo.ts | 168 +++++ .../input.unit.list/input.unit.list.repo.ts | 130 ++++ .../question.response.repo.ts | 658 +++++++++--------- .../template.folder/template.folder.repo.ts | 133 ++++ src/database/sql/typeorm/typeorm.injector.ts | 12 +- .../forms/favorite.template.domain.types.ts | 30 + .../form.template.approval.domain.types.ts | 37 + .../forms/input.unit.list.domain.types.ts | 32 + .../forms/response.domain.types.ts | 2 + .../forms/template.folder.domain.types.ts | 33 + .../favorite.template.service.ts | 39 ++ .../form.template.approval.service.ts | 37 + .../input.unit.list.service.ts | 33 + .../question.response.service.ts | 206 +++--- src/services/question/question.service.ts | 350 +++++----- .../template.folder.service.ts | 33 + src/startup/router.ts | 6 + 110 files changed, 3297 insertions(+), 933 deletions(-) rename {form-service => bruno/form-service}/Form Submission/Create new form.bru (87%) rename {form-service => bruno/form-service}/Form Submission/Delete a form record.bru (93%) rename {form-service => bruno/form-service}/Form Submission/Generic search.bru (75%) rename {form-service => bruno/form-service}/Form Submission/Get form by id.bru (100%) rename {form-service => bruno/form-service}/Form Submission/Submit the instance of template.bru (95%) rename {form-service => bruno/form-service}/Form Submission/Update a form record.bru (84%) create mode 100644 bruno/form-service/Form Submission/folder.bru rename {form-service => bruno/form-service}/Form section/Create new form section.bru (85%) rename {form-service => bruno/form-service}/Form section/Delete the form section record.bru (95%) rename {form-service/Question => bruno/form-service/Form section}/Generic search.bru (73%) rename {form-service => bruno/form-service}/Form section/Get form section by id.bru (97%) create mode 100644 bruno/form-service/Form section/Get form section by template id.bru rename {form-service => bruno/form-service}/Form section/Update form section.bru (96%) create mode 100644 bruno/form-service/Form section/folder.bru rename {form-service => bruno/form-service}/Form template/Delete form template record.bru (95%) create mode 100644 bruno/form-service/Form template/Export Form Template.bru rename {form-service => bruno/form-service}/Form template/Generic search.bru (79%) rename {form-service => bruno/form-service}/Form template/Get Details by template id.bru (55%) create mode 100644 bruno/form-service/Form template/Get Form Submissions.bru rename {form-service => bruno/form-service}/Form template/Get form template by id.bru (97%) create mode 100644 bruno/form-service/Form template/Preview Form Template.bru rename {form-service => bruno/form-service}/Form template/Update form template record.bru (95%) rename {form-service => bruno/form-service}/Form template/create a form template.bru (94%) create mode 100644 bruno/form-service/Form template/folder.bru rename {form-service => bruno/form-service}/Health Check Request.bru (92%) rename {form-service => bruno/form-service}/Question Response/Create a new response.bru (76%) rename {form-service => bruno/form-service}/Question Response/Delete a response.bru (95%) rename {form-service => bruno/form-service}/Question Response/Export Responses in CSV.bru (100%) rename {form-service => bruno/form-service}/Question Response/Export responses in PDF.bru (100%) rename {form-service => bruno/form-service}/Question Response/Generic search.bru (96%) rename {form-service => bruno/form-service}/Question Response/Get response by id.bru (62%) create mode 100644 bruno/form-service/Question Response/Save Question Response.bru rename {form-service => bruno/form-service}/Question Response/Update a existing response.bru (99%) create mode 100644 bruno/form-service/Question Response/folder.bru create mode 100644 bruno/form-service/Question/Create a new question.bru rename {form-service => bruno/form-service}/Question/Delete a question record.bru (95%) rename {form-service/User => bruno/form-service/Question}/Generic search.bru (77%) create mode 100644 bruno/form-service/Question/Get Question By Template Id.bru rename {form-service => bruno/form-service}/Question/Get question by id.bru (97%) rename {form-service => bruno/form-service}/Question/Update a question record.bru (79%) create mode 100644 bruno/form-service/Question/folder.bru rename {form-service => bruno/form-service}/User Login Session/Create a new session.bru (100%) rename {form-service => bruno/form-service}/User Login Session/Delete a existing session.bru (100%) rename {form-service => bruno/form-service}/User Login Session/Get a login session by id.bru (100%) rename {form-service => bruno/form-service}/User Login Session/Update a session.bru (100%) create mode 100644 bruno/form-service/User Login Session/folder.bru rename {form-service => bruno/form-service}/User/Delete user.bru (94%) rename {form-service/Form section => bruno/form-service/User}/Generic search.bru (72%) create mode 100644 bruno/form-service/User/Get All Users.bru rename {form-service => bruno/form-service}/User/Get user by id.bru (97%) rename {form-service => bruno/form-service}/User/New user.bru (98%) rename {form-service => bruno/form-service}/User/Update user record.bru (98%) create mode 100644 bruno/form-service/User/folder.bru rename {form-service => bruno/form-service}/bruno.json (100%) create mode 100644 bruno/form-service/collection.bru create mode 100644 bruno/form-service/environments/forms-service.bru delete mode 100644 form-service/Form Submission/get submission by template id.bru delete mode 100644 form-service/Question/Create a new question.bru delete mode 100644 form-service/collection.bru create mode 100644 schema.json create mode 100644 src/api/favorite.template/favorite.template.controller.ts create mode 100644 src/api/favorite.template/favorite.template.router.ts create mode 100644 src/api/favorite.template/favorite.template.validator.ts create mode 100644 src/api/form.template.approval/form.template.approval.controller.ts create mode 100644 src/api/form.template.approval/form.template.approval.router.ts create mode 100644 src/api/form.template.approval/form.template.approval.validator.ts create mode 100644 src/api/input.unit.list/input.unit.list.controller.ts create mode 100644 src/api/input.unit.list/input.unit.list.router.ts create mode 100644 src/api/input.unit.list/input.unit.list.validator.ts create mode 100644 src/api/template.folder/template.folder.controller.ts create mode 100644 src/api/template.folder/template.folder.router.ts create mode 100644 src/api/template.folder/template.folder.validator.ts create mode 100644 src/database/repository.interfaces/favorite.template/favorite.template.repo.interface.ts create mode 100644 src/database/repository.interfaces/form.template.approval/form.template.approval.repo.interface.ts create mode 100644 src/database/repository.interfaces/input.unit.list/input.unit.list.repo.interface.ts create mode 100644 src/database/repository.interfaces/template.folder/template.folder.repo.interface.ts create mode 100644 src/database/sql/typeorm/mappers/favorite.template.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/form.template.approval.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/input.unit.list.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/template.folder.mapper.ts create mode 100644 src/database/sql/typeorm/models/logic/base.logic.model.ts create mode 100644 src/database/sql/typeorm/repositories/favorite.template/favorite.template.repo.ts create mode 100644 src/database/sql/typeorm/repositories/form.template.approval/form.template.approval.repo.ts create mode 100644 src/database/sql/typeorm/repositories/input.unit.list/input.unit.list.repo.ts create mode 100644 src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts create mode 100644 src/domain.types/forms/favorite.template.domain.types.ts create mode 100644 src/domain.types/forms/form.template.approval.domain.types.ts create mode 100644 src/domain.types/forms/input.unit.list.domain.types.ts create mode 100644 src/domain.types/forms/template.folder.domain.types.ts create mode 100644 src/services/favorite.template/favorite.template.service.ts create mode 100644 src/services/form.template.approval/form.template.approval.service.ts create mode 100644 src/services/input.unit.list/input.unit.list.service.ts create mode 100644 src/services/template.folder/template.folder.service.ts diff --git a/form-service/Form Submission/Create new form.bru b/bruno/form-service/Form Submission/Create new form.bru similarity index 87% rename from form-service/Form Submission/Create new form.bru rename to bruno/form-service/Form Submission/Create new form.bru index 77c640f..863f57a 100644 --- a/form-service/Form Submission/Create new form.bru +++ b/bruno/form-service/Form Submission/Create new form.bru @@ -12,14 +12,14 @@ post { body:json { { - "FormTemplateId":"163bac66-4af0-40dc-a09f-bfbe866e7e89" //Form Template Id - // "FormUrl": "This is Third form", + "FormTemplateId":"{{TEMPLATE_ID}}" //Form Template Id + // "FormUrl": "This is Third form" // "AnsweredByUserId": "c9e5b365-a0ff-4f73-b168-f3a4263b777e", //Submitter / User Id // "Status": "InProgress" } } -tests { +script:post-response { try { var jsonRes = res.getBody(); bru.setEnvVar("SUBMISSION_ID", jsonRes.Data.id); diff --git a/form-service/Form Submission/Delete a form record.bru b/bruno/form-service/Form Submission/Delete a form record.bru similarity index 93% rename from form-service/Form Submission/Delete a form record.bru rename to bruno/form-service/Form Submission/Delete a form record.bru index f69500d..915cd40 100644 --- a/form-service/Form Submission/Delete a form record.bru +++ b/bruno/form-service/Form Submission/Delete a form record.bru @@ -1,7 +1,7 @@ meta { name: Delete a form record type: http - seq: 7 + seq: 6 } delete { @@ -10,7 +10,7 @@ delete { auth: none } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Form Submission/Generic search.bru b/bruno/form-service/Form Submission/Generic search.bru similarity index 75% rename from form-service/Form Submission/Generic search.bru rename to bruno/form-service/Form Submission/Generic search.bru index c9144f8..4330521 100644 --- a/form-service/Form Submission/Generic search.bru +++ b/bruno/form-service/Form Submission/Generic search.bru @@ -1,20 +1,20 @@ meta { name: Generic search type: http - seq: 6 + seq: 5 } get { - url: {{BASE_URL}}/form-submissions/search?id=b0ba4554-ffc0-496f-9985-45325e37de09 + url: {{BASE_URL}}/form-submissions/search?id={{SUBMISSION_ID}} body: none auth: none } params:query { - id: b0ba4554-ffc0-496f-9985-45325e37de09 + id: {{SUBMISSION_ID}} } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Form Submission/Get form by id.bru b/bruno/form-service/Form Submission/Get form by id.bru similarity index 100% rename from form-service/Form Submission/Get form by id.bru rename to bruno/form-service/Form Submission/Get form by id.bru diff --git a/form-service/Form Submission/Submit the instance of template.bru b/bruno/form-service/Form Submission/Submit the instance of template.bru similarity index 95% rename from form-service/Form Submission/Submit the instance of template.bru rename to bruno/form-service/Form Submission/Submit the instance of template.bru index 6c25885..0bd31e4 100644 --- a/form-service/Form Submission/Submit the instance of template.bru +++ b/bruno/form-service/Form Submission/Submit the instance of template.bru @@ -1,7 +1,7 @@ meta { name: Submit the instance of template type: http - seq: 5 + seq: 4 } post { diff --git a/form-service/Form Submission/Update a form record.bru b/bruno/form-service/Form Submission/Update a form record.bru similarity index 84% rename from form-service/Form Submission/Update a form record.bru rename to bruno/form-service/Form Submission/Update a form record.bru index 2ba7d6a..fac6f53 100644 --- a/form-service/Form Submission/Update a form record.bru +++ b/bruno/form-service/Form Submission/Update a form record.bru @@ -12,14 +12,15 @@ put { body:json { { - "FormTemplateId":"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6", - "FormUrl": "This is Third form", - "AnsweredByUserId": "05b04a84-ce2d-4c5a-9528-4cc168e0ad0a", + "FormTemplateId":"{{TEMPLATE_ID}}", + // "FormUrl": "This is Third form", + // "AnsweredByUserId": "05b04a84-ce2d-4c5a-9528-4cc168e0ad0a", + "UserId": "6e0793de-49f0-4bce-b4e0-de0c808f3037", "Status": "LinkShared" } } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form Submission/folder.bru b/bruno/form-service/Form Submission/folder.bru new file mode 100644 index 0000000..6f609f8 --- /dev/null +++ b/bruno/form-service/Form Submission/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Form Submission + seq: 5 +} + +auth { + mode: none +} diff --git a/form-service/Form section/Create new form section.bru b/bruno/form-service/Form section/Create new form section.bru similarity index 85% rename from form-service/Form section/Create new form section.bru rename to bruno/form-service/Form section/Create new form section.bru index 98bc4b4..bed61fd 100644 --- a/form-service/Form section/Create new form section.bru +++ b/bruno/form-service/Form section/Create new form section.bru @@ -21,16 +21,16 @@ body:json { // "ParentSectionId": "2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6" //any uuid value // } { - "ParentFormTemplateId": "b78158bd-7496-48f1-b625-2c489b9c65a9", + "ParentFormTemplateId": "7d6fe0be-3f6c-4a8e-8231-b8a7568d2c9d", // "SectionIdentifier": "new form section 1" - // "Title": "New form section ", - // "Description": "sf esdrg", - // "Sequence":"abc" - "ParentSectionId": "d04ca675-d4eb-4c6a-9820-6ba1cce3e625" + "Title": "New form section ", + "Description": "sf esdrg", + "Sequence": 12, + "ParentSectionId": "44071647-f61e-448b-8dec-ae2c8d3017c6" } } -tests { +script:post-response { try { var jsonRes = res.getBody(); bru.setEnvVar("SECTION_ID", jsonRes.Data.id); diff --git a/form-service/Form section/Delete the form section record.bru b/bruno/form-service/Form section/Delete the form section record.bru similarity index 95% rename from form-service/Form section/Delete the form section record.bru rename to bruno/form-service/Form section/Delete the form section record.bru index 3e3c7ab..1ef198c 100644 --- a/form-service/Form section/Delete the form section record.bru +++ b/bruno/form-service/Form section/Delete the form section record.bru @@ -10,7 +10,7 @@ delete { auth: none } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Question/Generic search.bru b/bruno/form-service/Form section/Generic search.bru similarity index 73% rename from form-service/Question/Generic search.bru rename to bruno/form-service/Form section/Generic search.bru index 0d83ab8..321e89b 100644 --- a/form-service/Question/Generic search.bru +++ b/bruno/form-service/Form section/Generic search.bru @@ -5,16 +5,16 @@ meta { } get { - url: {{BASE_URL}}/questions/search?parentTemplateId=210939f1-c6c4-4274-843d-72f44f77da65 + url: {{BASE_URL}}/form-sections/search?parentFormTemplateId={{TEMPLATE_ID}} body: none auth: none } params:query { - parentTemplateId: 210939f1-c6c4-4274-843d-72f44f77da65 + parentFormTemplateId: {{TEMPLATE_ID}} } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Form section/Get form section by id.bru b/bruno/form-service/Form section/Get form section by id.bru similarity index 97% rename from form-service/Form section/Get form section by id.bru rename to bruno/form-service/Form section/Get form section by id.bru index 5335a0b..5c9af51 100644 --- a/form-service/Form section/Get form section by id.bru +++ b/bruno/form-service/Form section/Get form section by id.bru @@ -10,7 +10,7 @@ get { auth: none } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form section/Get form section by template id.bru b/bruno/form-service/Form section/Get form section by template id.bru new file mode 100644 index 0000000..005d092 --- /dev/null +++ b/bruno/form-service/Form section/Get form section by template id.bru @@ -0,0 +1,11 @@ +meta { + name: Get form section by template id + type: http + seq: 6 +} + +get { + url: {{BASE_URL}}/form-sections/templateId/{{TEMPLATE_ID}} + body: none + auth: none +} diff --git a/form-service/Form section/Update form section.bru b/bruno/form-service/Form section/Update form section.bru similarity index 96% rename from form-service/Form section/Update form section.bru rename to bruno/form-service/Form section/Update form section.bru index e35e451..733c057 100644 --- a/form-service/Form section/Update form section.bru +++ b/bruno/form-service/Form section/Update form section.bru @@ -25,11 +25,11 @@ body:json { "Title": "first section", "Description": "description", // "Sequence": "1", - "ParentSectionId": "6eb7e68d-608b-4076-8a9d-bbc467ab9211" + "ParentSectionId": "{{PARENT_SECTION_ID}}" } } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form section/folder.bru b/bruno/form-service/Form section/folder.bru new file mode 100644 index 0000000..45bb69b --- /dev/null +++ b/bruno/form-service/Form section/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Form section + seq: 3 +} + +auth { + mode: none +} diff --git a/form-service/Form template/Delete form template record.bru b/bruno/form-service/Form template/Delete form template record.bru similarity index 95% rename from form-service/Form template/Delete form template record.bru rename to bruno/form-service/Form template/Delete form template record.bru index a3ff73a..d13f424 100644 --- a/form-service/Form template/Delete form template record.bru +++ b/bruno/form-service/Form template/Delete form template record.bru @@ -10,7 +10,7 @@ delete { auth: none } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form template/Export Form Template.bru b/bruno/form-service/Form template/Export Form Template.bru new file mode 100644 index 0000000..5beccd8 --- /dev/null +++ b/bruno/form-service/Form template/Export Form Template.bru @@ -0,0 +1,11 @@ +meta { + name: Export Form Template + type: http + seq: 8 +} + +get { + url: + body: none + auth: none +} diff --git a/form-service/Form template/Generic search.bru b/bruno/form-service/Form template/Generic search.bru similarity index 79% rename from form-service/Form template/Generic search.bru rename to bruno/form-service/Form template/Generic search.bru index fd119fb..6444dd5 100644 --- a/form-service/Form template/Generic search.bru +++ b/bruno/form-service/Form template/Generic search.bru @@ -5,18 +5,18 @@ meta { } get { - url: {{BASE_URL}}/form-templates/search?id=210939f1-c6c4-4274-843d-72f44f77da65 + url: {{BASE_URL}}/form-templates/search?id={{TEMPLATE_ID}} body: none auth: none } params:query { - id: 210939f1-c6c4-4274-843d-72f44f77da65 + id: {{TEMPLATE_ID}} ~title: What is your full name ~description: about testing the assessment } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Form template/Get Details by template id.bru b/bruno/form-service/Form template/Get Details by template id.bru similarity index 55% rename from form-service/Form template/Get Details by template id.bru rename to bruno/form-service/Form template/Get Details by template id.bru index 1c73fd8..2e6500e 100644 --- a/form-service/Form template/Get Details by template id.bru +++ b/bruno/form-service/Form template/Get Details by template id.bru @@ -5,7 +5,7 @@ meta { } get { - url: {{BASE_URL}}/form-templates/d71cc1a7-abdb-4196-bfef-19ba4e7cd1be/details + url: {{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/details body: none auth: none } diff --git a/bruno/form-service/Form template/Get Form Submissions.bru b/bruno/form-service/Form template/Get Form Submissions.bru new file mode 100644 index 0000000..c7804ce --- /dev/null +++ b/bruno/form-service/Form template/Get Form Submissions.bru @@ -0,0 +1,11 @@ +meta { + name: Get Form Submissions + type: http + seq: 7 +} + +get { + url: {{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/submissions + body: none + auth: none +} diff --git a/form-service/Form template/Get form template by id.bru b/bruno/form-service/Form template/Get form template by id.bru similarity index 97% rename from form-service/Form template/Get form template by id.bru rename to bruno/form-service/Form template/Get form template by id.bru index b2b06be..6e96848 100644 --- a/form-service/Form template/Get form template by id.bru +++ b/bruno/form-service/Form template/Get form template by id.bru @@ -10,7 +10,7 @@ get { auth: none } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form template/Preview Form Template.bru b/bruno/form-service/Form template/Preview Form Template.bru new file mode 100644 index 0000000..b7a85fe --- /dev/null +++ b/bruno/form-service/Form template/Preview Form Template.bru @@ -0,0 +1,11 @@ +meta { + name: Preview Form Template + type: http + seq: 9 +} + +get { + url: + body: none + auth: none +} diff --git a/form-service/Form template/Update form template record.bru b/bruno/form-service/Form template/Update form template record.bru similarity index 95% rename from form-service/Form template/Update form template record.bru rename to bruno/form-service/Form template/Update form template record.bru index 045f005..f027b57 100644 --- a/form-service/Form template/Update form template record.bru +++ b/bruno/form-service/Form template/Update form template record.bru @@ -18,13 +18,13 @@ body:json { "Type": "Questionnaire", "DisplayCode": "xyz1234asssssdfghj", "OwnerUserId": "16377833-8e6f-41b4-944a-98a91815a4d5", - "RootSectionId": "d04ca675-d4eb-4c6a-9820-6ba1cce3e625", + // "RootSectionId": "d04ca675-d4eb-4c6a-9820-6ba1cce3e625", "DefaultSectionNumbering": true, "ItemsPerPage":"OneQuestion" } } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Form template/create a form template.bru b/bruno/form-service/Form template/create a form template.bru similarity index 94% rename from form-service/Form template/create a form template.bru rename to bruno/form-service/Form template/create a form template.bru index a310fba..ce4e194 100644 --- a/form-service/Form template/create a form template.bru +++ b/bruno/form-service/Form template/create a form template.bru @@ -17,14 +17,14 @@ body:json { "CurrentVersion": 1, "Type": "Questionnaire", // "DisplayCode": "xyz1234", - "OwnerUserId": "16377833-8e6f-41b4-944a-98a91815a4d5", + "OwnerUserId": "ad693c12-3141-45e7-ab3a-59d03306757f", // "RootSectionId": "9618c6a8-0555-4a14-95ec-1946ec09c8e0", "DefaultSectionNumbering": true, "ItemsPerPage":"OneQuestion" } } -tests { +script:post-response { try { var jsonRes = res.getBody(); bru.setEnvVar("TEMPLATE_ID", jsonRes.Data.id); diff --git a/bruno/form-service/Form template/folder.bru b/bruno/form-service/Form template/folder.bru new file mode 100644 index 0000000..c06640e --- /dev/null +++ b/bruno/form-service/Form template/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Form template + seq: 2 +} + +auth { + mode: none +} diff --git a/form-service/Health Check Request.bru b/bruno/form-service/Health Check Request.bru similarity index 92% rename from form-service/Health Check Request.bru rename to bruno/form-service/Health Check Request.bru index c6b71ee..6d56c37 100644 --- a/form-service/Health Check Request.bru +++ b/bruno/form-service/Health Check Request.bru @@ -1,7 +1,7 @@ meta { name: Health Check Request type: http - seq: 1 + seq: 8 } get { diff --git a/form-service/Question Response/Create a new response.bru b/bruno/form-service/Question Response/Create a new response.bru similarity index 76% rename from form-service/Question Response/Create a new response.bru rename to bruno/form-service/Question Response/Create a new response.bru index 6f8aa20..7090e8b 100644 --- a/form-service/Question Response/Create a new response.bru +++ b/bruno/form-service/Question Response/Create a new response.bru @@ -5,26 +5,27 @@ meta { } post { - url: {{BASE_URL}}/question-responses/save + url: {{BASE_URL}}/question-responses body: json auth: none } body:json { - // { - // "FormSubmissionId": "2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6", - // "QuestionId": "1ce7590c-b37d-4668-b324-d20f0a86c923", - // "ResponseType": "Text", - // "IntegerValue": 1, - // "FloatValue": 1.2, - // "BooleanValue": true, - // "DateTimeValue": "2024-03-08 18:25:30.843", - // "Url": "https://meet.google.com/zrb-tyni-rdz", - // "FileResourceId": "0c9ea637-cb8d-468e-b4c7-303d2604c1b5", - // "TextValue": "hjsj1234-erhh233", + { + "FormSubmissionId": "e89a1449-a420-4b99-880c-f3eb14cf3803", + "QuestionId": "2391ccf4-0adf-4be0-a500-261ad79ac238", + "ResponseType": "Text", + "IntegerValue": 1, + "FloatValue": 1.2, + "BooleanValue": true, + "DateTimeValue": "2024-03-08 18:25:30.843", + "Url": "https://meet.google.com/zrb-tyni-rdz", + "FileResourceId": "0c9ea637-cb8d-468e-b4c7-303d2604c1b5", + "TextValue": "hjsj1234-erhh233", // "SubmissionTimestamp": "2024-03-08 18:25:30.843", - // "LastSaveTimestamp": "2024-03-08 18:25:30.843" - // } + // "LastSaveTimestamp": "2024-03-08 18:25:30.843", + "UserResponse":"Yes" + } // { // "FormSubmissionId": "2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6", // "QuestionId": "1ce7590c-b37d-4668-b324-d20f0a86c923", @@ -93,19 +94,19 @@ body:json { // "FormSubmissionId": "a4728261-1b9a-47a4-aabe-7a3437429639" // } - { - "Data": { - "2255a13c-f4a3-4bcf-9125-e947adc262af": "Answer of first question", - // "f242a2f0-ab75-4423-bb3d-b08f496a66b9": 123456, - // "39b788f6-fbb2-4b77-bf38-d9c2b779bb26": 6, - // "d397554c-3ac5-4a25-8824-92bffb33e644": ["Prashant", "Pandurang", "Kharade"], - "02f21e5d-787f-409b-8456-f2ed691bc2b1": "true" - }, - "FormSubmissionId": "8df8d6a5-5f35-46d8-8cae-4838a7a43d41" - } + // { + // "Data": { + // "2255a13c-f4a3-4bcf-9125-e947adc262af": "Answer of first question", + // // "f242a2f0-ab75-4423-bb3d-b08f496a66b9": 123456, + // // "39b788f6-fbb2-4b77-bf38-d9c2b779bb26": 6, + // // "d397554c-3ac5-4a25-8824-92bffb33e644": ["Prashant", "Pandurang", "Kharade"], + // "02f21e5d-787f-409b-8456-f2ed691bc2b1": "true" + // }, + // "FormSubmissionId": "19dd2f7e-3001-432d-99b8-93d0495bc542" + // } } -tests { +script:post-response { try { var jsonRes = res.getBody(); bru.setEnvVar("RESPONSE_ID", jsonRes.Data.id); diff --git a/form-service/Question Response/Delete a response.bru b/bruno/form-service/Question Response/Delete a response.bru similarity index 95% rename from form-service/Question Response/Delete a response.bru rename to bruno/form-service/Question Response/Delete a response.bru index fbbb394..e0a1ba3 100644 --- a/form-service/Question Response/Delete a response.bru +++ b/bruno/form-service/Question Response/Delete a response.bru @@ -10,7 +10,7 @@ delete { auth: none } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Question Response/Export Responses in CSV.bru b/bruno/form-service/Question Response/Export Responses in CSV.bru similarity index 100% rename from form-service/Question Response/Export Responses in CSV.bru rename to bruno/form-service/Question Response/Export Responses in CSV.bru diff --git a/form-service/Question Response/Export responses in PDF.bru b/bruno/form-service/Question Response/Export responses in PDF.bru similarity index 100% rename from form-service/Question Response/Export responses in PDF.bru rename to bruno/form-service/Question Response/Export responses in PDF.bru diff --git a/form-service/Question Response/Generic search.bru b/bruno/form-service/Question Response/Generic search.bru similarity index 96% rename from form-service/Question Response/Generic search.bru rename to bruno/form-service/Question Response/Generic search.bru index d762c0a..d4f65fa 100644 --- a/form-service/Question Response/Generic search.bru +++ b/bruno/form-service/Question Response/Generic search.bru @@ -16,7 +16,7 @@ params:query { ~questionId: b56027ee-bdba-4182-bed6-d544a5bcc8e4 } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Question Response/Get response by id.bru b/bruno/form-service/Question Response/Get response by id.bru similarity index 62% rename from form-service/Question Response/Get response by id.bru rename to bruno/form-service/Question Response/Get response by id.bru index cf7b9d4..65d3a01 100644 --- a/form-service/Question Response/Get response by id.bru +++ b/bruno/form-service/Question Response/Get response by id.bru @@ -5,7 +5,7 @@ meta { } get { - url: {{BASE_URL}}/question-responses/{RESPONSE_ID}} + url: {{BASE_URL}}/question-responses/{{RESPONSE_ID}} body: none auth: none } diff --git a/bruno/form-service/Question Response/Save Question Response.bru b/bruno/form-service/Question Response/Save Question Response.bru new file mode 100644 index 0000000..815de3d --- /dev/null +++ b/bruno/form-service/Question Response/Save Question Response.bru @@ -0,0 +1,31 @@ +meta { + name: Save Question Response + type: http + seq: 8 +} + +post { + url: {{BASE_URL}}/question-responses/save + body: text + auth: none +} + +body:text { + { + "QuestionResponses": [{ + "id": "a9ee5e15-cb9a-4989-8709-293c33563045", + "FormSubmissionId": "{{SUBMISSION_ID}}", + "QuestionId": "{{QUESTION_ID}}", + "ResponseType": "Text", + "IntegerValue": 1, + "FloatValue": 1.2, + "BooleanValue": "1", + "DateTimeValue": "2024-03-08T12:55:31.000Z", + "Url": "https://meet.google.com/zrb-tyni-rdz", + "FileResourceId": "0c9ea637-cb8d-468e-b4c7-303d2604c1b5", + "SubmissionTimestamp": null, + "LastSaveTimestamp": "2025-06-21T11:43:57.000Z" + }], + "FormSubmissionKey": "{{SUBMISSION_KEY}}" + } +} diff --git a/form-service/Question Response/Update a existing response.bru b/bruno/form-service/Question Response/Update a existing response.bru similarity index 99% rename from form-service/Question Response/Update a existing response.bru rename to bruno/form-service/Question Response/Update a existing response.bru index f40070d..48ff830 100644 --- a/form-service/Question Response/Update a existing response.bru +++ b/bruno/form-service/Question Response/Update a existing response.bru @@ -23,7 +23,7 @@ body:json { } } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Question Response/folder.bru b/bruno/form-service/Question Response/folder.bru new file mode 100644 index 0000000..d9415c7 --- /dev/null +++ b/bruno/form-service/Question Response/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Question Response + seq: 7 +} + +auth { + mode: none +} diff --git a/bruno/form-service/Question/Create a new question.bru b/bruno/form-service/Question/Create a new question.bru new file mode 100644 index 0000000..5997458 --- /dev/null +++ b/bruno/form-service/Question/Create a new question.bru @@ -0,0 +1,384 @@ +meta { + name: Create a new question + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/questions + body: json + auth: none +} + +body:json { + // { + // "ParentTemplateId": "d9e0416b-b142-4492-a522-a7fc57f9c224", //Form Template id + // "ParentSectionId": "36869bba-551f-4aaf-a150-ccafd9567254", //Form Section Id + // "Title": "Question Two", + // "Description": "Question Two description", + // "DisplayCode": "Question Two Display code", + // "ResponseType": "Integer", + // "Score": 11, + // "CorrectAnswer": "Question Two correct answer", + // "Hint": "Question Two hint", + // "Options":"option1,option2", + // "QuestionImageUrl":"this.is.image", + // "RangeMin":2, + // "RangeMax":4 + // }, + // { + // "ParentTemplateId": "d9e0416b-b142-4492-a522-a7fc57f9c224", + // "ParentSectionId": "36869bba-551f-4aaf-a150-ccafd9567254", + // "Title": "range ........!", + // "Description": "Birth Day", + // "ResponseType": "Boolean", + // "Score": 5, + // // "DisplayCode": "2b3b3ea7-d55f-46fb-901f-380a92be0059", + // "CorrectAnswer": "1234", + // "Hint": "date", + // "QuestionImageUrl": "a", + // "RangeMin": 1, + // "RangeMax": 2, + // "Options": "option1, option2" + // } + // { + // "ParentTemplateId": "5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a", + // "ParentSectionId": "a1b31378-59b0-4ca8-8d54-323798bf924e", + // "Title": "qqqqqqqqqqqqqqqq", + // "Description": "qqqqqqqqq", + // "ResponseType": "Text", + // "Score": 1, + // "CorrectAnswer": "qqqqqqqqqqqqqqq", + // "Hint": "qqqqqqqqqqqqq", + // "QuestionImageUrl": "qqqqqqqqqqqq", + // "RangeMin": null, + // "RangeMax": null, + // "Options": [ + // "option1", + // "option2" + // ] + // } + // { + // "ParentTemplateId": "5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a", + // "ParentSectionId": "a1b31378-59b0-4ca8-8d54-323798bf924e", + // "Title": "qqqqqqqqqqqqqqqq", + // "Description": "qqqqqqqqqq", + // "ResponseType": "SinglehoiceSelection", + // "Score": 1, + // "CorrectAnswer": "qqqqqqqqqqqqqqq", + // "Hint": "qqqqqqqqqqqqq", + // "QuestionImageUrl": "qqqqqqqqqqqq", + // "RangeMin": null, + // "RangeMax": null, + // "Options": [ + // { + // "Text": "qqqqq", + // "Sequence": 1 + // }, + // { + // "Text": "qqqq", + // "Sequence": 2 + // }, + // { + // "Text": "qq", + // "Sequence": 3 + // } + // ] + // } + { + "ParentTemplateId": "{{TEMPLATE_ID}}", + "ParentSectionId": "{{PARENT_SECTION_ID}}", + "ResponseType": "SingleChoiceSelection", + "Options": [ + { + "Text": "qqqqq", + "Sequence": "1", + "ImageUrl": "asdasfdsf" + }, + { + "Text": "qqqqq", + "Sequence": "2", + "ImageUrl": "asdasfdsf" + }, + { + "Text": "qqqqq", + "Sequence": "3", + "ImageUrl": "asdasfdsf" + }, + { + "Text": "qqqqq", + "Sequence": "1", + "ImageUrl": "asdasfdsf" + }, + { + "Text": "qqqqq", + "Sequence": "2", + "ImageUrl": "asdasfdsf" + }, + { + "Text": "qqqqq", + "Sequence": "3", + "ImageUrl": "asdasfdsf" + } + ] + } + + + + // { + // "Title": "What is your full name", + // "Description": "description", + // "ResponseType": "SingleChoiceSelection", + // "Options": [ + // { + // "Text": "sdfg", + // "Sequence": "a1", + // "ImageUrl": "wererterytryur" + // }, + // { + // "Text": "sdagdf", + // "Sequence": "a2", + // "ImageUrl": "asdf" + // }, + // { + // "Text": "dzfg", + // "Sequence": "a3", + // "ImageUrl": "zsdfg" + // }, + // { + // "Text": "zdfgdfg", + // "Sequence": "a4", + // "ImageUrl": "dfg" + // } + // ] + // } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("QUESTION_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successfull", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Question is created", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('ParentTemplateId'); + expect(jsonRes.Data).to.have.property('ParentSectionId'); + expect(jsonRes.Data).to.have.property('Title'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data).to.have.property('ResponseType'); + expect(jsonRes.Data).to.have.property('Score'); + expect(jsonRes.Data).to.have.property('CorrectAnswer'); + expect(jsonRes.Data).to.have.property('Hint'); + expect(jsonRes.Data).to.have.property('Options'); + expect(jsonRes.Data).to.have.property('QuestionImageUrl'); + expect(jsonRes.Data).to.have.property('RangeMin'); + expect(jsonRes.Data).to.have.property('RangeMax'); + + }); + var template = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldValue
Status{{response.Status}}
Message{{response.Message}}
HttpCode{{response.HttpCode}}
Request Method{{response.Request.Method}}
Request Host{{response.Request.Host}}
Request Body - ParentTemplateId{{response.Request.Body.ParentTemplateId}}
Request Body - ParentSectionId{{response.Request.Body.ParentSectionId}}
Request Body - ResponseType{{response.Request.Body.ResponseType}}
Request Body - Options{{response.Request.Body.Options}}
Request Url{{response.Request.Url}}
Request Params{{response.Request.Params}}
Data ID{{response.Data.id}}
Data Title{{response.Data.Title}}
Data Description{{response.Data.Description}}
Data DisplayCode{{response.Data.DisplayCode}}
Data ResponseType{{response.Data.ResponseType}}
Data Score{{response.Data.Score}}
Data Sequence{{response.Data.Sequence}}
Data CorrectAnswer{{response.Data.CorrectAnswer}}
Data Hint{{response.Data.Hint}}
Data Options{{response.Data.Options}}
Data QuestionImageUrl{{response.Data.QuestionImageUrl}}
Data RangeMin{{response.Data.RangeMin}}
Data RangeMax{{response.Data.RangeMax}}
Data ParentFormSection ID{{response.Data.ParentFormSection.id}}
Data ParentFormSection SectionIdentifier{{response.Data.ParentFormSection.SectionIdentifier}}
Data ParentFormSection Title{{response.Data.ParentFormSection.Title}}
Data ParentFormSection Description{{response.Data.ParentFormSection.Description}}
Data ParentFormSection DisplayCode{{response.Data.ParentFormSection.DisplayCode}}
Data ParentFormSection Sequence{{response.Data.ParentFormSection.Sequence}}
Data ParentFormSection ParentSectionId{{response.Data.ParentFormSection.ParentSectionId}}
Data ParentFormSection CreatedAt{{response.Data.ParentFormSection.CreatedAt}}
Data ParentFormTemplate ID{{response.Data.ParentFormTemplate.id}}
Data ParentFormTemplate Title{{response.Data.ParentFormTemplate.Title}}
Data ParentFormTemplate Description{{response.Data.ParentFormTemplate.Description}}
Data ParentFormTemplate Type{{response.Data.ParentFormTemplate.Type}}
Data ParentFormTemplate DisplayCode{{response.Data.ParentFormTemplate.DisplayCode}}
Data ParentFormTemplate OwnerUserId{{response.Data.ParentFormTemplate.OwnerUserId}}
Data ParentFormTemplate RootSectionId{{response.Data.ParentFormTemplate.RootSectionId}}
Data ParentFormTemplate DefaultSectionNumbering{{response.Data.ParentFormTemplate.DefaultSectionNumbering}}
Data ParentFormTemplate CreatedAt{{response.Data.ParentFormTemplate.CreatedAt}}
Data CreatedAt{{response.Data.CreatedAt}}
Data UpdatedAt{{response.Data.UpdatedAt}}
+ `; + + function constructVisualizerPayload() { + return {response: res.getBody()}; + } + + // pm.visualizer.set(template, constructVisualizerPayload()); +} diff --git a/form-service/Question/Delete a question record.bru b/bruno/form-service/Question/Delete a question record.bru similarity index 95% rename from form-service/Question/Delete a question record.bru rename to bruno/form-service/Question/Delete a question record.bru index d7b33a1..5108ce6 100644 --- a/form-service/Question/Delete a question record.bru +++ b/bruno/form-service/Question/Delete a question record.bru @@ -10,7 +10,7 @@ delete { auth: none } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/User/Generic search.bru b/bruno/form-service/Question/Generic search.bru similarity index 77% rename from form-service/User/Generic search.bru rename to bruno/form-service/Question/Generic search.bru index 188d756..5a6e515 100644 --- a/form-service/User/Generic search.bru +++ b/bruno/form-service/Question/Generic search.bru @@ -5,17 +5,16 @@ meta { } get { - url: {{BASE_URL}}/users/search?username=Username&password=Password + url: {{BASE_URL}}/questions/search?parentTemplateId={{TEMPLATE_ID}} body: none auth: none } params:query { - username: Username - password: Password + parentTemplateId: {{TEMPLATE_ID}} } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Question/Get Question By Template Id.bru b/bruno/form-service/Question/Get Question By Template Id.bru new file mode 100644 index 0000000..d762e08 --- /dev/null +++ b/bruno/form-service/Question/Get Question By Template Id.bru @@ -0,0 +1,11 @@ +meta { + name: Get Question By Template Id + type: http + seq: 6 +} + +get { + url: {{BASE_URL}}/questions/by-template-id/{{TEMPLATE_ID}} + body: none + auth: none +} diff --git a/form-service/Question/Get question by id.bru b/bruno/form-service/Question/Get question by id.bru similarity index 97% rename from form-service/Question/Get question by id.bru rename to bruno/form-service/Question/Get question by id.bru index d6b6c78..bcf49c3 100644 --- a/form-service/Question/Get question by id.bru +++ b/bruno/form-service/Question/Get question by id.bru @@ -6,11 +6,11 @@ meta { get { url: {{BASE_URL}}/questions/{{QUESTION_ID}} - body: none + body: json auth: none } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(201); var jsonRes = res.getBody(); diff --git a/form-service/Question/Update a question record.bru b/bruno/form-service/Question/Update a question record.bru similarity index 79% rename from form-service/Question/Update a question record.bru rename to bruno/form-service/Question/Update a question record.bru index 9e17ab7..02368ec 100644 --- a/form-service/Question/Update a question record.bru +++ b/bruno/form-service/Question/Update a question record.bru @@ -16,13 +16,20 @@ body:json { "ResponseType": "MultiChoiceSelection", "RangeMin": 1, "RangeMax": 2, - "Options": [ - "Acute Coronary Syndrome - STEMI,Acute Coronary Syndrome - NSTEMI,Acute Coronary Syndrome - Unstable Angina,Stable Coronary Artery Disease or Stable Angina,Heart Failure,Atrial Fibrillation/Flutter/SVT,Peripheral Arterial Disease,Percutaneous Cardiac Intervention (TAVI/ASD Repair/Valvotomies),Heart Valve replacement,High cardiovascular risk patient (High CVD risk score, Diabetes, Metabolic syndrome, Chronic Kidney Disease, Multiple risk factors),Other" + // "Options": [ + // "Acute Coronary Syndrome - STEMI,Acute Coronary Syndrome - NSTEMI,Acute Coronary Syndrome - Unstable Angina,Stable Coronary Artery Disease or Stable Angina,Heart Failure,Atrial Fibrillation/Flutter/SVT,Peripheral Arterial Disease,Percutaneous Cardiac Intervention (TAVI/ASD Repair/Valvotomies),Heart Valve replacement,High cardiovascular risk patient (High CVD risk score, Diabetes, Metabolic syndrome, Chronic Kidney Disease, Multiple risk factors),Other" + // ] + "Options": [ + { + "Text": "pppp", + "Sequence": "1", + "ImageUrl": "asdasfdsf" + } ] } } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Question/folder.bru b/bruno/form-service/Question/folder.bru new file mode 100644 index 0000000..f3913b1 --- /dev/null +++ b/bruno/form-service/Question/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Question + seq: 4 +} + +auth { + mode: none +} diff --git a/form-service/User Login Session/Create a new session.bru b/bruno/form-service/User Login Session/Create a new session.bru similarity index 100% rename from form-service/User Login Session/Create a new session.bru rename to bruno/form-service/User Login Session/Create a new session.bru diff --git a/form-service/User Login Session/Delete a existing session.bru b/bruno/form-service/User Login Session/Delete a existing session.bru similarity index 100% rename from form-service/User Login Session/Delete a existing session.bru rename to bruno/form-service/User Login Session/Delete a existing session.bru diff --git a/form-service/User Login Session/Get a login session by id.bru b/bruno/form-service/User Login Session/Get a login session by id.bru similarity index 100% rename from form-service/User Login Session/Get a login session by id.bru rename to bruno/form-service/User Login Session/Get a login session by id.bru diff --git a/form-service/User Login Session/Update a session.bru b/bruno/form-service/User Login Session/Update a session.bru similarity index 100% rename from form-service/User Login Session/Update a session.bru rename to bruno/form-service/User Login Session/Update a session.bru diff --git a/bruno/form-service/User Login Session/folder.bru b/bruno/form-service/User Login Session/folder.bru new file mode 100644 index 0000000..48a884f --- /dev/null +++ b/bruno/form-service/User Login Session/folder.bru @@ -0,0 +1,8 @@ +meta { + name: User Login Session + seq: 6 +} + +auth { + mode: none +} diff --git a/form-service/User/Delete user.bru b/bruno/form-service/User/Delete user.bru similarity index 94% rename from form-service/User/Delete user.bru rename to bruno/form-service/User/Delete user.bru index 9cd0680..c2633fe 100644 --- a/form-service/User/Delete user.bru +++ b/bruno/form-service/User/Delete user.bru @@ -10,7 +10,7 @@ delete { auth: none } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Form section/Generic search.bru b/bruno/form-service/User/Generic search.bru similarity index 72% rename from form-service/Form section/Generic search.bru rename to bruno/form-service/User/Generic search.bru index 65e3db5..d4d9dcf 100644 --- a/form-service/Form section/Generic search.bru +++ b/bruno/form-service/User/Generic search.bru @@ -5,16 +5,17 @@ meta { } get { - url: {{BASE_URL}}/form-sections/search?parentFormTemplateId=51688651-458d-4b8f-922e-28ce072cfa15 + url: {{BASE_URL}}/users/search?username="Username"&password="Password" body: none auth: none } params:query { - parentFormTemplateId: 51688651-458d-4b8f-922e-28ce072cfa15 + username: "Username" + password: "Password" } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/User/Get All Users.bru b/bruno/form-service/User/Get All Users.bru new file mode 100644 index 0000000..47ea1d6 --- /dev/null +++ b/bruno/form-service/User/Get All Users.bru @@ -0,0 +1,11 @@ +meta { + name: Get All Users + type: http + seq: 6 +} + +get { + url: {{BASE_URL}}/users/all + body: none + auth: none +} diff --git a/form-service/User/Get user by id.bru b/bruno/form-service/User/Get user by id.bru similarity index 97% rename from form-service/User/Get user by id.bru rename to bruno/form-service/User/Get user by id.bru index fa325a6..ca4b205 100644 --- a/form-service/User/Get user by id.bru +++ b/bruno/form-service/User/Get user by id.bru @@ -10,7 +10,7 @@ get { auth: none } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/User/New user.bru b/bruno/form-service/User/New user.bru similarity index 98% rename from form-service/User/New user.bru rename to bruno/form-service/User/New user.bru index 04db521..336ffb6 100644 --- a/form-service/User/New user.bru +++ b/bruno/form-service/User/New user.bru @@ -22,7 +22,7 @@ body:json { } } -tests { +script:post-response { try { var jsonRes = res.getBody(); bru.setEnvVar("USER_ID", jsonRes.Data.id); diff --git a/form-service/User/Update user record.bru b/bruno/form-service/User/Update user record.bru similarity index 98% rename from form-service/User/Update user record.bru rename to bruno/form-service/User/Update user record.bru index 8f34539..55a5971 100644 --- a/form-service/User/Update user record.bru +++ b/bruno/form-service/User/Update user record.bru @@ -22,7 +22,7 @@ body:json { } } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/User/folder.bru b/bruno/form-service/User/folder.bru new file mode 100644 index 0000000..b8e707b --- /dev/null +++ b/bruno/form-service/User/folder.bru @@ -0,0 +1,8 @@ +meta { + name: User + seq: 1 +} + +auth { + mode: none +} diff --git a/form-service/bruno.json b/bruno/form-service/bruno.json similarity index 100% rename from form-service/bruno.json rename to bruno/form-service/bruno.json diff --git a/bruno/form-service/collection.bru b/bruno/form-service/collection.bru new file mode 100644 index 0000000..412e8c0 --- /dev/null +++ b/bruno/form-service/collection.bru @@ -0,0 +1,8 @@ +meta { + name: form-service + seq: 1 +} + +auth { + mode: none +} diff --git a/bruno/form-service/environments/forms-service.bru b/bruno/form-service/environments/forms-service.bru new file mode 100644 index 0000000..e3d3610 --- /dev/null +++ b/bruno/form-service/environments/forms-service.bru @@ -0,0 +1,11 @@ +vars { + BASE_URL: http://localhost:5555/api/v1 + USER_ID: a760ea0b-93e6-4eec-b27b-e9ede7d3e57a + TEMPLATE_ID: 7d6fe0be-3f6c-4a8e-8231-b8a7568d2c9d + SECTION_ID: 1642c8d1-9849-4402-91f8-ae07bda27dd8 + QUESTION_ID: + SUBMISSION_ID: + RESPONSE_ID: + PARENT_SECTION_ID: 44071647-f61e-448b-8dec-ae2c8d3017c6 + SUBMISSION_KEY: +} diff --git a/form-service/Form Submission/get submission by template id.bru b/form-service/Form Submission/get submission by template id.bru deleted file mode 100644 index 6161a5d..0000000 --- a/form-service/Form Submission/get submission by template id.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: get submission by template id - type: http - seq: 4 -} - -get { - url: {{BASE_URL}}/form-submissions/by-template/5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a - body: none - auth: none -} diff --git a/form-service/Question/Create a new question.bru b/form-service/Question/Create a new question.bru deleted file mode 100644 index e3598b9..0000000 --- a/form-service/Question/Create a new question.bru +++ /dev/null @@ -1,160 +0,0 @@ -meta { - name: Create a new question - type: http - seq: 1 -} - -post { - url: {{BASE_URL}}/questions - body: json - auth: none -} - -body:json { - // { - // "ParentTemplateId": "d9e0416b-b142-4492-a522-a7fc57f9c224", //Form Template id - // "ParentSectionId": "36869bba-551f-4aaf-a150-ccafd9567254", //Form Section Id - // "Title": "Question Two", - // "Description": "Question Two description", - // "DisplayCode": "Question Two Display code", - // "ResponseType": "Integer", - // "Score": 11, - // "CorrectAnswer": "Question Two correct answer", - // "Hint": "Question Two hint", - // "Options":"option1,option2", - // "QuestionImageUrl":"this.is.image", - // "RangeMin":2, - // "RangeMax":4 - // }, - // { - // "ParentTemplateId": "d9e0416b-b142-4492-a522-a7fc57f9c224", - // "ParentSectionId": "36869bba-551f-4aaf-a150-ccafd9567254", - // "Title": "range ........!", - // "Description": "Birth Day", - // "ResponseType": "Boolean", - // "Score": 5, - // // "DisplayCode": "2b3b3ea7-d55f-46fb-901f-380a92be0059", - // "CorrectAnswer": "1234", - // "Hint": "date", - // "QuestionImageUrl": "a", - // "RangeMin": 1, - // "RangeMax": 2, - // "Options": "option1, option2" - // } - // { - // "ParentTemplateId": "5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a", - // "ParentSectionId": "a1b31378-59b0-4ca8-8d54-323798bf924e", - // "Title": "qqqqqqqqqqqqqqqq", - // "Description": "qqqqqqqqq", - // "ResponseType": "Text", - // "Score": 1, - // "CorrectAnswer": "qqqqqqqqqqqqqqq", - // "Hint": "qqqqqqqqqqqqq", - // "QuestionImageUrl": "qqqqqqqqqqqq", - // "RangeMin": null, - // "RangeMax": null, - // "Options": [ - // "option1", - // "option2" - // ] - // } - // { - // "ParentTemplateId": "5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a", - // "ParentSectionId": "a1b31378-59b0-4ca8-8d54-323798bf924e", - // "Title": "qqqqqqqqqqqqqqqq", - // "Description": "qqqqqqqqqq", - // "ResponseType": "SinglehoiceSelection", - // "Score": 1, - // "CorrectAnswer": "qqqqqqqqqqqqqqq", - // "Hint": "qqqqqqqqqqqqq", - // "QuestionImageUrl": "qqqqqqqqqqqq", - // "RangeMin": null, - // "RangeMax": null, - // "Options": [ - // { - // "Text": "qqqqq", - // "Sequence": 1 - // }, - // { - // "Text": "qqqq", - // "Sequence": 2 - // }, - // { - // "Text": "qq", - // "Sequence": 3 - // } - // ] - // } - { - "ParentTemplateId": "4fabef36-5992-4f9c-aa7b-2539ae24aa7e", - "ParentSectionId": "b8243963-7999-4623-b357-d1d16549d619", - "ResponseType": "SingleChoiceSelection", - "Options": [ - { - "Text": "qqqqq", - "Sequence": "1", - "ImageUrl": "asdasfdsf" - }, - { - "Text": "qqqqq", - "Sequence": "2", - "ImageUrl": "asdasfdsf" - }, - { - "Text": "qqqqq", - "Sequence": "3", - "ImageUrl": "asdasfdsf" - }, - { - "Text": "qqqqq", - "Sequence": "1", - "ImageUrl": "asdasfdsf" - }, - { - "Text": "qqqqq", - "Sequence": "2", - "ImageUrl": "asdasfdsf" - }, - { - "Text": "qqqqq", - "Sequence": "3", - "ImageUrl": "asdasfdsf" - } - ] - } -} - -tests { - try { - var jsonRes = res.getBody(); - bru.setEnvVar("QUESTION_ID", jsonRes.Data.id); - } - catch (error) { - console.log(error.message); - } - - test("Request is successfull", function () { - expect(res.getStatus()).to.equal(201); - var jsonRes = res.getBody(); - expect(jsonRes.Status).to.eql('Success'); - }); - - test("Question is created", function () { - var jsonRes = res.getBody(); - expect(jsonRes.Data).to.have.property('id'); - expect(jsonRes.Data).to.have.property('ParentTemplateId'); - expect(jsonRes.Data).to.have.property('ParentSectionId'); - expect(jsonRes.Data).to.have.property('Title'); - expect(jsonRes.Data).to.have.property('Description'); - expect(jsonRes.Data).to.have.property('DisplayCode'); - expect(jsonRes.Data).to.have.property('ResponseType'); - expect(jsonRes.Data).to.have.property('Score'); - expect(jsonRes.Data).to.have.property('CorrectAnswer'); - expect(jsonRes.Data).to.have.property('Hint'); - expect(jsonRes.Data).to.have.property('Options'); - expect(jsonRes.Data).to.have.property('QuestionImageUrl'); - expect(jsonRes.Data).to.have.property('RangeMin'); - expect(jsonRes.Data).to.have.property('RangeMax'); - - }); -} diff --git a/form-service/collection.bru b/form-service/collection.bru deleted file mode 100644 index e69de29..0000000 diff --git a/schema.json b/schema.json new file mode 100644 index 0000000..c533fe1 --- /dev/null +++ b/schema.json @@ -0,0 +1,115 @@ +{ + "OperationCreateModel": { + "Name": "string", + "Description": "string", + "ParentRuleId": "uuid", + "ParentConditionId": "uuid", + "OperatorType": "OperatorType", + "LogicalOperatorType": "LogicalOperatorType", + "CompositionOperatorType": "CompositionOperatorType", + "MathematicalOperatorType": "MathematicalOperatorType", + "FirstOperand": "Operand", + "SecondOperand": "Operand", + "ThirdOperand": "Operand" + }, + "OperatorType": { + "Logical": "Logical", + "Mathematical": "Mathematical", + "Composition": "Composition", + "Iterate": "Iterate" + }, + "LogicalOperatorType": { + "Equal": "Equal", + "NotEqual": "NotEqual", + "GreaterThan": "GreaterThan", + "GreaterThanOrEqual": "GreaterThanOrEqual", + "LessThan": "LessThan", + "LessThanOrEqual": "LessThanOrEqual", + "In": "In", + "NotIn": "NotIn", + "Contains": "Contains", + "DoesNotContain": "DoesNotContain", + "Between": "Between", + "IsTrue": "IsTrue", + "IsFalse": "IsFalse", + "Exists": "Exists", + "HasConsecutiveOccurrences": "HasConsecutiveOccurrences", + "RangesOverlap": "RangesOverlap", + "None": "None" + }, + "CompositionOperatorType": { + "And": "And", + "Or": "Or", + "Xor": "Xor", + "None": "None" + }, + "MathematicalOperatorType": { + "Add": "Add", + "Subtract": "Subtract", + "Divide": "Divide", + "Multiply": "Multiply", + "Percentage": "Percentage", + "None": "None" + }, + "OperandDataType": { + "Float": "Float", + "Integer": "Integer", + "Boolean": "Boolean", + "Text": "Text", + "Array": "Array", + "Object": "Object", + "Date": "Date" + }, + "AssessmentNodeType": { + "Message": "Message", + "Question": "Question", + "NodeList": "Node list" + }, + "Rule": { + "Id": "uuid", + "Name": "string", + "Description": "string", + "SkipMessage": "string", + "ParentRuleId": "uuid", + "ParentConditionId": "uuid", + "ConditionRules": "Rules" + }, + "Question": { + "Name": "string", + "Description": "string", + "Skip": "boolean", + "SkipMessage": "string", + "SkipRuleId": "uuid", + "CalculationLogicId": "uuid", + "CalculationLogicType": "CalculationLogicType", + "ConditionRules": "Rules", + "ValidationLogic": "string", + "ValidationLogicId": "uuid", + "ValidationLogicType": "ValidationLogicType" + }, + "CalculationLogicType": { + "Title": "string", + "Description": "string", + "Rule": "Rule", + "FallbackRule": "Rule" + }, + "ValidationLogic": { + "Title": "string", + "Description": "string", + "Rule": "Rule", + "RuleType": "RuleType", + "FallbackRule": "Rule" + }, + "RuleType": { + "Rule": "Rule", + "FallbackRule": "FallbackRule", + "RegEx": "RegEx" + }, + "RegEx": { + "Title": "string", + "Description": "string", + "RegEx": "string", + "RegExType": "RegExType", + "FallbackRegEx": "string" + } +} \ No newline at end of file diff --git a/src/api/favorite.template/favorite.template.controller.ts b/src/api/favorite.template/favorite.template.controller.ts new file mode 100644 index 0000000..34069fa --- /dev/null +++ b/src/api/favorite.template/favorite.template.controller.ts @@ -0,0 +1,79 @@ +import express from 'express'; +import { ResponseHandler } from '../../common/handlers/response.handler'; +import { FavoriteTemplateValidator } from './favorite.template.validator'; +import { BaseController } from '../base.controller'; +import { ErrorHandler } from '../../common/handlers/error.handler'; +import { uuid } from '../../domain.types/miscellaneous/system.types'; +import { FavoriteTemplateService } from '../../services/favorite.template/favorite.template.service'; +import { FavoriteTemplateCreateModel, FavoriteTemplateSearchFilters, FavoriteTemplateUpdateModel } from '../../domain.types/forms/favorite.template.domain.types'; +import { container } from 'tsyringe'; +import { Injector } from '../../startup/injector'; + +export class FavoriteTemplateController extends BaseController { + + _service: FavoriteTemplateService = Injector.Container.resolve(FavoriteTemplateService); + _validator: FavoriteTemplateValidator = new FavoriteTemplateValidator(); + + constructor() { + super(); + } + + create = async (request: express.Request, response: express.Response) => { + try { + const model: FavoriteTemplateCreateModel = await this._validator.validateCreateRequest(request); + const record = await this._service.create(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to add favorite template!', new Error()); + } + const message = 'Favorite template added successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getById(id); + const message = 'Favorite template retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + update = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: FavoriteTemplateUpdateModel = await this._validator.validateUpdateRequest(request); + const updatedRecord = await this._service.update(id, model); + const message = 'Favorite template updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + delete = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.delete(id); + const message = 'Favorite template deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + search = async (request: express.Request, response: express.Response) => { + try { + const filters: FavoriteTemplateSearchFilters = await this._validator.validateSearchRequest(request); + const searchResults = await this._service.search(filters); + const message = 'Favorite templates retrieved successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; +} \ No newline at end of file diff --git a/src/api/favorite.template/favorite.template.router.ts b/src/api/favorite.template/favorite.template.router.ts new file mode 100644 index 0000000..b1601ad --- /dev/null +++ b/src/api/favorite.template/favorite.template.router.ts @@ -0,0 +1,16 @@ +import express from 'express'; +import { FavoriteTemplateController } from './favorite.template.controller'; + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new FavoriteTemplateController(); + + router.get('/search', controller.search); + router.post('/', controller.create); + router.put('/:id', controller.update); + router.get('/:id', controller.getById); + router.delete('/:id', controller.delete); + + app.use('/api/v1/favorite-templates', router); +}; \ No newline at end of file diff --git a/src/api/favorite.template/favorite.template.validator.ts b/src/api/favorite.template/favorite.template.validator.ts new file mode 100644 index 0000000..9e33d49 --- /dev/null +++ b/src/api/favorite.template/favorite.template.validator.ts @@ -0,0 +1,102 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../common/handlers/error.handler'; +import BaseValidator from '../base.validator'; +import { FavoriteTemplateCreateModel, FavoriteTemplateSearchFilters, FavoriteTemplateUpdateModel } from '../../domain.types/forms/favorite.template.domain.types'; +import { ParsedQs } from 'qs'; + +export class FavoriteTemplateValidator extends BaseValidator { + + public validateCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + UserId: joi.string().uuid().required(), + TemplateId: joi.string().uuid().required() + }); + await schema.validateAsync(request.body); + return { + UserId: request.body.UserId, + TemplateId: request.body.TemplateId + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + UserId: joi.string().uuid().optional(), + TemplateId: joi.string().uuid().optional() + }); + await schema.validateAsync(request.body); + return { + UserId: request.body.UserId ?? null, + TemplateId: request.body.TemplateId ?? null + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + userId: joi.string().uuid().optional(), + templateId: joi.string().uuid().optional(), + itemsPerPage: joi.number().optional(), + pageIndex: joi.number().optional(), + orderBy: joi.string().optional(), + order: joi.string().optional() + }); + + await schema.validateAsync(request.query); + const filters = this.getSearchFilters(request.query); + return filters; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + private getSearchFilters = (query: ParsedQs): FavoriteTemplateSearchFilters => { + const filters: any = {}; + + const id = query.id ? query.id : null; + if (id != null) { + filters['id'] = id; + } + + const userId = query.userId ? query.userId : null; + if (userId != null) { + filters['UserId'] = userId; + } + + const templateId = query.templateId ? query.templateId : null; + if (templateId != null) { + filters['TemplateId'] = templateId; + } + + const itemsPerPage = query.itemsPerPage ? query.itemsPerPage : 25; + if (itemsPerPage != null) { + filters['ItemsPerPage'] = Number(itemsPerPage); + } + + const orderBy = query.orderBy ? query.orderBy : 'CreatedAt'; + if (orderBy != null) { + filters['OrderBy'] = orderBy; + } + + const order = query.order ? query.order : 'ASC'; + if (order != null) { + filters['Order'] = order; + } + + const pageIndex = query.pageIndex ? query.pageIndex : 0; + if (pageIndex != null) { + filters['PageIndex'] = pageIndex; + } + + return filters; + }; +} \ No newline at end of file diff --git a/src/api/form.section/form.section.controller.ts b/src/api/form.section/form.section.controller.ts index 81cd97f..9cb9ef4 100644 --- a/src/api/form.section/form.section.controller.ts +++ b/src/api/form.section/form.section.controller.ts @@ -20,7 +20,7 @@ export class FormSectionController extends BaseController { _service: FormSectionService = Injector.Container.resolve(FormSectionService); - _templService: FormTemplateService=Injector.Container.resolve(FormTemplateService); + _templService: FormTemplateService = Injector.Container.resolve(FormTemplateService); _validator: FormSectionValidator = new FormSectionValidator(); @@ -137,16 +137,15 @@ export class FormSectionController extends BaseController { // } // }); - const templateData=await this._templService.getById(parentTemplateId); + const templateData = await this._templService.getById(parentTemplateId); - if(templateData.DefaultSectionNumbering === true) - { - sequence = (Object.keys(sectionsByTemplateId).length + 1); + if (templateData.DefaultSectionNumbering === true) { + sequence = (Object.keys(sectionsByTemplateId).length + 1); } - else{ + else { sequence = request.body.Sequence; } - + model.Sequence = sequence; const record = await this._service.create(model); if (record === null) { diff --git a/src/api/form.section/form.section.validator.ts b/src/api/form.section/form.section.validator.ts index dada3fa..c2c415a 100644 --- a/src/api/form.section/form.section.validator.ts +++ b/src/api/form.section/form.section.validator.ts @@ -19,7 +19,7 @@ export class FormSectionValidator extends BaseValidator { Title: joi.string().optional(), Description: joi.string().optional(), // SectionIdentifier: joi.string().optional(), - DisplayCode: joi.string().optional(), + // DisplayCode: joi.string().optional(), Sequence: joi.number().optional(), ParentSectionId: joi.string().uuid().optional(), }); diff --git a/src/api/form.submission/form.controller.ts b/src/api/form.submission/form.controller.ts index d3695c6..17147da 100644 --- a/src/api/form.submission/form.controller.ts +++ b/src/api/form.submission/form.controller.ts @@ -32,16 +32,15 @@ export class FormController extends BaseController { create = async (request: express.Request, response: express.Response) => { try { let model: FormSubmissionCreateModel = await this._validator.validateCreateRequest(request); - + const template = await this._formTemplateService.getById(model.FormTemplateId); if (!template) { ErrorHandler.throwNotFoundError('Template not found!'); } - if(model.Title == null) - { - model.Title=template.Title; + if (model.Title == null) { + model.Title = template.Title; } const record = await this._service.create(model); @@ -53,7 +52,7 @@ export class FormController extends BaseController { const formSubmissionUpdateModel: FormSubmissionUpdateModel = {}; formSubmissionUpdateModel.Encrypted = this.generateUniqueKey(`id=${record.id}${model.UserId ? `&userId=${record.UserId}` : ''}`); - + if (!formSubmissionUpdateModel.Encrypted) { ErrorHandler.throwInternalServerError('Unable to generate form link!', {}); } @@ -98,14 +97,14 @@ export class FormController extends BaseController { try { const id = await this._validator.validateParamAsUUID(request, 'id'); - const formSubmission = await this._service.getById(id); + const formSubmission = await this._service.getById(id); if (!formSubmission) { ErrorHandler.throwNotFoundError('Form submission not found!'); } var model: FormSubmissionUpdateModel = await this._validator.validateUpdateRequest(request); - + if (model.UserId) { formSubmission.LinkQueryParams.UserId = model.UserId; model.LinkQueryParams = JSON.stringify(formSubmission.LinkQueryParams); @@ -127,7 +126,7 @@ export class FormController extends BaseController { ErrorHandler.throwNotFoundError('Form submission not found!'); } const result = await this._service.delete(id); - if(!result) { + if (!result) { ErrorHandler.throwNotFoundError('Form not found!'); } const message = 'Form deleted successfully!'; @@ -141,10 +140,10 @@ export class FormController extends BaseController { try { const SubmissionKey = await this._validator.validateSubmitRequest(request); - const formSubmission = await this._service.search({Encrypted: SubmissionKey}); + const formSubmission = await this._service.search({ Encrypted: SubmissionKey }); if (formSubmission.Items?.length !== 1) { - ErrorHandler.throwNotFoundError('Form submission not found!'); + ErrorHandler.throwNotFoundError('Form submission not found!'); } const submission = formSubmission.Items[0]; @@ -174,16 +173,16 @@ export class FormController extends BaseController { } }; - private generateUniqueKey = (input: string): string =>{ + private generateUniqueKey = (input: string): string => { try { const privateKey = process.env.PRIVATE_KEY; return crypto.createHmac("sha256", privateKey) - .update(input) - .digest("hex"); + .update(input) + .digest("hex"); } catch (error) { return null; } - + } } diff --git a/src/api/form.template.approval/form.template.approval.controller.ts b/src/api/form.template.approval/form.template.approval.controller.ts new file mode 100644 index 0000000..fb8066b --- /dev/null +++ b/src/api/form.template.approval/form.template.approval.controller.ts @@ -0,0 +1,89 @@ +import express from 'express'; +import { ResponseHandler } from '../../common/handlers/response.handler'; +import { FormTemplateApprovalValidator } from './form.template.approval.validator'; +import { BaseController } from '../base.controller'; +import { ErrorHandler } from '../../common/handlers/error.handler'; +import { uuid } from '../../domain.types/miscellaneous/system.types'; +import { FormTemplateApprovalService } from '../../services/form.template.approval/form.template.approval.service'; +import { FormTemplateApprovalCreateModel, FormTemplateApprovalSearchFilters, FormTemplateApprovalUpdateModel } from '../../domain.types/forms/form.template.approval.domain.types'; +import { Injector } from '../../startup/injector'; + +export class FormTemplateApprovalController extends BaseController { + + _service: FormTemplateApprovalService = Injector.Container.resolve(FormTemplateApprovalService); + _validator: FormTemplateApprovalValidator = new FormTemplateApprovalValidator(); + + constructor() { + super(); + } + + create = async (request: express.Request, response: express.Response) => { + try { + const model: FormTemplateApprovalCreateModel = await this._validator.validateCreateRequest(request); + const record = await this._service.create(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to add form template approval!', new Error()); + } + const message = 'Form template approval added successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getById(id); + const message = 'Form template approval retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getByTemplateId = async (request: express.Request, response: express.Response) => { + try { + const templateId: uuid = await this._validator.validateParamAsUUID(request, 'templateId'); + const record = await this._service.getByTemplateId(templateId); + const message = 'Form template approval retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + update = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: FormTemplateApprovalUpdateModel = await this._validator.validateUpdateRequest(request); + const updatedRecord = await this._service.update(id, model); + const message = 'Form template approval updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + delete = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.delete(id); + const message = 'Form template approval deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + search = async (request: express.Request, response: express.Response) => { + try { + const filters: FormTemplateApprovalSearchFilters = await this._validator.validateSearchRequest(request); + const searchResults = await this._service.search(filters); + const message = 'Form template approvals retrieved successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; +} \ No newline at end of file diff --git a/src/api/form.template.approval/form.template.approval.router.ts b/src/api/form.template.approval/form.template.approval.router.ts new file mode 100644 index 0000000..8877efd --- /dev/null +++ b/src/api/form.template.approval/form.template.approval.router.ts @@ -0,0 +1,16 @@ +import express from 'express'; +import { FormTemplateApprovalController } from './form.template.approval.controller'; + +export const register = (app: express.Application): void => { + const router = express.Router(); + const controller = new FormTemplateApprovalController(); + + router.get('/search', controller.search); + router.post('/', controller.create); + router.put('/:id', controller.update); + router.get('/:id', controller.getById); + router.get('/template/:templateId', controller.getByTemplateId); + router.delete('/:id', controller.delete); + + app.use('/api/v1/form-template-approvals', router); +}; \ No newline at end of file diff --git a/src/api/form.template.approval/form.template.approval.validator.ts b/src/api/form.template.approval/form.template.approval.validator.ts new file mode 100644 index 0000000..a694837 --- /dev/null +++ b/src/api/form.template.approval/form.template.approval.validator.ts @@ -0,0 +1,116 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../common/handlers/error.handler'; +import BaseValidator from '../base.validator'; +import { FormTemplateApprovalCreateModel, FormTemplateApprovalSearchFilters, FormTemplateApprovalUpdateModel } from '../../domain.types/forms/form.template.approval.domain.types'; +import { ParsedQs } from 'qs'; + +export class FormTemplateApprovalValidator extends BaseValidator { + + public validateCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + ApproverUserId: joi.string().uuid().required(), + TemplateId: joi.string().uuid().required(), + Approved: joi.boolean().required(), + ReviewComments: joi.string().max(512).optional() + }); + await schema.validateAsync(request.body); + return { + ApproverUserId: request.body.ApproverUserId, + TemplateId: request.body.TemplateId, + Approved: request.body.Approved, + ReviewComments: request.body.ReviewComments + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + ApproverUserId: joi.string().uuid().optional(), + TemplateId: joi.string().uuid().optional(), + Approved: joi.boolean().optional(), + ReviewComments: joi.string().max(512).optional() + }); + await schema.validateAsync(request.body); + return { + ApproverUserId: request.body.ApproverUserId ?? null, + TemplateId: request.body.TemplateId ?? null, + Approved: request.body.Approved ?? null, + ReviewComments: request.body.ReviewComments ?? null + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + approverUserId: joi.string().uuid().optional(), + templateId: joi.string().uuid().optional(), + approved: joi.boolean().optional(), + itemsPerPage: joi.number().optional(), + pageIndex: joi.number().optional(), + orderBy: joi.string().optional(), + order: joi.string().optional() + }); + + await schema.validateAsync(request.query); + const filters = this.getSearchFilters(request.query); + return filters; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + private getSearchFilters = (query: ParsedQs): FormTemplateApprovalSearchFilters => { + const filters: any = {}; + + const id = query.id ? query.id : null; + if (id != null) { + filters['id'] = id; + } + + const approverUserId = query.approverUserId ? query.approverUserId : null; + if (approverUserId != null) { + filters['ApproverUserId'] = approverUserId; + } + + const templateId = query.templateId ? query.templateId : null; + if (templateId != null) { + filters['TemplateId'] = templateId; + } + + const approved = query.approved !== undefined ? query.approved : null; + if (approved != null) { + filters['Approved'] = approved === 'true'; + } + + const itemsPerPage = query.itemsPerPage ? query.itemsPerPage : 25; + if (itemsPerPage != null) { + filters['ItemsPerPage'] = Number(itemsPerPage); + } + + const orderBy = query.orderBy ? query.orderBy : 'CreatedAt'; + if (orderBy != null) { + filters['OrderBy'] = orderBy; + } + + const order = query.order ? query.order : 'ASC'; + if (order != null) { + filters['Order'] = order; + } + + const pageIndex = query.pageIndex ? query.pageIndex : 0; + if (pageIndex != null) { + filters['PageIndex'] = pageIndex; + } + + return filters; + }; +} \ No newline at end of file diff --git a/src/api/input.unit.list/input.unit.list.controller.ts b/src/api/input.unit.list/input.unit.list.controller.ts new file mode 100644 index 0000000..982e9c2 --- /dev/null +++ b/src/api/input.unit.list/input.unit.list.controller.ts @@ -0,0 +1,77 @@ +import express from 'express'; +import { ResponseHandler } from '../../common/handlers/response.handler'; +import { InputUnitListValidator } from './input.unit.list.validator'; +import { BaseController } from '../base.controller'; +import { ErrorHandler } from '../../common/handlers/error.handler'; +import { uuid } from '../../domain.types/miscellaneous/system.types'; +import { InputUnitListService } from '../../services/input.unit.list/input.unit.list.service'; +import { InputUnitListCreateModel, InputUnitListSearchFilters, InputUnitListUpdateModel } from '../../domain.types/forms/input.unit.list.domain.types'; +import { Injector } from '../../startup/injector'; + +export class InputUnitListController extends BaseController { + _service: InputUnitListService = Injector.Container.resolve(InputUnitListService); + _validator: InputUnitListValidator = new InputUnitListValidator(); + + constructor() { + super(); + } + + create = async (request: express.Request, response: express.Response) => { + try { + const model: InputUnitListCreateModel = await this._validator.validateCreateRequest(request); + const record = await this._service.create(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to add input unit list!', new Error()); + } + const message = 'Input unit list added successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getById(id); + const message = 'Input unit list retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + update = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: InputUnitListUpdateModel = await this._validator.validateUpdateRequest(request); + const updatedRecord = await this._service.update(id, model); + const message = 'Input unit list updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + delete = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.delete(id); + const message = 'Input unit list deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + search = async (request: express.Request, response: express.Response) => { + try { + const filters: InputUnitListSearchFilters = await this._validator.validateSearchRequest(request); + const searchResults = await this._service.search(filters); + const message = 'Input unit lists retrieved successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; +} \ No newline at end of file diff --git a/src/api/input.unit.list/input.unit.list.router.ts b/src/api/input.unit.list/input.unit.list.router.ts new file mode 100644 index 0000000..d59f46f --- /dev/null +++ b/src/api/input.unit.list/input.unit.list.router.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import { InputUnitListController } from './input.unit.list.controller'; + +export const register = (app: express.Application): void => { + const router = express.Router(); + const controller = new InputUnitListController(); + + router.get('/search', controller.search); + router.post('/', controller.create); + router.put('/:id', controller.update); + router.get('/:id', controller.getById); + router.delete('/:id', controller.delete); + + app.use('/api/v1/input-unit-lists', router); +}; \ No newline at end of file diff --git a/src/api/input.unit.list/input.unit.list.validator.ts b/src/api/input.unit.list/input.unit.list.validator.ts new file mode 100644 index 0000000..f096be1 --- /dev/null +++ b/src/api/input.unit.list/input.unit.list.validator.ts @@ -0,0 +1,91 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../common/handlers/error.handler'; +import BaseValidator from '../base.validator'; +import { InputUnitListCreateModel, InputUnitListSearchFilters, InputUnitListUpdateModel } from '../../domain.types/forms/input.unit.list.domain.types'; +import { ParsedQs } from 'qs'; + +export class InputUnitListValidator extends BaseValidator { + public validateCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().max(128).required(), + Description: joi.string().max(512).required(), + Units: joi.array().required() + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Units: request.body.Units + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().max(128).optional(), + Description: joi.string().max(512).optional(), + Units: joi.array().optional() + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name ?? null, + Description: request.body.Description ?? null, + Units: request.body.Units ?? null + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + name: joi.string().optional(), + itemsPerPage: joi.number().optional(), + pageIndex: joi.number().optional(), + orderBy: joi.string().optional(), + order: joi.string().optional() + }); + await schema.validateAsync(request.query); + const filters = this.getSearchFilters(request.query); + return filters; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + private getSearchFilters = (query: ParsedQs): InputUnitListSearchFilters => { + const filters: any = {}; + const id = query.id ? query.id : null; + if (id != null) { + filters['id'] = id; + } + const name = query.name ? query.name : null; + if (name != null) { + filters['Name'] = name; + } + const itemsPerPage = query.itemsPerPage ? query.itemsPerPage : 25; + if (itemsPerPage != null) { + filters['ItemsPerPage'] = Number(itemsPerPage); + } + const orderBy = query.orderBy ? query.orderBy : 'CreatedAt'; + if (orderBy != null) { + filters['OrderBy'] = orderBy; + } + const order = query.order ? query.order : 'ASC'; + if (order != null) { + filters['Order'] = order; + } + const pageIndex = query.pageIndex ? query.pageIndex : 0; + if (pageIndex != null) { + filters['PageIndex'] = pageIndex; + } + return filters; + }; +} \ No newline at end of file diff --git a/src/api/question.response/question.response.controller.ts b/src/api/question.response/question.response.controller.ts index bea8fad..6e0f7a7 100644 --- a/src/api/question.response/question.response.controller.ts +++ b/src/api/question.response/question.response.controller.ts @@ -115,7 +115,8 @@ export class QuestionResponseController extends BaseController { DateTimeValue: model.DateTimeValue, Url: model.Url, FileResourceId: model.FileResourceId, - TextValue: model.TextValue + TextValue: model.TextValue, + UserResponse: model.UserResponse ?? null } await this._service.create(createModel); } diff --git a/src/api/question.response/question.response.validator.ts b/src/api/question.response/question.response.validator.ts index cc2cd9d..b11ce65 100644 --- a/src/api/question.response/question.response.validator.ts +++ b/src/api/question.response/question.response.validator.ts @@ -31,6 +31,7 @@ export class QuestionResponseValidator extends BaseValidator { Url: joi.string().optional(), FileResourceId: joi.string().optional(), TextValue: joi.string().optional(), + UserResponse: joi.string().optional(), }); await schema.validateAsync(request.body); return { @@ -44,6 +45,7 @@ export class QuestionResponseValidator extends BaseValidator { Url: request.body.Url ?? null, FileResourceId: request.body.FileResourceId ?? null, TextValue: request.body.TextValue ?? null, + UserResponse: request.body.UserResponse ?? null, }; } catch (error) { ErrorHandler.handleValidationError(error); diff --git a/src/api/template.folder/template.folder.controller.ts b/src/api/template.folder/template.folder.controller.ts new file mode 100644 index 0000000..5b15d88 --- /dev/null +++ b/src/api/template.folder/template.folder.controller.ts @@ -0,0 +1,77 @@ +import express from 'express'; +import { ResponseHandler } from '../../common/handlers/response.handler'; +import { TemplateFolderValidator } from './template.folder.validator'; +import { BaseController } from '../base.controller'; +import { ErrorHandler } from '../../common/handlers/error.handler'; +import { uuid } from '../../domain.types/miscellaneous/system.types'; +import { TemplateFolderService } from '../../services/template.folder/template.folder.service'; +import { TemplateFolderCreateModel, TemplateFolderSearchFilters, TemplateFolderUpdateModel } from '../../domain.types/forms/template.folder.domain.types'; +import { Injector } from '../../startup/injector'; + +export class TemplateFolderController extends BaseController { + _service: TemplateFolderService = Injector.Container.resolve(TemplateFolderService); + _validator: TemplateFolderValidator = new TemplateFolderValidator(); + + constructor() { + super(); + } + + create = async (request: express.Request, response: express.Response) => { + try { + const model: TemplateFolderCreateModel = await this._validator.validateCreateRequest(request); + const record = await this._service.create(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to add template folder!', new Error()); + } + const message = 'Template folder added successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getById(id); + const message = 'Template folder retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + update = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: TemplateFolderUpdateModel = await this._validator.validateUpdateRequest(request); + const updatedRecord = await this._service.update(id, model); + const message = 'Template folder updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + delete = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.delete(id); + const message = 'Template folder deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + search = async (request: express.Request, response: express.Response) => { + try { + const filters: TemplateFolderSearchFilters = await this._validator.validateSearchRequest(request); + const searchResults = await this._service.search(filters); + const message = 'Template folders retrieved successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; +} \ No newline at end of file diff --git a/src/api/template.folder/template.folder.router.ts b/src/api/template.folder/template.folder.router.ts new file mode 100644 index 0000000..9ee6c24 --- /dev/null +++ b/src/api/template.folder/template.folder.router.ts @@ -0,0 +1,15 @@ +import express from 'express'; +import { TemplateFolderController } from './template.folder.controller'; + +export const register = (app: express.Application): void => { + const router = express.Router(); + const controller = new TemplateFolderController(); + + router.get('/search', controller.search); + router.post('/', controller.create); + router.put('/:id', controller.update); + router.get('/:id', controller.getById); + router.delete('/:id', controller.delete); + + app.use('/api/v1/template-folders', router); +}; \ No newline at end of file diff --git a/src/api/template.folder/template.folder.validator.ts b/src/api/template.folder/template.folder.validator.ts new file mode 100644 index 0000000..4a47ae6 --- /dev/null +++ b/src/api/template.folder/template.folder.validator.ts @@ -0,0 +1,96 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../common/handlers/error.handler'; +import BaseValidator from '../base.validator'; +import { TemplateFolderCreateModel, TemplateFolderSearchFilters, TemplateFolderUpdateModel } from '../../domain.types/forms/template.folder.domain.types'; +import { ParsedQs } from 'qs'; + +export class TemplateFolderValidator extends BaseValidator { + public validateCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().max(128).required(), + Description: joi.string().max(512).optional(), + ParentFolderId: joi.string().uuid().optional() + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + ParentFolderId: request.body.ParentFolderId + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().max(128).optional(), + Description: joi.string().max(512).optional(), + ParentFolderId: joi.string().uuid().optional() + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name ?? null, + Description: request.body.Description ?? null, + ParentFolderId: request.body.ParentFolderId ?? null + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + name: joi.string().optional(), + parentFolderId: joi.string().uuid().optional(), + itemsPerPage: joi.number().optional(), + pageIndex: joi.number().optional(), + orderBy: joi.string().optional(), + order: joi.string().optional() + }); + await schema.validateAsync(request.query); + const filters = this.getSearchFilters(request.query); + return filters; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + private getSearchFilters = (query: ParsedQs): TemplateFolderSearchFilters => { + const filters: any = {}; + const id = query.id ? query.id : null; + if (id != null) { + filters['id'] = id; + } + const name = query.name ? query.name : null; + if (name != null) { + filters['Name'] = name; + } + const parentFolderId = query.parentFolderId ? query.parentFolderId : null; + if (parentFolderId != null) { + filters['ParentFolderId'] = parentFolderId; + } + const itemsPerPage = query.itemsPerPage ? query.itemsPerPage : 25; + if (itemsPerPage != null) { + filters['ItemsPerPage'] = Number(itemsPerPage); + } + const orderBy = query.orderBy ? query.orderBy : 'CreatedAt'; + if (orderBy != null) { + filters['OrderBy'] = orderBy; + } + const order = query.order ? query.order : 'ASC'; + if (order != null) { + filters['Order'] = order; + } + const pageIndex = query.pageIndex ? query.pageIndex : 0; + if (pageIndex != null) { + filters['PageIndex'] = pageIndex; + } + return filters; + }; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/favorite.template/favorite.template.repo.interface.ts b/src/database/repository.interfaces/favorite.template/favorite.template.repo.interface.ts new file mode 100644 index 0000000..b7fc4ab --- /dev/null +++ b/src/database/repository.interfaces/favorite.template/favorite.template.repo.interface.ts @@ -0,0 +1,13 @@ +import { FavoriteTemplateCreateModel, FavoriteTemplateResponseDto, FavoriteTemplateSearchFilters, FavoriteTemplateUpdateModel } from "../../../domain.types/forms/favorite.template.domain.types"; + +export interface IFavoriteTemplateRepo { + create(model: FavoriteTemplateCreateModel): Promise; + + update(id: string, model: FavoriteTemplateUpdateModel): Promise; + + getById(id: string): Promise; + + delete(id: string): Promise; + + search(filters: FavoriteTemplateSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/form.section/form.section.repo.interface.ts b/src/database/repository.interfaces/form.section/form.section.repo.interface.ts index b8fc428..b16f16b 100644 --- a/src/database/repository.interfaces/form.section/form.section.repo.interface.ts +++ b/src/database/repository.interfaces/form.section/form.section.repo.interface.ts @@ -1,6 +1,11 @@ -import { FormSectionCreateModel, FormSectionResponseDto, FormSectionSearchFilters, FormSectionUpdateModel } from "../../../domain.types/forms/form.section.domain.types"; +import { + FormSectionCreateModel, + FormSectionResponseDto, + FormSectionSearchFilters, + FormSectionUpdateModel +} from "../../../domain.types/forms/form.section.domain.types"; -export interface IFormSectionRepo{ +export interface IFormSectionRepo { create(model: FormSectionCreateModel): Promise; @@ -12,6 +17,6 @@ export interface IFormSectionRepo{ getByTemplateId(id: string): Promise; - search(filters: FormSectionSearchFilters) : Promise; + search(filters: FormSectionSearchFilters): Promise; } \ No newline at end of file diff --git a/src/database/repository.interfaces/form.template.approval/form.template.approval.repo.interface.ts b/src/database/repository.interfaces/form.template.approval/form.template.approval.repo.interface.ts new file mode 100644 index 0000000..1196e10 --- /dev/null +++ b/src/database/repository.interfaces/form.template.approval/form.template.approval.repo.interface.ts @@ -0,0 +1,16 @@ +import { FormTemplateApprovalCreateModel, FormTemplateApprovalResponseDto, FormTemplateApprovalSearchFilters, FormTemplateApprovalUpdateModel } from "../../../domain.types/forms/form.template.approval.domain.types"; + +export interface IFormTemplateApprovalRepo { + + create(model: FormTemplateApprovalCreateModel): Promise; + + update(id: string, model: FormTemplateApprovalUpdateModel): Promise; + + getById(id: string): Promise; + + getByTemplateId(templateId: string): Promise; + + delete(id: string): Promise; + + search(filters: FormTemplateApprovalSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/input.unit.list/input.unit.list.repo.interface.ts b/src/database/repository.interfaces/input.unit.list/input.unit.list.repo.interface.ts new file mode 100644 index 0000000..87585da --- /dev/null +++ b/src/database/repository.interfaces/input.unit.list/input.unit.list.repo.interface.ts @@ -0,0 +1,14 @@ +import { InputUnitListCreateModel, InputUnitListResponseDto, InputUnitListSearchFilters, InputUnitListUpdateModel } from "../../../domain.types/forms/input.unit.list.domain.types"; + +export interface IInputUnitListRepo { + + create(model: InputUnitListCreateModel): Promise; + + update(id: string, model: InputUnitListUpdateModel): Promise; + + getById(id: string): Promise; + + delete(id: string): Promise; + + search(filters: InputUnitListSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/template.folder/template.folder.repo.interface.ts b/src/database/repository.interfaces/template.folder/template.folder.repo.interface.ts new file mode 100644 index 0000000..55a00f1 --- /dev/null +++ b/src/database/repository.interfaces/template.folder/template.folder.repo.interface.ts @@ -0,0 +1,14 @@ +import { TemplateFolderCreateModel, TemplateFolderResponseDto, TemplateFolderSearchFilters, TemplateFolderUpdateModel } from "../../../domain.types/forms/template.folder.domain.types"; + +export interface ITemplateFolderRepo { + + create(model: TemplateFolderCreateModel): Promise; + + update(id: string, model: TemplateFolderUpdateModel): Promise; + + getById(id: string): Promise; + + delete(id: string): Promise; + + search(filters: TemplateFolderSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/favorite.template.mapper.ts b/src/database/sql/typeorm/mappers/favorite.template.mapper.ts new file mode 100644 index 0000000..3d7a56b --- /dev/null +++ b/src/database/sql/typeorm/mappers/favorite.template.mapper.ts @@ -0,0 +1,25 @@ +import { FavoriteTemplateResponseDto } from "../../../../domain.types/forms/favorite.template.domain.types"; + +export class FavoriteTemplateMapper { + static toDto = (record: any): FavoriteTemplateResponseDto => { + if (record === null) { + return null; + } + + const dto: FavoriteTemplateResponseDto = { + id: record.id, + UserId: record.UserId, + TemplateId: record.TemplateId, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toArrayDto(records: any[]): FavoriteTemplateResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => FavoriteTemplateMapper.toDto(record)); + } +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/form.template.approval.mapper.ts b/src/database/sql/typeorm/mappers/form.template.approval.mapper.ts new file mode 100644 index 0000000..1a74b40 --- /dev/null +++ b/src/database/sql/typeorm/mappers/form.template.approval.mapper.ts @@ -0,0 +1,27 @@ +import { FormTemplateApprovalResponseDto } from "../../../../domain.types/forms/form.template.approval.domain.types"; + +export class FormTemplateApprovalMapper { + static toDto = (record: any): FormTemplateApprovalResponseDto => { + if (record === null) { + return null; + } + + const dto: FormTemplateApprovalResponseDto = { + id: record.id, + ApproverUserId: record.ApproverUserId, + TemplateId: record.TemplateId, + Approved: record.Approved, + ReviewComments: record.ReviewComments, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toArrayDto(records: any[]): FormTemplateApprovalResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => FormTemplateApprovalMapper.toDto(record)); + } +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/input.unit.list.mapper.ts b/src/database/sql/typeorm/mappers/input.unit.list.mapper.ts new file mode 100644 index 0000000..0eb1c46 --- /dev/null +++ b/src/database/sql/typeorm/mappers/input.unit.list.mapper.ts @@ -0,0 +1,31 @@ +import { InputUnitListResponseDto } from "../../../../domain.types/forms/input.unit.list.domain.types"; + +export class InputUnitListMapper { + static toDto = (record: any): InputUnitListResponseDto => { + if (record === null) { + return null; + } + let units: any[] = []; + try { + units = typeof record.Units === 'string' ? JSON.parse(record.Units) : record.Units; + } catch { + units = []; + } + const dto: InputUnitListResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Units: units, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toArrayDto(records: any[]): InputUnitListResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => InputUnitListMapper.toDto(record)); + } +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/template.folder.mapper.ts b/src/database/sql/typeorm/mappers/template.folder.mapper.ts new file mode 100644 index 0000000..28bd32f --- /dev/null +++ b/src/database/sql/typeorm/mappers/template.folder.mapper.ts @@ -0,0 +1,25 @@ +import { TemplateFolderResponseDto } from "../../../../domain.types/forms/template.folder.domain.types"; + +export class TemplateFolderMapper { + static toDto = (record: any): TemplateFolderResponseDto => { + if (record === null) { + return null; + } + const dto: TemplateFolderResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + ParentFolderId: record.ParentFolderId, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toArrayDto(records: any[]): TemplateFolderResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => TemplateFolderMapper.toDto(record)); + } +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/favorite.template/favorite.template.model.ts b/src/database/sql/typeorm/models/favorite.template/favorite.template.model.ts index 884fe15..75944d1 100644 --- a/src/database/sql/typeorm/models/favorite.template/favorite.template.model.ts +++ b/src/database/sql/typeorm/models/favorite.template/favorite.template.model.ts @@ -9,5 +9,5 @@ export class FavoriteTemplate extends BaseEntity { @Column({ type: 'uuid', nullable: false }) TemplateId: string - + } diff --git a/src/database/sql/typeorm/models/form.section/form.section.model.ts b/src/database/sql/typeorm/models/form.section/form.section.model.ts index 7842ebd..cc8f4b2 100644 --- a/src/database/sql/typeorm/models/form.section/form.section.model.ts +++ b/src/database/sql/typeorm/models/form.section/form.section.model.ts @@ -3,36 +3,36 @@ import { BaseEntity } from '../base.entity'; import { FormTemplate } from '../form.template/form.template.model'; import { Question } from '../question/question.model'; -enum SectionNodeType { - RootSection='RootSection', - ChildSection='ChildSection' -} +// enum SectionNodeType { +// RootSection = 'RootSection', +// ChildSection = 'ChildSection' +// } @Entity('form_sections') export class FormSection extends BaseEntity { - @Column({type: 'uuid', nullable: false }) + @Column({ type: 'uuid', nullable: false }) FormTemplateId: string; - @Column({type: 'uuid', nullable: true }) + @Column({ type: 'uuid', nullable: true }) ParentSectionId?: string; - @Column({type: 'varchar', length: 128, nullable: false }) + @Column({ type: 'varchar', length: 128, nullable: false }) Title: string; - @Column({type: 'varchar', length: 128, nullable: false }) + @Column({ type: 'varchar', length: 128, nullable: false }) DisplayCode?: string; - @Column({type: 'varchar',length: 512, nullable: true }) + @Column({ type: 'varchar', length: 512, nullable: true }) Description?: string; //Represent sequence within parent section - @Column({type: 'int', default: 0, nullable: false }) + @Column({ type: 'int', default: 0, nullable: false }) Sequence?: number; // @Column({ type : 'enum', enum: SectionNodeType, nullable: false }) // SectionNodeType: SectionNodeType; - @Column({type: 'uuid', nullable: true }) + @Column({ type: 'uuid', nullable: true }) ImageResourceId?: string; // @Column({type: 'boolean', default: true, nullable: false }) diff --git a/src/database/sql/typeorm/models/input.unit.list/input.unit.list.model.ts b/src/database/sql/typeorm/models/input.unit.list/input.unit.list.model.ts index b57b926..45184e5 100644 --- a/src/database/sql/typeorm/models/input.unit.list/input.unit.list.model.ts +++ b/src/database/sql/typeorm/models/input.unit.list/input.unit.list.model.ts @@ -9,9 +9,8 @@ export class InputUnitList extends BaseEntity { @Column({ type: 'varchar', length: 512, nullable: false }) Description: string - + // Units represents JSON array @Column({ type: 'json', nullable: false }) Units: string -} - \ No newline at end of file +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/logic/base.logic.model.ts b/src/database/sql/typeorm/models/logic/base.logic.model.ts new file mode 100644 index 0000000..84534ed --- /dev/null +++ b/src/database/sql/typeorm/models/logic/base.logic.model.ts @@ -0,0 +1,36 @@ +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; +import { LogicType } from '../../../../engine/enums'; + +// Base Logic Interface +export interface BaseLogic { + id: string; + Type: LogicType; + FieldId: string; + Enabled: boolean; +} + +// Base Logic Entity (Abstract - no table) +export abstract class BaseLogicEntity { + @PrimaryGeneratedColumn('uuid') + id!: string; + + @Column({ + type: 'varchar', + length: 255, + nullable: false + }) + FieldId!: string; + + @Column({ + type: 'boolean', + nullable: false, + default: true + }) + Enabled!: boolean; + + @CreateDateColumn() + CreatedAt!: Date; + + @UpdateDateColumn() + UpdatedAt!: Date; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/question.response/question.response.model.ts b/src/database/sql/typeorm/models/question.response/question.response.model.ts index bfbab64..fc61606 100644 --- a/src/database/sql/typeorm/models/question.response/question.response.model.ts +++ b/src/database/sql/typeorm/models/question.response/question.response.model.ts @@ -7,47 +7,47 @@ import { Question } from '../question/question.model'; @Entity('question_responses') export class QuestionResponse extends BaseEntity { - @Column({type: 'uuid', nullable: false }) + @Column({ type: 'uuid', nullable: false }) FormSubmissionId: string; - @Column({type: 'uuid', nullable: false }) + @Column({ type: 'uuid', nullable: false }) QuestionId: string; @Column({ type: 'enum', enum: QueryResponseType, nullable: false, default: QueryResponseType.SingleChoiceSelection }) ResponseType: QueryResponseType; - @Column({type: 'int', nullable: true }) + @Column({ type: 'int', nullable: true }) Sequence: number; - - @Column({type: 'int', nullable: true }) + + @Column({ type: 'int', nullable: true }) IntegerValue?: number; @Column({ nullable: true, type: 'float' }) FloatValue?: number; - @Column({type: 'varchar', nullable: true }) + @Column({ type: 'varchar', nullable: true }) BooleanValue?: string; - @Column({type: 'timestamp', nullable: true }) + @Column({ type: 'timestamp', nullable: true }) DateTimeValue?: Date; - @Column({type: 'varchar', nullable: true }) + @Column({ type: 'varchar', nullable: true }) Url?: string; - @Column({type: 'uuid', nullable: true }) + @Column({ type: 'uuid', nullable: true }) FileResourceId?: string; - @Column({type: 'varchar', length: 2048, nullable: true }) + @Column({ type: 'varchar', length: 2048, nullable: true }) Text?: string; //Represent the response of the user - @Column({type: 'text', nullable: true }) + @Column({ type: 'text', nullable: true }) UserResponse: string; // @Column() // SubmissionTimestamp: Date; - @Column({nullable: true}) + @Column({ nullable: true }) SubmissionTimestamp: Date; @Column() diff --git a/src/database/sql/typeorm/models/question/question.model.ts b/src/database/sql/typeorm/models/question/question.model.ts index a45ecf5..d0a94e9 100644 --- a/src/database/sql/typeorm/models/question/question.model.ts +++ b/src/database/sql/typeorm/models/question/question.model.ts @@ -27,55 +27,55 @@ import { QueryResponseType } from '../../../../../domain.types/forms/query.respo @Entity({ name: 'questions' }) export class Question extends BaseEntity { - @Column({type: 'uuid', nullable: true }) + @Column({ type: 'uuid', nullable: true }) TemplateId: string; - @Column({type: 'uuid', nullable: false }) + @Column({ type: 'uuid', nullable: false }) ParentSectionId: string; - @Column({type: 'varchar',length: 128, nullable: true }) + @Column({ type: 'varchar', length: 128, nullable: true }) Title: string; - @Column({type: 'varchar',length: 512, nullable: true }) + @Column({ type: 'varchar', length: 512, nullable: true }) Description: string; - @Column({type: 'varchar',length: 128, nullable: false }) + @Column({ type: 'varchar', length: 128, nullable: false }) DisplayCode: string; @Column({ type: 'enum', enum: QueryResponseType }) ResponseType: QueryResponseType; - @Column({type: 'int', nullable: true }) + @Column({ type: 'int', nullable: true }) Score: number; - @Column({type: 'int', nullable: true }) + @Column({ type: 'int', nullable: true }) Sequence: number; - @Column({type: 'varchar', nullable: true }) + @Column({ type: 'varchar', nullable: true }) ExpectedAnswer: string; - @Column({type: 'boolean', nullable: true }) + @Column({ type: 'boolean', nullable: true }) IsRequired: boolean; - @Column({type: 'varchar', length: 512, nullable: true }) + @Column({ type: 'varchar', length: 512, nullable: true }) Hint: string; @Column({ type: 'json', nullable: true }) Options: string; - @Column({type: 'uuid', nullable: true }) + @Column({ type: 'uuid', nullable: true }) ImageResourceId?: string; - @Column({type: 'int', nullable: true }) + @Column({ type: 'int', nullable: true }) RangeMin: number; - @Column({type: 'int', nullable: true }) + @Column({ type: 'int', nullable: true }) RangeMax: number; - @Column({type: 'varchar', nullable: true }) + @Column({ type: 'varchar', nullable: true }) DefaultExpectedUnit: string; - @Column({type: 'boolean', nullable: false, default: false }) + @Column({ type: 'boolean', nullable: false, default: false }) PageBreakAfter: boolean; @OneToMany(() => QuestionResponse, (response) => response.Question) diff --git a/src/database/sql/typeorm/repositories/favorite.template/favorite.template.repo.ts b/src/database/sql/typeorm/repositories/favorite.template/favorite.template.repo.ts new file mode 100644 index 0000000..ef13f7b --- /dev/null +++ b/src/database/sql/typeorm/repositories/favorite.template/favorite.template.repo.ts @@ -0,0 +1,141 @@ +import { FavoriteTemplateCreateModel, FavoriteTemplateResponseDto, FavoriteTemplateSearchFilters, FavoriteTemplateUpdateModel } from "../../../../../domain.types/forms/favorite.template.domain.types"; +import { IFavoriteTemplateRepo } from "../../../../repository.interfaces/favorite.template/favorite.template.repo.interface"; +import { FavoriteTemplate } from "../../models/favorite.template/favorite.template.model"; +import { Source } from "../../database.connector.typeorm"; +import { FavoriteTemplateMapper } from "../../mappers/favorite.template.mapper"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class FavoriteTemplateRepo extends BaseRepo implements IFavoriteTemplateRepo { + + _favoriteTemplateRepo: Repository = Source.getRepository(FavoriteTemplate); + + create = async (model: FavoriteTemplateCreateModel): Promise => { + try { + const data = this._favoriteTemplateRepo.create({ + UserId: model.UserId, + TemplateId: model.TemplateId, + }); + + const record = await this._favoriteTemplateRepo.save(data); + return FavoriteTemplateMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update = async (id: string, model: FavoriteTemplateUpdateModel): Promise => { + try { + const record = await this._favoriteTemplateRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + ErrorHandler.throwNotFoundError('Favorite template not found!'); + } + + if (model.UserId !== undefined) { + record.UserId = model.UserId; + } + if (model.TemplateId !== undefined) { + record.TemplateId = model.TemplateId; + } + + const updatedRecord = await this._favoriteTemplateRepo.save(record); + return FavoriteTemplateMapper.toDto(updatedRecord); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById = async (id: string): Promise => { + try { + const record = await this._favoriteTemplateRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return FavoriteTemplateMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete = async (id: string): Promise => { + try { + const record = await this._favoriteTemplateRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._favoriteTemplateRepo.save(record); + + return true; // Soft delete successful + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search = async (filters: FavoriteTemplateSearchFilters): Promise => { + try { + const search = this.getSearchModel(filters); + const { search: searchWithPagination, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._favoriteTemplateRepo.findAndCount(searchWithPagination); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => FavoriteTemplateMapper.toDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: FavoriteTemplateSearchFilters) => { + const search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where["id"] = filters.id; + } + + if (filters.UserId) { + search.where["UserId"] = filters.UserId; + } + + if (filters.TemplateId) { + search.where["TemplateId"] = filters.TemplateId; + } + + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/form.template.approval/form.template.approval.repo.ts b/src/database/sql/typeorm/repositories/form.template.approval/form.template.approval.repo.ts new file mode 100644 index 0000000..9a385c5 --- /dev/null +++ b/src/database/sql/typeorm/repositories/form.template.approval/form.template.approval.repo.ts @@ -0,0 +1,168 @@ +import { FormTemplateApprovalCreateModel, FormTemplateApprovalResponseDto, FormTemplateApprovalSearchFilters, FormTemplateApprovalUpdateModel } from "../../../../../domain.types/forms/form.template.approval.domain.types"; +import { IFormTemplateApprovalRepo } from "../../../../repository.interfaces/form.template.approval/form.template.approval.repo.interface"; +import { FormTemplateApproval } from "../../models/form.template.approval/form.template.approval.model"; +import { Source } from "../../database.connector.typeorm"; +import { FormTemplateApprovalMapper } from "../../mappers/form.template.approval.mapper"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class FormTemplateApprovalRepo extends BaseRepo implements IFormTemplateApprovalRepo { + + _formTemplateApprovalRepo: Repository = Source.getRepository(FormTemplateApproval); + + create = async (model: FormTemplateApprovalCreateModel): Promise => { + try { + const data = this._formTemplateApprovalRepo.create({ + ApproverUserId: model.ApproverUserId, + TemplateId: model.TemplateId, + Approved: model.Approved, + ReviewComments: model.ReviewComments, + }); + + const record = await this._formTemplateApprovalRepo.save(data); + return FormTemplateApprovalMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update = async (id: string, model: FormTemplateApprovalUpdateModel): Promise => { + try { + const record = await this._formTemplateApprovalRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + ErrorHandler.throwNotFoundError('Form template approval not found!'); + } + + if (model.ApproverUserId !== undefined) { + record.ApproverUserId = model.ApproverUserId; + } + if (model.TemplateId !== undefined) { + record.TemplateId = model.TemplateId; + } + if (model.Approved !== undefined) { + record.Approved = model.Approved; + } + if (model.ReviewComments !== undefined) { + record.ReviewComments = model.ReviewComments; + } + + const updatedRecord = await this._formTemplateApprovalRepo.save(record); + return FormTemplateApprovalMapper.toDto(updatedRecord); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById = async (id: string): Promise => { + try { + const record = await this._formTemplateApprovalRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return FormTemplateApprovalMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getByTemplateId = async (templateId: string): Promise => { + try { + const record = await this._formTemplateApprovalRepo.findOne({ + where: { + TemplateId: templateId, + DeletedAt: null, + }, + }); + return FormTemplateApprovalMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete = async (id: string): Promise => { + try { + const record = await this._formTemplateApprovalRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._formTemplateApprovalRepo.save(record); + + return true; // Soft delete successful + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search = async (filters: FormTemplateApprovalSearchFilters): Promise => { + try { + const search = this.getSearchModel(filters); + const { search: searchWithPagination, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._formTemplateApprovalRepo.findAndCount(searchWithPagination); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => FormTemplateApprovalMapper.toDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: FormTemplateApprovalSearchFilters) => { + const search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where["id"] = filters.id; + } + + if (filters.ApproverUserId) { + search.where["ApproverUserId"] = filters.ApproverUserId; + } + + if (filters.TemplateId) { + search.where["TemplateId"] = filters.TemplateId; + } + + if (filters.Approved !== undefined) { + search.where["Approved"] = filters.Approved; + } + + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/input.unit.list/input.unit.list.repo.ts b/src/database/sql/typeorm/repositories/input.unit.list/input.unit.list.repo.ts new file mode 100644 index 0000000..24d9810 --- /dev/null +++ b/src/database/sql/typeorm/repositories/input.unit.list/input.unit.list.repo.ts @@ -0,0 +1,130 @@ +import { InputUnitListCreateModel, InputUnitListResponseDto, InputUnitListSearchFilters, InputUnitListUpdateModel } from "../../../../../domain.types/forms/input.unit.list.domain.types"; +import { IInputUnitListRepo } from "../../../../repository.interfaces/input.unit.list/input.unit.list.repo.interface"; +import { InputUnitList } from "../../models/input.unit.list/input.unit.list.model"; +import { Source } from "../../database.connector.typeorm"; +import { InputUnitListMapper } from "../../mappers/input.unit.list.mapper"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class InputUnitListRepo extends BaseRepo implements IInputUnitListRepo { + _inputUnitListRepo: Repository = Source.getRepository(InputUnitList); + + create = async (model: InputUnitListCreateModel): Promise => { + try { + const data = this._inputUnitListRepo.create({ + Name: model.Name, + Description: model.Description, + Units: JSON.stringify(model.Units), + }); + const record = await this._inputUnitListRepo.save(data); + return InputUnitListMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update = async (id: string, model: InputUnitListUpdateModel): Promise => { + try { + const record = await this._inputUnitListRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!record) { + ErrorHandler.throwNotFoundError('Input unit list not found!'); + } + if (model.Name !== undefined) { + record.Name = model.Name; + } + if (model.Description !== undefined) { + record.Description = model.Description; + } + if (model.Units !== undefined) { + record.Units = JSON.stringify(model.Units); + } + const updatedRecord = await this._inputUnitListRepo.save(record); + return InputUnitListMapper.toDto(updatedRecord); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById = async (id: string): Promise => { + try { + const record = await this._inputUnitListRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return InputUnitListMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete = async (id: string): Promise => { + try { + const record = await this._inputUnitListRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!record) { + return false; + } + record.DeletedAt = new Date(); + await this._inputUnitListRepo.save(record); + return true; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search = async (filters: InputUnitListSearchFilters): Promise => { + try { + const search = this.getSearchModel(filters); + const { search: searchWithPagination, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._inputUnitListRepo.findAndCount(searchWithPagination); + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => InputUnitListMapper.toDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: InputUnitListSearchFilters) => { + const search: FindManyOptions = { + relations: {}, + where: {}, + }; + if (filters.id) { + search.where["id"] = filters.id; + } + if (filters.Name) { + search.where["Name"] = filters.Name; + } + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts b/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts index 3e2c320..61dd9e8 100644 --- a/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts +++ b/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts @@ -19,337 +19,335 @@ import path from "path"; import { createObjectCsvWriter } from "csv-writer"; import { QuestionResponseDto } from "../../../../../domain.types/forms/question.domain.types"; -export class ResponseRepo extends BaseRepo implements IResponseRepo{ - - _responseRepo = Source.getRepository(QuestionResponse); - - _questionRepo = Source.getRepository(Question); - - create=async (model: QuestionResponseCreateModel) : Promise => { - try { - const data = await this._responseRepo.create({ - FormSubmissionId: model.FormSubmissionId, - QuestionId: model.QuestionId, - ResponseType: model.ResponseType as QueryResponseType, - IntegerValue: model.IntegerValue, - FloatValue: model.FloatValue, - BooleanValue: model.BooleanValue, - DateTimeValue: model.DateTimeValue, - Url: model.Url, - FileResourceId: model.FileResourceId, - Text: model.TextValue, - SubmissionTimestamp: null, - LastSaveTimestamp: new Date(), - }); - const record = await this._responseRepo.save(data); - return ResponseMapper.toDto(record); - } catch (error) { - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - save=async (model: any) => { - try { - const data = await this._responseRepo.create({ - ResponseType: model.ResponseType as QueryResponseType, - FloatValue: model.FloatValue, - IntegerValue: model.IntegerValue, - BooleanValue: model.BooleanValue, - DateTimeValue: model.DateTimeValue, - Url: model.Url, - Text: model.TextValue, - SubmissionTimestamp: null, - LastSaveTimestamp: new Date(), - }); - const record = await this._responseRepo.save(data); - return ResponseMapper.toDto(record); - } catch (error) { - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - update=async (id: string, model: QuestionResponseUpdateModel) : Promise => { - try { - - const updateData = await this._responseRepo.findOne({ - where : { - id : id, - DeletedAt: null, - }, - }); - - - if (!updateData) { - ErrorHandler.throwNotFoundError('Question Response Data not found!'); - } - // if (model.SectionIdentifier) { - // updateData.SectionIdentifier = model.SectionIdentifier; - // } - if (model.ResponseType) { - updateData.ResponseType = model.ResponseType; - } - if ( model.IntegerValue) { - updateData.IntegerValue = model.IntegerValue; - } - if (model.FloatValue) { - updateData.FloatValue = model.FloatValue; - } - - if (model.BooleanValue) { - updateData.BooleanValue = model.BooleanValue; - } - - // if (model.QueryParams) { - // updateData.QueryParams = model.QueryParams; - // } - - if (model.DateTimeValue) { - updateData.DateTimeValue = model.DateTimeValue; - } - - if (model.Url) { - updateData.Url = model.Url; - } - - if (model.FileResourceId) { - updateData.FileResourceId = model.FileResourceId; - } - - if (model.TextValue) { - updateData.Text = model.TextValue; - } - - updateData.LastSaveTimestamp=new Date(); - - updateData.UpdatedAt=new Date(); - - // if (model.Status) { - // updateData.Status = model.Status; - // } - - // if (model.Category) { - // updateData.Category = model.Category; - // } - - var record = await this._responseRepo.save(updateData); - return ResponseMapper.toDto(record); - } - catch (error) - { - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - getById=async (id: string) : Promise => { - try { - var record = await this._responseRepo.findOne({ - where: { - id: id, - DeletedAt: null, - }, - }); - return ResponseMapper.toDto(record); - } - - catch (error) { - Logger.instance().log(error.message); - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - getQuestionById=async (id: string) : Promise => { - try { - var record = await this._questionRepo.findOne({ - where: { - id: id, - DeletedAt: null, - }, - }); - return QuestionMapper.toDto(record); - } - catch (error) - { - Logger.instance().log(error.message); - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - delete=async (id: string) : Promise => { - try { - var record = await this._responseRepo.findOne({ - where : { - id : id, - DeletedAt: null, - }, - }); - - if (!record) { - return false; // Record not found - } - record.DeletedAt = new Date(); // Soft delete - await this._responseRepo.save(record); - return true; // Soft delete successful - } - catch (error) - { - Logger.instance().log(error.message); - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - getAll=async () : Promise => { - try { - const data = []; - var prompts = await this._responseRepo.find(); - for (var i of prompts) { - const record = ResponseMapper.toDto(i); - // const record = i; - data.push(record); - } - return data; - } catch (error) { - Logger.instance().log(error.message); - ErrorHandler.throwDbAccessError('DB Error: Unable to get Llm prompt record!', error); - } - }; - - exportCsv = async (): Promise => { - const data = await this.getAll(); - const csvPath = path.join(__dirname, '../../../storage/data.csv'); - const csvWriter = createObjectCsvWriter({ - path: csvPath, - header: [ - { id: 'id', title: 'ID' }, - { id: 'FormSubmission.id', title: 'Submission ID' }, - { id: 'FormSubmission.TemplateId', title: 'Template ID' }, - { id: 'FormSubmission.FormUrl', title: 'Form URL' }, - { id: 'FormSubmission.UserId', title: 'User ID' }, - { id: 'FormSubmission.Status', title: 'Status' }, - { id: 'FormSubmission.SubmissionTimestamp', title: 'Submission Timestamp' }, - { id: 'FormSubmission.CreatedAt', title: 'Created At' }, - { id: 'Question.id', title: 'Question ID' }, - { id: 'Question.Title', title: 'Question Title' }, - { id: 'Question.Description', title: 'Question Description' }, - { id: 'Question.DisplayCode', title: 'Display Code' }, - { id: 'Question.ResponseType', title: 'Response Type' }, - { id: 'Question.Score', title: 'Score' }, - { id: 'Question.CorrectAnswer', title: 'Correct Answer' }, - { id: 'Question.Hint', title: 'Hint' }, - { id: 'Question.TemplateId', title: 'Question Template ID' }, - { id: 'Question.SectionId', title: 'Question Section ID' }, - { id: 'Question.CreatedAt', title: 'Question Created At' }, - { id: 'Question.UpdatedAt', title: 'Question Updated At' }, - { id: 'ResponseType', title: 'Query Response Type' }, - { id: 'IntegerValue', title: 'Integer Value' }, - { id: 'FloatValue', title: 'Float Value' }, - { id: 'BooleanValue', title: 'Boolean Value' }, - { id: 'DateTimeValue', title: 'Date Time Value' }, - { id: 'Url', title: 'URL' }, - { id: 'FileResourceId', title: 'File Resource ID' }, - { id: 'TextValue', title: 'Text Value' }, - { id: 'SubmissionTimestamp', title: 'Submission Timestamp' }, - { id: 'LastSaveTimestamp', title: 'Last Save Timestamp' } - ] - }); - await csvWriter.writeRecords(data); - return csvPath; - }; +export class ResponseRepo extends BaseRepo implements IResponseRepo { + + _responseRepo = Source.getRepository(QuestionResponse); + + _questionRepo = Source.getRepository(Question); + + create = async (model: QuestionResponseCreateModel): Promise => { + try { + const data = await this._responseRepo.create({ + FormSubmissionId: model.FormSubmissionId, + QuestionId: model.QuestionId, + ResponseType: model.ResponseType as QueryResponseType, + IntegerValue: model.IntegerValue, + FloatValue: model.FloatValue, + BooleanValue: model.BooleanValue, + DateTimeValue: model.DateTimeValue, + Url: model.Url, + FileResourceId: model.FileResourceId, + Text: model.TextValue, + SubmissionTimestamp: null, + LastSaveTimestamp: new Date(), + UserResponse: model.UserResponse ?? null + }); + const record = await this._responseRepo.save(data); + return ResponseMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + save = async (model: any) => { + try { + const data = await this._responseRepo.create({ + ResponseType: model.ResponseType as QueryResponseType, + FloatValue: model.FloatValue, + IntegerValue: model.IntegerValue, + BooleanValue: model.BooleanValue, + DateTimeValue: model.DateTimeValue, + Url: model.Url, + Text: model.TextValue, + SubmissionTimestamp: null, + LastSaveTimestamp: new Date(), + }); + const record = await this._responseRepo.save(data); + return ResponseMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update = async (id: string, model: QuestionResponseUpdateModel): Promise => { + try { + + const updateData = await this._responseRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + + if (!updateData) { + ErrorHandler.throwNotFoundError('Question Response Data not found!'); + } + // if (model.SectionIdentifier) { + // updateData.SectionIdentifier = model.SectionIdentifier; + // } + if (model.ResponseType) { + updateData.ResponseType = model.ResponseType; + } + if (model.IntegerValue) { + updateData.IntegerValue = model.IntegerValue; + } + if (model.FloatValue) { + updateData.FloatValue = model.FloatValue; + } + + if (model.BooleanValue) { + updateData.BooleanValue = model.BooleanValue; + } + + // if (model.QueryParams) { + // updateData.QueryParams = model.QueryParams; + // } + + if (model.DateTimeValue) { + updateData.DateTimeValue = model.DateTimeValue; + } + + if (model.Url) { + updateData.Url = model.Url; + } + + if (model.FileResourceId) { + updateData.FileResourceId = model.FileResourceId; + } + + if (model.TextValue) { + updateData.Text = model.TextValue; + } + + updateData.LastSaveTimestamp = new Date(); + + updateData.UpdatedAt = new Date(); + + // if (model.Status) { + // updateData.Status = model.Status; + // } + + // if (model.Category) { + // updateData.Category = model.Category; + // } + + var record = await this._responseRepo.save(updateData); + return ResponseMapper.toDto(record); + } + catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById = async (id: string): Promise => { + try { + var record = await this._responseRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return ResponseMapper.toDto(record); + } + + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; - exportPdf = async (): Promise => { - const data = await this.getAll(); - const pdfPath = path.join(__dirname, '../../../storage/data.pdf'); - const doc = new PDFDocument(); - doc.pipe(fs.createWriteStream(pdfPath)); - - doc.text('ID\tSubmission ID\tTemplate ID\tForm URL\tUser ID\tStatus\tSubmission Timestamp\tCreated At\t' + - 'Question ID\tQuestion Title\tQuestion Description\tDisplay Code\tResponse Type\tScore\t' + - 'Correct Answer\tHint\tQuestion Template ID\tQuestion Section ID\tQuestion Created At\t' + - 'Question Updated At\tQuery Response Type\tInteger Value\tFloat Value\tBoolean Value\t' + - 'Date Time Value\tURL\tFile Resource ID\tText Value\tSubmission Timestamp\tLast Save Timestamp'); - - data.forEach(row => { - doc.text(`${row.id}\t${row.FormSubmission.id}\t${row.FormSubmission.TemplateId}\t${row.FormSubmission.FormUrl}\t${row.FormSubmission.UserId}\t${row.FormSubmission.Status}\t${row.FormSubmission.SubmissionTimestamp}\t${row.FormSubmission.CreatedAt}\t` + - `${row.Question.id}\t${row.Question.Title}\t${row.Question.Description}\t${row.Question.DisplayCode}\t${row.Question.ResponseType}\t${row.Question.Score}\t${row.Question.CorrectAnswer}\t${row.Question.Hint}\t` + - `${row.Question.TemplateId}\t${row.Question.SectionId}\t${row.Question.CreatedAt}\t${row.Question.UpdatedAt}\t${row.ResponseType}\t${row.IntegerValue}\t${row.FloatValue}\t${row.BooleanValue}\t` + - `${row.DateTimeValue}\t${row.Url}\t${row.FileResourceId}\t${row.TextValue}\t${row.SubmissionTimestamp}\t${row.LastSaveTimestamp}`); - }); - - doc.end(); - return pdfPath; + getQuestionById = async (id: string): Promise => { + try { + var record = await this._questionRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return QuestionMapper.toDto(record); + } + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete = async (id: string): Promise => { + try { + var record = await this._responseRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._responseRepo.save(record); + return true; // Soft delete successful + } + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getAll = async (): Promise => { + try { + const data = []; + var prompts = await this._responseRepo.find(); + for (var i of prompts) { + const record = ResponseMapper.toDto(i); + // const record = i; + data.push(record); + } + return data; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to get Llm prompt record!', error); + } + }; + + exportCsv = async (): Promise => { + const data = await this.getAll(); + const csvPath = path.join(__dirname, '../../../storage/data.csv'); + const csvWriter = createObjectCsvWriter({ + path: csvPath, + header: [ + { id: 'id', title: 'ID' }, + { id: 'FormSubmission.id', title: 'Submission ID' }, + { id: 'FormSubmission.TemplateId', title: 'Template ID' }, + { id: 'FormSubmission.FormUrl', title: 'Form URL' }, + { id: 'FormSubmission.UserId', title: 'User ID' }, + { id: 'FormSubmission.Status', title: 'Status' }, + { id: 'FormSubmission.SubmissionTimestamp', title: 'Submission Timestamp' }, + { id: 'FormSubmission.CreatedAt', title: 'Created At' }, + { id: 'Question.id', title: 'Question ID' }, + { id: 'Question.Title', title: 'Question Title' }, + { id: 'Question.Description', title: 'Question Description' }, + { id: 'Question.DisplayCode', title: 'Display Code' }, + { id: 'Question.ResponseType', title: 'Response Type' }, + { id: 'Question.Score', title: 'Score' }, + { id: 'Question.CorrectAnswer', title: 'Correct Answer' }, + { id: 'Question.Hint', title: 'Hint' }, + { id: 'Question.TemplateId', title: 'Question Template ID' }, + { id: 'Question.SectionId', title: 'Question Section ID' }, + { id: 'Question.CreatedAt', title: 'Question Created At' }, + { id: 'Question.UpdatedAt', title: 'Question Updated At' }, + { id: 'ResponseType', title: 'Query Response Type' }, + { id: 'IntegerValue', title: 'Integer Value' }, + { id: 'FloatValue', title: 'Float Value' }, + { id: 'BooleanValue', title: 'Boolean Value' }, + { id: 'DateTimeValue', title: 'Date Time Value' }, + { id: 'Url', title: 'URL' }, + { id: 'FileResourceId', title: 'File Resource ID' }, + { id: 'TextValue', title: 'Text Value' }, + { id: 'SubmissionTimestamp', title: 'Submission Timestamp' }, + { id: 'LastSaveTimestamp', title: 'Last Save Timestamp' } + ] + }); + await csvWriter.writeRecords(data); + return csvPath; + }; + + exportPdf = async (): Promise => { + const data = await this.getAll(); + const pdfPath = path.join(__dirname, '../../../storage/data.pdf'); + const doc = new PDFDocument(); + doc.pipe(fs.createWriteStream(pdfPath)); + + doc.text('ID\tSubmission ID\tTemplate ID\tForm URL\tUser ID\tStatus\tSubmission Timestamp\tCreated At\t' + + 'Question ID\tQuestion Title\tQuestion Description\tDisplay Code\tResponse Type\tScore\t' + + 'Correct Answer\tHint\tQuestion Template ID\tQuestion Section ID\tQuestion Created At\t' + + 'Question Updated At\tQuery Response Type\tInteger Value\tFloat Value\tBoolean Value\t' + + 'Date Time Value\tURL\tFile Resource ID\tText Value\tSubmission Timestamp\tLast Save Timestamp'); + + data.forEach(row => { + doc.text(`${row.id}\t${row.FormSubmission.id}\t${row.FormSubmission.TemplateId}\t${row.FormSubmission.FormUrl}\t${row.FormSubmission.UserId}\t${row.FormSubmission.Status}\t${row.FormSubmission.SubmissionTimestamp}\t${row.FormSubmission.CreatedAt}\t` + + `${row.Question.id}\t${row.Question.Title}\t${row.Question.Description}\t${row.Question.DisplayCode}\t${row.Question.ResponseType}\t${row.Question.Score}\t${row.Question.CorrectAnswer}\t${row.Question.Hint}\t` + + `${row.Question.TemplateId}\t${row.Question.SectionId}\t${row.Question.CreatedAt}\t${row.Question.UpdatedAt}\t${row.ResponseType}\t${row.IntegerValue}\t${row.FloatValue}\t${row.BooleanValue}\t` + + `${row.DateTimeValue}\t${row.Url}\t${row.FileResourceId}\t${row.TextValue}\t${row.SubmissionTimestamp}\t${row.LastSaveTimestamp}`); + }); + + doc.end(); + return pdfPath; + }; + + + search = async (filters: QuestionResponseSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._responseRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === 'DESC' ? 'descending' : 'ascending', + OrderedBy: orderByColumn, + Items: list.map(x => ResponseMapper.toDto(x)), + }; + + return searchResults; + } + + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + } + }; + + private getSearchModel = (filters: QuestionResponseSearchFilters) => { + + var search: FindManyOptions = { + relations: {}, + where: {}, }; - - search=async (filters: QuestionResponseSearchFilters) : Promise => { - try { - var search = this.getSearchModel(filters); - var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); - const [list, count] = await this._responseRepo.findAndCount(search); - - const searchResults = { - TotalCount : count, - RetrievedCount : list.length, - PageIndex : pageIndex, - ItemsPerPage : limit, - Order : order === 'DESC' ? 'descending' : 'ascending', - OrderedBy : orderByColumn, - Items : list.map(x => ResponseMapper.toDto(x)), - }; - - return searchResults; - } - - catch (error) { - Logger.instance().log(error.message); - ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); - } - }; - - private getSearchModel = (filters: QuestionResponseSearchFilters) => { - - var search: FindManyOptions = { - relations: {}, - where: {}, - }; - - if (filters.FormSubmissionId) { - search.where["FormSubmissionId"] = - filters.FormSubmissionId; - } - - if (filters.QuestionId) { - search.where["QuestionId"] = filters.QuestionId; - } - - if (filters.ResponseType) { - search.where["ResponseType"] = - filters.ResponseType; - } - - if (filters.IntegerValue) { - search.where["IntegerValue"] = - filters.IntegerValue; - } - - if (filters.FloatValue) { - search.where["FloatValue"] = filters.FloatValue; - } - - if (filters.BooleanValue) { - search.where["BooleanValue"] = - filters.BooleanValue; - } - if (filters.Url) { - search.where["Url"] = filters.Url; - } - if (filters.FileResourceId) { - search.where["FileResourceId"] = - filters.FileResourceId; - } - if (filters.TextValue) { - search.where["TextValue"] = filters.TextValue; - } - - - return search; - }; + if (filters.FormSubmissionId) { + search.where["FormSubmissionId"] = + filters.FormSubmissionId; + } + + if (filters.QuestionId) { + search.where["QuestionId"] = filters.QuestionId; + } + + if (filters.ResponseType) { + search.where["ResponseType"] = + filters.ResponseType; + } + + if (filters.IntegerValue) { + search.where["IntegerValue"] = + filters.IntegerValue; + } + + if (filters.FloatValue) { + search.where["FloatValue"] = filters.FloatValue; + } + + if (filters.BooleanValue) { + search.where["BooleanValue"] = + filters.BooleanValue; + } + if (filters.Url) { + search.where["Url"] = filters.Url; + } + if (filters.FileResourceId) { + search.where["FileResourceId"] = + filters.FileResourceId; + } + if (filters.TextValue) { + search.where["TextValue"] = filters.TextValue; + } + + + return search; + }; } \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts b/src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts new file mode 100644 index 0000000..37595bc --- /dev/null +++ b/src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts @@ -0,0 +1,133 @@ +import { TemplateFolderCreateModel, TemplateFolderResponseDto, TemplateFolderSearchFilters, TemplateFolderUpdateModel } from "../../../../../domain.types/forms/template.folder.domain.types"; +import { ITemplateFolderRepo } from "../../../../repository.interfaces/template.folder/template.folder.repo.interface"; +import { TemplateFolder } from "../../models/template.folder/template.folder.model"; +import { Source } from "../../database.connector.typeorm"; +import { TemplateFolderMapper } from "../../mappers/template.folder.mapper"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class TemplateFolderRepo extends BaseRepo implements ITemplateFolderRepo { + _templateFolderRepo: Repository = Source.getRepository(TemplateFolder); + + create = async (model: TemplateFolderCreateModel): Promise => { + try { + const data = this._templateFolderRepo.create({ + Name: model.Name, + Description: model.Description, + ParentFolderId: model.ParentFolderId, + }); + const record = await this._templateFolderRepo.save(data); + return TemplateFolderMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update = async (id: string, model: TemplateFolderUpdateModel): Promise => { + try { + const record = await this._templateFolderRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!record) { + ErrorHandler.throwNotFoundError('Template folder not found!'); + } + if (model.Name !== undefined) { + record.Name = model.Name; + } + if (model.Description !== undefined) { + record.Description = model.Description; + } + if (model.ParentFolderId !== undefined) { + record.ParentFolderId = model.ParentFolderId; + } + const updatedRecord = await this._templateFolderRepo.save(record); + return TemplateFolderMapper.toDto(updatedRecord); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById = async (id: string): Promise => { + try { + const record = await this._templateFolderRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return TemplateFolderMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete = async (id: string): Promise => { + try { + const record = await this._templateFolderRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!record) { + return false; + } + record.DeletedAt = new Date(); + await this._templateFolderRepo.save(record); + return true; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search = async (filters: TemplateFolderSearchFilters): Promise => { + try { + const search = this.getSearchModel(filters); + const { search: searchWithPagination, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._templateFolderRepo.findAndCount(searchWithPagination); + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => TemplateFolderMapper.toDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: TemplateFolderSearchFilters) => { + const search: FindManyOptions = { + relations: {}, + where: {}, + }; + if (filters.id) { + search.where["id"] = filters.id; + } + if (filters.Name) { + search.where["Name"] = filters.Name; + } + if (filters.ParentFolderId) { + search.where["ParentFolderId"] = filters.ParentFolderId; + } + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/typeorm.injector.ts b/src/database/sql/typeorm/typeorm.injector.ts index 4f8fc53..d6f0b70 100644 --- a/src/database/sql/typeorm/typeorm.injector.ts +++ b/src/database/sql/typeorm/typeorm.injector.ts @@ -7,6 +7,10 @@ import { FormTemplateRepo } from './repositories/form.template/form.template.rep import { QuestionRepo } from './repositories/question/question.repo'; import { ResponseRepo } from './repositories/question.response/question.response.repo'; import { UserRepo } from './repositories/user/user.repo'; +import { FavoriteTemplateRepo } from './repositories/favorite.template/favorite.template.repo'; +import { FormTemplateApprovalRepo } from './repositories/form.template.approval/form.template.approval.repo'; +import { InputUnitListRepo } from './repositories/input.unit.list/input.unit.list.repo'; +import { TemplateFolderRepo } from './repositories/template.folder/template.folder.repo'; @@ -24,7 +28,11 @@ export class TypeOrmInjector { container.register('IQuestionRepo', QuestionRepo); container.register('IResponseRepo', ResponseRepo); container.register('IUserRepo', UserRepo); - - + container.register('IFavoriteTemplateRepo', FavoriteTemplateRepo); + container.register('IFormTemplateApprovalRepo', FormTemplateApprovalRepo); + container.register('IInputUnitListRepo', InputUnitListRepo); + container.register('ITemplateFolderRepo', TemplateFolderRepo); + + } } diff --git a/src/domain.types/forms/favorite.template.domain.types.ts b/src/domain.types/forms/favorite.template.domain.types.ts new file mode 100644 index 0000000..63127a1 --- /dev/null +++ b/src/domain.types/forms/favorite.template.domain.types.ts @@ -0,0 +1,30 @@ +import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; +import { uuid } from "../miscellaneous/system.types"; + +export interface FavoriteTemplateCreateModel { + UserId: string; + TemplateId: string; +} + +export interface FavoriteTemplateUpdateModel { + UserId?: string; + TemplateId?: string; +} + +export interface FavoriteTemplateResponseDto { + id: string; + UserId: string; + TemplateId: string; + CreatedAt: Date; + UpdatedAt: Date; +} + +export interface FavoriteTemplateSearchFilters extends BaseSearchFilters { + id?: string; + UserId?: uuid; + TemplateId?: uuid; +} + +export interface FavoriteTemplateSearchResults extends BaseSearchResults { + Items: FavoriteTemplateResponseDto[]; +} \ No newline at end of file diff --git a/src/domain.types/forms/form.template.approval.domain.types.ts b/src/domain.types/forms/form.template.approval.domain.types.ts new file mode 100644 index 0000000..89fa719 --- /dev/null +++ b/src/domain.types/forms/form.template.approval.domain.types.ts @@ -0,0 +1,37 @@ +import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; +import { uuid } from "../miscellaneous/system.types"; + +export interface FormTemplateApprovalCreateModel { + ApproverUserId: string; + TemplateId: string; + Approved: boolean; + ReviewComments?: string; +} + +export interface FormTemplateApprovalUpdateModel { + ApproverUserId?: string; + TemplateId?: string; + Approved?: boolean; + ReviewComments?: string; +} + +export interface FormTemplateApprovalResponseDto { + id: string; + ApproverUserId: string; + TemplateId: string; + Approved: boolean; + ReviewComments?: string; + CreatedAt: Date; + UpdatedAt: Date; +} + +export interface FormTemplateApprovalSearchFilters extends BaseSearchFilters { + id?: string; + ApproverUserId?: uuid; + TemplateId?: uuid; + Approved?: boolean; +} + +export interface FormTemplateApprovalSearchResults extends BaseSearchResults { + Items: FormTemplateApprovalResponseDto[]; +} \ No newline at end of file diff --git a/src/domain.types/forms/input.unit.list.domain.types.ts b/src/domain.types/forms/input.unit.list.domain.types.ts new file mode 100644 index 0000000..b0cee8f --- /dev/null +++ b/src/domain.types/forms/input.unit.list.domain.types.ts @@ -0,0 +1,32 @@ +import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; +import { uuid } from "../miscellaneous/system.types"; + +export interface InputUnitListCreateModel { + Name: string; + Description: string; + Units: any[]; // Array of units (JSON array) +} + +export interface InputUnitListUpdateModel { + Name?: string; + Description?: string; + Units?: any[]; +} + +export interface InputUnitListResponseDto { + id: string; + Name: string; + Description: string; + Units: any[]; + CreatedAt: Date; + UpdatedAt: Date; +} + +export interface InputUnitListSearchFilters extends BaseSearchFilters { + id?: string; + Name?: string; +} + +export interface InputUnitListSearchResults extends BaseSearchResults { + Items: InputUnitListResponseDto[]; +} \ No newline at end of file diff --git a/src/domain.types/forms/response.domain.types.ts b/src/domain.types/forms/response.domain.types.ts index b881446..82abd36 100644 --- a/src/domain.types/forms/response.domain.types.ts +++ b/src/domain.types/forms/response.domain.types.ts @@ -16,6 +16,7 @@ export interface QuestionResponseCreateModel { Url : string; FileResourceId : string; TextValue : string; + UserResponse : string; } export interface QuestionResponseUpdateModel { @@ -31,6 +32,7 @@ export interface QuestionResponseUpdateModel { Url ?: string; FileResourceId ?: string; TextValue ?: string; + UserResponse ?: string; } export interface QuestionResponseSaveModel { diff --git a/src/domain.types/forms/template.folder.domain.types.ts b/src/domain.types/forms/template.folder.domain.types.ts new file mode 100644 index 0000000..77336c3 --- /dev/null +++ b/src/domain.types/forms/template.folder.domain.types.ts @@ -0,0 +1,33 @@ +import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; +import { uuid } from "../miscellaneous/system.types"; + +export interface TemplateFolderCreateModel { + Name: string; + Description?: string; + ParentFolderId?: string; +} + +export interface TemplateFolderUpdateModel { + Name?: string; + Description?: string; + ParentFolderId?: string; +} + +export interface TemplateFolderResponseDto { + id: string; + Name: string; + Description?: string; + ParentFolderId?: string; + CreatedAt: Date; + UpdatedAt: Date; +} + +export interface TemplateFolderSearchFilters extends BaseSearchFilters { + id?: string; + Name?: string; + ParentFolderId?: uuid; +} + +export interface TemplateFolderSearchResults extends BaseSearchResults { + Items: TemplateFolderResponseDto[]; +} \ No newline at end of file diff --git a/src/services/favorite.template/favorite.template.service.ts b/src/services/favorite.template/favorite.template.service.ts new file mode 100644 index 0000000..60a7b4c --- /dev/null +++ b/src/services/favorite.template/favorite.template.service.ts @@ -0,0 +1,39 @@ +import { + FavoriteTemplateCreateModel, + FavoriteTemplateResponseDto, + FavoriteTemplateSearchFilters, + FavoriteTemplateUpdateModel +} from "../../domain.types/forms/favorite.template.domain.types"; +import { IFavoriteTemplateRepo } from "../../database/repository.interfaces/favorite.template/favorite.template.repo.interface"; +import { inject, injectable } from "tsyringe"; + +@injectable() +export class FavoriteTemplateService { + constructor(@inject('IFavoriteTemplateRepo') private _favoriteTemplateRepo: IFavoriteTemplateRepo) { + } + + create = async (model: FavoriteTemplateCreateModel): Promise => { + const dto = await this._favoriteTemplateRepo.create(model); + return dto; + }; + + update = async (id: string, model: FavoriteTemplateUpdateModel): Promise => { + const dto = await this._favoriteTemplateRepo.update(id, model); + return dto; + }; + + getById = async (id: string): Promise => { + const dto = await this._favoriteTemplateRepo.getById(id); + return dto; + }; + + delete = async (id: string): Promise => { + const dto = await this._favoriteTemplateRepo.delete(id); + return dto; + }; + + search = async (filters: FavoriteTemplateSearchFilters): Promise => { + const dto = await this._favoriteTemplateRepo.search(filters); + return dto; + }; +} \ No newline at end of file diff --git a/src/services/form.template.approval/form.template.approval.service.ts b/src/services/form.template.approval/form.template.approval.service.ts new file mode 100644 index 0000000..fada72a --- /dev/null +++ b/src/services/form.template.approval/form.template.approval.service.ts @@ -0,0 +1,37 @@ +import { + FormTemplateApprovalCreateModel, + FormTemplateApprovalResponseDto, + FormTemplateApprovalSearchFilters, + FormTemplateApprovalUpdateModel +} from "../../domain.types/forms/form.template.approval.domain.types"; +import { IFormTemplateApprovalRepo } from "../../database/repository.interfaces/form.template.approval/form.template.approval.repo.interface"; +import { inject, injectable } from "tsyringe"; + +@injectable() +export class FormTemplateApprovalService { + constructor(@inject('IFormTemplateApprovalRepo') private _formTemplateApprovalRepo: IFormTemplateApprovalRepo) { } + + create = async (model: FormTemplateApprovalCreateModel): Promise => { + return await this._formTemplateApprovalRepo.create(model); + }; + + update = async (id: string, model: FormTemplateApprovalUpdateModel): Promise => { + return await this._formTemplateApprovalRepo.update(id, model); + }; + + getById = async (id: string): Promise => { + return await this._formTemplateApprovalRepo.getById(id); + }; + + getByTemplateId = async (templateId: string): Promise => { + return await this._formTemplateApprovalRepo.getByTemplateId(templateId); + }; + + delete = async (id: string): Promise => { + return await this._formTemplateApprovalRepo.delete(id); + }; + + search = async (filters: FormTemplateApprovalSearchFilters): Promise => { + return await this._formTemplateApprovalRepo.search(filters); + }; +} \ No newline at end of file diff --git a/src/services/input.unit.list/input.unit.list.service.ts b/src/services/input.unit.list/input.unit.list.service.ts new file mode 100644 index 0000000..945d1e3 --- /dev/null +++ b/src/services/input.unit.list/input.unit.list.service.ts @@ -0,0 +1,33 @@ +import { + InputUnitListCreateModel, + InputUnitListResponseDto, + InputUnitListSearchFilters, + InputUnitListUpdateModel +} from "../../domain.types/forms/input.unit.list.domain.types"; +import { IInputUnitListRepo } from "../../database/repository.interfaces/input.unit.list/input.unit.list.repo.interface"; +import { inject, injectable } from "tsyringe"; + +@injectable() +export class InputUnitListService { + constructor(@inject('IInputUnitListRepo') private _inputUnitListRepo: IInputUnitListRepo) { } + + create = async (model: InputUnitListCreateModel): Promise => { + return await this._inputUnitListRepo.create(model); + }; + + update = async (id: string, model: InputUnitListUpdateModel): Promise => { + return await this._inputUnitListRepo.update(id, model); + }; + + getById = async (id: string): Promise => { + return await this._inputUnitListRepo.getById(id); + }; + + delete = async (id: string): Promise => { + return await this._inputUnitListRepo.delete(id); + }; + + search = async (filters: InputUnitListSearchFilters): Promise => { + return await this._inputUnitListRepo.search(filters); + }; +} \ No newline at end of file diff --git a/src/services/question.response/question.response.service.ts b/src/services/question.response/question.response.service.ts index da370a7..7d550e9 100644 --- a/src/services/question.response/question.response.service.ts +++ b/src/services/question.response/question.response.service.ts @@ -5,108 +5,108 @@ import { QuestionResponseDto } from "../../domain.types/forms/question.domain.ty @injectable() export class ResponseService { - // prisma: PrismaClient = null; - // private exportDirectory = path.join(__dirname, '../exports'); - - constructor(@inject('IResponseRepo') private _respRepo : IResponseRepo) { - // this.prisma = PrismaClientInit.instance().getPrismaInstance(); - // if (!fs.existsSync(this.exportDirectory)) { - // fs.mkdirSync(this.exportDirectory); - // } - } - - // allResponses = async (): Promise => { - // const response = await this.prisma.questionResponse.findMany({ - // include: { - // FormSubmission: true, - // Question: true - // }, - // where: { - // DeletedAt: null - // } - // }); - // return ResponseMapper.toArrayDto(response); - // }; - - // create = async (model: QuestionResponseCreateModel) => { - // const response = await this.prisma.questionResponse.create({ - // data: { - // Question: { - // connect: { id: model.QuestionId } - // }, - // FormSubmission: { - // connect: { id: model.FormSubmissionId } - // }, - // ResponseType: model.ResponseType as QueryResponseType, - // IntegerValue: model.IntegerValue, - // FloatValue: model.FloatValue, - // BooleanValue: model.BooleanValue, - // DateTimeValue: model.DateTimeValue, - // Url: model.Url, - // FileResourceId: model.FileResourceId, - // TextValue: model.TextValue, - // SubmissionTimestamp: null, - // LastSaveTimestamp: new Date(), - // // DeletedAt : null, - // }, - // include: { - // FormSubmission: true, - // Question: true - // } - // }); - // return ResponseMapper.toDto(response); - // }; - - create = async (model: QuestionResponseCreateModel) : Promise => { - const dto=await this._respRepo.create(model); - return dto; - }; - - save = async (model: any) => { - const dto=await this._respRepo.save(model); - return dto; - }; - - update = async (id: string, model: QuestionResponseUpdateModel) : Promise => { - const dto=await this._respRepo.update(id,model); - return dto; - }; - - getById = async (id: string) : Promise => { - const dto=await this._respRepo.getById(id); - return dto; - }; - - getQuestionById = async (id: string) : Promise => { - const dto=await this._respRepo.getQuestionById(id); - return dto; - }; - - delete = async (id: string) : Promise => { - const dto=await this._respRepo.delete(id); - return dto; - }; - - getAll = async () => { - const dto=await this._respRepo.getAll(); - return dto; - }; - - exportCsv = async (): Promise => { - - const dto=await this._respRepo.exportCsv(); - return dto; - }; - - exportPdf = async (): Promise => { - const dto=await this._respRepo.exportPdf(); - return dto; - }; - - - public search = async (filters: QuestionResponseSearchFilters) => { - const dto=await this._respRepo.search(filters); - return dto; - }; + // prisma: PrismaClient = null; + // private exportDirectory = path.join(__dirname, '../exports'); + + constructor(@inject('IResponseRepo') private _respRepo: IResponseRepo) { + // this.prisma = PrismaClientInit.instance().getPrismaInstance(); + // if (!fs.existsSync(this.exportDirectory)) { + // fs.mkdirSync(this.exportDirectory); + // } + } + + // allResponses = async (): Promise => { + // const response = await this.prisma.questionResponse.findMany({ + // include: { + // FormSubmission: true, + // Question: true + // }, + // where: { + // DeletedAt: null + // } + // }); + // return ResponseMapper.toArrayDto(response); + // }; + + // create = async (model: QuestionResponseCreateModel) => { + // const response = await this.prisma.questionResponse.create({ + // data: { + // Question: { + // connect: { id: model.QuestionId } + // }, + // FormSubmission: { + // connect: { id: model.FormSubmissionId } + // }, + // ResponseType: model.ResponseType as QueryResponseType, + // IntegerValue: model.IntegerValue, + // FloatValue: model.FloatValue, + // BooleanValue: model.BooleanValue, + // DateTimeValue: model.DateTimeValue, + // Url: model.Url, + // FileResourceId: model.FileResourceId, + // TextValue: model.TextValue, + // SubmissionTimestamp: null, + // LastSaveTimestamp: new Date(), + // // DeletedAt : null, + // }, + // include: { + // FormSubmission: true, + // Question: true + // } + // }); + // return ResponseMapper.toDto(response); + // }; + + create = async (model: QuestionResponseCreateModel): Promise => { + const dto = await this._respRepo.create(model); + return dto; + }; + + save = async (model: any) => { + const dto = await this._respRepo.save(model); + return dto; + }; + + update = async (id: string, model: QuestionResponseUpdateModel): Promise => { + const dto = await this._respRepo.update(id, model); + return dto; + }; + + getById = async (id: string): Promise => { + const dto = await this._respRepo.getById(id); + return dto; + }; + + getQuestionById = async (id: string): Promise => { + const dto = await this._respRepo.getQuestionById(id); + return dto; + }; + + delete = async (id: string): Promise => { + const dto = await this._respRepo.delete(id); + return dto; + }; + + getAll = async () => { + const dto = await this._respRepo.getAll(); + return dto; + }; + + exportCsv = async (): Promise => { + + const dto = await this._respRepo.exportCsv(); + return dto; + }; + + exportPdf = async (): Promise => { + const dto = await this._respRepo.exportPdf(); + return dto; + }; + + + public search = async (filters: QuestionResponseSearchFilters) => { + const dto = await this._respRepo.search(filters); + return dto; + }; } diff --git a/src/services/question/question.service.ts b/src/services/question/question.service.ts index ba304cd..37be54b 100644 --- a/src/services/question/question.service.ts +++ b/src/services/question/question.service.ts @@ -7,180 +7,180 @@ import { IQuestionRepo } from "../../database/repository.interfaces/question/que @injectable() export class QuestionService { - constructor(@inject('IQuestionRepo') private _questRepo : IQuestionRepo) { - } - - // allQuestions = async (): Promise => { - // const response = await this.prisma.question.findMany({ - // include: { - // ParentFormTemplate: true, - // ParentFormSection: true - // }, - // where: { - // DeletedAt: null - // } - // }); - // return QuestionMapper.toArrayDto(response); - // }; - - // create = async (model: QuestionCreateModel) => { - - // const jsonData: Prisma.JsonValue = { - // Sequence: model.Options.Sequence, - // Option: model.Options.Data, - // ImageUrl: model.Options.ImageUrl, - // } as Prisma.JsonObject; - - // const response = await this.prisma.question.create({ - // data: { - // ParentFormTemplate: { - // connect: { id: model.ParentTemplateId } - // }, - // ParentFormSection: { - // connect: { id: model.ParentSectionId } - // }, - // Title: model.Title, - // Description: model.Description, - // DisplayCode: model.DisplayCode, - // ResponseType: model.ResponseType as QueryResponseType, - // Score: model.Score, - // CorrectAnswer: model.CorrectAnswer, - // Hint: model.Hint, - // Sequence: model.Sequence, - // Options: jsonData, - // QuestionImageUrl: model.QuestionImageUrl, - // RangeMax: model.RangeMax, - // RangeMin: model.RangeMin, - // CreatedAt: new Date(), - // // UpdatedAt: new Date(), - // // DeletedAt: null, - // }, - // include: { - // ParentFormTemplate: true, - // ParentFormSection: true - // } - // }); - // return QuestionMapper.toDto(response); - - // }; - - create = async (model: QuestionCreateModel) : Promise => { - const dto=await this._questRepo.create(model); - return dto; - }; - - update = async (id: string, model: QuestionUpdateModel) : Promise => { - const dto=await this._questRepo.update(id,model); - return dto; - }; - - getById = async (id: string) : Promise => { - const dto=await this._questRepo.getById(id); - return dto; - }; - - getByTemplateId = async (id: string) : Promise => { - const dto=await this._questRepo.getByTemplateId(id); - return dto; - }; - - delete = async (id: string) : Promise => { - const dto=await this._questRepo.delete(id); - return true; - }; - - public search = async (filters: QuestionSearchFilters) : Promise=> { - const dto=await this._questRepo.search(filters); - return dto; - }; - - // private getSearchModel = (filters: QuestionSearchFilters): Prisma.QuestionWhereInput => { - // const where: Prisma.QuestionWhereInput = { DeletedAt: null }; - - // if (filters.id) { - // where.id = { - // equals: filters.id, - // }; - // } - // if (filters.parentTemplateId) { - // where.ParentTemplateId = { - // equals: filters.parentTemplateId, - // }; - // } - // if (filters.parentSectionId) { - // where.ParentSectionId = { - // equals: filters.parentSectionId, - // }; - // } - - // if (filters.title) { - // where.Title = { - // equals: filters.title, - // }; - // } - - // if (filters.description) { - // where.Description = { - // equals: filters.description, - // }; - // } - - // if (filters.displayCode) { - // where.DisplayCode = { - // equals: filters.displayCode, - // }; - // } - - // if (filters.responseType) { - // where.ResponseType = { - // equals: filters.responseType, - // }; - // } - - // if (filters.score) { - // where.Score = { - // equals: filters.score, - // }; - // } - - // if (filters.isRequired) { - // where.IsRequired = { - // equals: filters.isRequired, - // }; - // } - - // if (filters.hint) { - // where.Hint = { - // equals: filters.hint, - // }; - // } - // // if (filters.options) { - // // where.Options = { - // // equals: filters.options, - // // }; - // // } - // if (filters.questionImageUrl) { - // where.QuestionImageUrl = { - // equals: filters.questionImageUrl, - // }; - // } - // if (filters.rangeMin) { - // where.RangeMin = { - // equals: filters.rangeMin, - // }; - // } - // if (filters.rangeMax) { - // where.RangeMax = { - // equals: filters.rangeMax, - // }; - // } - // if (filters.correctAnswer) { - // where.CorrectAnswer = { - // equals: filters.correctAnswer, - // }; - // } - - // return where; - // }; + constructor(@inject('IQuestionRepo') private _questRepo: IQuestionRepo) { + } + + // allQuestions = async (): Promise => { + // const response = await this.prisma.question.findMany({ + // include: { + // ParentFormTemplate: true, + // ParentFormSection: true + // }, + // where: { + // DeletedAt: null + // } + // }); + // return QuestionMapper.toArrayDto(response); + // }; + + // create = async (model: QuestionCreateModel) => { + + // const jsonData: Prisma.JsonValue = { + // Sequence: model.Options.Sequence, + // Option: model.Options.Data, + // ImageUrl: model.Options.ImageUrl, + // } as Prisma.JsonObject; + + // const response = await this.prisma.question.create({ + // data: { + // ParentFormTemplate: { + // connect: { id: model.ParentTemplateId } + // }, + // ParentFormSection: { + // connect: { id: model.ParentSectionId } + // }, + // Title: model.Title, + // Description: model.Description, + // DisplayCode: model.DisplayCode, + // ResponseType: model.ResponseType as QueryResponseType, + // Score: model.Score, + // CorrectAnswer: model.CorrectAnswer, + // Hint: model.Hint, + // Sequence: model.Sequence, + // Options: jsonData, + // QuestionImageUrl: model.QuestionImageUrl, + // RangeMax: model.RangeMax, + // RangeMin: model.RangeMin, + // CreatedAt: new Date(), + // // UpdatedAt: new Date(), + // // DeletedAt: null, + // }, + // include: { + // ParentFormTemplate: true, + // ParentFormSection: true + // } + // }); + // return QuestionMapper.toDto(response); + + // }; + + create = async (model: QuestionCreateModel): Promise => { + const dto = await this._questRepo.create(model); + return dto; + }; + + update = async (id: string, model: QuestionUpdateModel): Promise => { + const dto = await this._questRepo.update(id, model); + return dto; + }; + + getById = async (id: string): Promise => { + const dto = await this._questRepo.getById(id); + return dto; + }; + + getByTemplateId = async (id: string): Promise => { + const dto = await this._questRepo.getByTemplateId(id); + return dto; + }; + + delete = async (id: string): Promise => { + const dto = await this._questRepo.delete(id); + return true; + }; + + public search = async (filters: QuestionSearchFilters): Promise => { + const dto = await this._questRepo.search(filters); + return dto; + }; + + // private getSearchModel = (filters: QuestionSearchFilters): Prisma.QuestionWhereInput => { + // const where: Prisma.QuestionWhereInput = { DeletedAt: null }; + + // if (filters.id) { + // where.id = { + // equals: filters.id, + // }; + // } + // if (filters.parentTemplateId) { + // where.ParentTemplateId = { + // equals: filters.parentTemplateId, + // }; + // } + // if (filters.parentSectionId) { + // where.ParentSectionId = { + // equals: filters.parentSectionId, + // }; + // } + + // if (filters.title) { + // where.Title = { + // equals: filters.title, + // }; + // } + + // if (filters.description) { + // where.Description = { + // equals: filters.description, + // }; + // } + + // if (filters.displayCode) { + // where.DisplayCode = { + // equals: filters.displayCode, + // }; + // } + + // if (filters.responseType) { + // where.ResponseType = { + // equals: filters.responseType, + // }; + // } + + // if (filters.score) { + // where.Score = { + // equals: filters.score, + // }; + // } + + // if (filters.isRequired) { + // where.IsRequired = { + // equals: filters.isRequired, + // }; + // } + + // if (filters.hint) { + // where.Hint = { + // equals: filters.hint, + // }; + // } + // // if (filters.options) { + // // where.Options = { + // // equals: filters.options, + // // }; + // // } + // if (filters.questionImageUrl) { + // where.QuestionImageUrl = { + // equals: filters.questionImageUrl, + // }; + // } + // if (filters.rangeMin) { + // where.RangeMin = { + // equals: filters.rangeMin, + // }; + // } + // if (filters.rangeMax) { + // where.RangeMax = { + // equals: filters.rangeMax, + // }; + // } + // if (filters.correctAnswer) { + // where.CorrectAnswer = { + // equals: filters.correctAnswer, + // }; + // } + + // return where; + // }; } diff --git a/src/services/template.folder/template.folder.service.ts b/src/services/template.folder/template.folder.service.ts new file mode 100644 index 0000000..4891ab0 --- /dev/null +++ b/src/services/template.folder/template.folder.service.ts @@ -0,0 +1,33 @@ +import { + TemplateFolderCreateModel, + TemplateFolderResponseDto, + TemplateFolderSearchFilters, + TemplateFolderUpdateModel +} from "../../domain.types/forms/template.folder.domain.types"; +import { ITemplateFolderRepo } from "../../database/repository.interfaces/template.folder/template.folder.repo.interface"; +import { inject, injectable } from "tsyringe"; + +@injectable() +export class TemplateFolderService { + constructor(@inject('ITemplateFolderRepo') private _templateFolderRepo: ITemplateFolderRepo) { } + + create = async (model: TemplateFolderCreateModel): Promise => { + return await this._templateFolderRepo.create(model); + }; + + update = async (id: string, model: TemplateFolderUpdateModel): Promise => { + return await this._templateFolderRepo.update(id, model); + }; + + getById = async (id: string): Promise => { + return await this._templateFolderRepo.getById(id); + }; + + delete = async (id: string): Promise => { + return await this._templateFolderRepo.delete(id); + }; + + search = async (filters: TemplateFolderSearchFilters): Promise => { + return await this._templateFolderRepo.search(filters); + }; +} \ No newline at end of file diff --git a/src/startup/router.ts b/src/startup/router.ts index 28a56c0..ee2745e 100644 --- a/src/startup/router.ts +++ b/src/startup/router.ts @@ -5,6 +5,9 @@ import { register as user } from '../api/user/user.router'; import { register as formSection } from '../api/form.section/form.section.router' import { register as question } from '../api/question/question.router'; import { register as Response } from '../api/question.response/question.response.router'; +import { register as favoriteTemplate } from '../api/favorite.template/favorite.template.router'; +import { register as formTemplateApproval } from '../api/form.template.approval/form.template.approval.router'; +import { register as templateFolder } from '../api/template.folder/template.folder.router'; /////////////////////////////////////////////////////////////////////////////////////// @@ -27,6 +30,9 @@ export class Router { formSection(this._app); question(this._app); Response(this._app); + favoriteTemplate(this._app); + formTemplateApproval(this._app); + templateFolder(this._app); resolve(true); } catch (error) { console.log("Error initilizing the routes") From 5aab6c7396e557d2e49a41ff4527f326d6b308b7 Mon Sep 17 00:00:00 2001 From: inflection-prashant Date: Thu, 10 Jul 2025 11:19:17 +0530 Subject: [PATCH 05/29] models and dtos of rule, operatiosn and rules --- .../sql/typeorm/mappers/form.field.mapper.ts | 88 +++++++ .../sql/typeorm/mappers/logic.mapper.ts | 152 +++++++++++ .../sql/typeorm/mappers/operation.mapper.ts | 165 ++++++++++++ .../sql/typeorm/mappers/rule.mapper.ts | 187 +++++++++++++ .../typeorm/models/form.field/field.types.ts | 55 ++++ .../models/form.field/form.field.model.ts | 246 ++++++++++++++++++ .../typeorm/models/logic/base.logic.model.ts | 40 +-- .../models/logic/calculation.logic.model.ts | 18 ++ .../models/logic/legacy.logic.model.ts | 38 +++ .../sql/typeorm/models/logic/logic.types.ts | 6 + .../typeorm/models/logic/skip.logic.model.ts | 19 ++ .../models/logic/validation.logic.model.ts | 16 ++ .../models/operation/base.operation.model.ts | 12 + .../operation/composition.operation.model.ts | 16 ++ .../function.expression.operation.model.ts | 17 ++ .../operation/iterate.operation.model.ts | 24 ++ .../operation/logical.operation.model.ts | 17 ++ .../operation/mathematical.operation.model.ts | 18 ++ .../models/operation/operation.types.ts | 47 ++++ .../typeorm/models/rule/base.rule.model.ts | 13 + .../models/rule/calculation.rule.model.ts | 29 +++ .../typeorm/models/rule/legacy.rule.model.ts | 30 +++ .../typeorm/models/rule/skip.rule.model.ts | 26 ++ .../models/rule/validation.rule.model.ts | 29 +++ .../forms/form.field.domain.types.ts | 198 ++++++++++++++ src/domain.types/forms/logic.domain.types.ts | 141 ++++++++++ .../forms/operation.domain.types.ts | 155 +++++++++++ src/domain.types/forms/rule.domain.types.ts | 175 +++++++++++++ 28 files changed, 1945 insertions(+), 32 deletions(-) create mode 100644 src/database/sql/typeorm/mappers/form.field.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/logic.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/operation.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/rule.mapper.ts create mode 100644 src/database/sql/typeorm/models/form.field/field.types.ts create mode 100644 src/database/sql/typeorm/models/form.field/form.field.model.ts create mode 100644 src/database/sql/typeorm/models/logic/calculation.logic.model.ts create mode 100644 src/database/sql/typeorm/models/logic/legacy.logic.model.ts create mode 100644 src/database/sql/typeorm/models/logic/logic.types.ts create mode 100644 src/database/sql/typeorm/models/logic/skip.logic.model.ts create mode 100644 src/database/sql/typeorm/models/logic/validation.logic.model.ts create mode 100644 src/database/sql/typeorm/models/operation/base.operation.model.ts create mode 100644 src/database/sql/typeorm/models/operation/composition.operation.model.ts create mode 100644 src/database/sql/typeorm/models/operation/function.expression.operation.model.ts create mode 100644 src/database/sql/typeorm/models/operation/iterate.operation.model.ts create mode 100644 src/database/sql/typeorm/models/operation/logical.operation.model.ts create mode 100644 src/database/sql/typeorm/models/operation/mathematical.operation.model.ts create mode 100644 src/database/sql/typeorm/models/operation/operation.types.ts create mode 100644 src/database/sql/typeorm/models/rule/base.rule.model.ts create mode 100644 src/database/sql/typeorm/models/rule/calculation.rule.model.ts create mode 100644 src/database/sql/typeorm/models/rule/legacy.rule.model.ts create mode 100644 src/database/sql/typeorm/models/rule/skip.rule.model.ts create mode 100644 src/database/sql/typeorm/models/rule/validation.rule.model.ts create mode 100644 src/domain.types/forms/form.field.domain.types.ts create mode 100644 src/domain.types/forms/logic.domain.types.ts create mode 100644 src/domain.types/forms/operation.domain.types.ts create mode 100644 src/domain.types/forms/rule.domain.types.ts diff --git a/src/database/sql/typeorm/mappers/form.field.mapper.ts b/src/database/sql/typeorm/mappers/form.field.mapper.ts new file mode 100644 index 0000000..6170cb8 --- /dev/null +++ b/src/database/sql/typeorm/mappers/form.field.mapper.ts @@ -0,0 +1,88 @@ +import { + FormFieldResponseDto +} from "../../../../domain.types/forms/form.field.domain.types"; + +export class FormFieldMapper { + static toDto = (record: any): FormFieldResponseDto => { + if (record === null) { + return null; + } + + const dto: FormFieldResponseDto = { + id: record.id, + Name: record.Name, + Label: record.Label, + Title: record.Title, + Description: record.Description, + DisplayCode: record.DisplayCode, + ResponseType: record.ResponseType, + QueryResponseType: record.QueryResponseType, + Required: record.Required, + Value: record.Value, + Score: record.Score, + Sequence: record.Sequence, + ExpectedAnswer: record.ExpectedAnswer, + Hint: record.Hint, + Options: record.Options, + ImageResourceId: record.ImageResourceId, + RangeMin: record.RangeMin, + RangeMax: record.RangeMax, + DefaultExpectedUnit: record.DefaultExpectedUnit, + PageBreakAfter: record.PageBreakAfter, + SkipLogicId: record.SkipLogicId, + CalculateLogicId: record.CalculateLogicId, + ValidateLogicId: record.ValidateLogicId, + TemplateId: record.TemplateId, + ParentSectionId: record.ParentSectionId, + FormId: record.FormId, + SkipLogic: record.SkipLogic ? { + id: record.SkipLogic.id, + Type: record.SkipLogic.Type, + DefaultSkip: record.SkipLogic.DefaultSkip + } : undefined, + CalculateLogic: record.CalculateLogic ? { + id: record.CalculateLogic.id, + Type: record.CalculateLogic.Type, + DefaultSkip: record.CalculateLogic.DefaultSkip + } : undefined, + ValidateLogic: record.ValidateLogic ? { + id: record.ValidateLogic.id, + Type: record.ValidateLogic.Type, + DefaultSkip: record.ValidateLogic.DefaultSkip + } : undefined, + FormTemplate: record.FormTemplate ? { + id: record.FormTemplate.id, + Title: record.FormTemplate.Title, + Description: record.FormTemplate.Description, + DisplayCode: record.FormTemplate.DisplayCode + } : undefined, + ParentFormSection: record.ParentFormSection ? { + id: record.ParentFormSection.id, + SectionIdentifier: record.ParentFormSection.SectionIdentifier, + Title: record.ParentFormSection.Title, + Description: record.ParentFormSection.Description, + DisplayCode: record.ParentFormSection.DisplayCode + } : undefined, + ExpectedInputUnitList: record.ExpectedInputUnitList ? { + id: record.ExpectedInputUnitList.id, + Name: record.ExpectedInputUnitList.Name, + Description: record.ExpectedInputUnitList.Description + } : undefined, + Responses: record.Responses ? record.Responses.map((response: any) => ({ + id: response.id, + ResponseValue: response.ResponseValue, + CreatedAt: response.CreatedAt + })) : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toArrayDto(records: any[]): FormFieldResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => FormFieldMapper.toDto(record)); + } +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/logic.mapper.ts b/src/database/sql/typeorm/mappers/logic.mapper.ts new file mode 100644 index 0000000..f6e2910 --- /dev/null +++ b/src/database/sql/typeorm/mappers/logic.mapper.ts @@ -0,0 +1,152 @@ +import { + BaseLogicResponseDto, + SkipLogicResponseDto, + CalculationLogicResponseDto, + ValidationLogicResponseDto, + LegacyLogicResponseDto +} from "../../../../domain.types/forms/logic.domain.types"; + +export class LogicMapper { + static toBaseDto = (record: any): BaseLogicResponseDto => { + if (record === null) { + return null; + } + + const dto: BaseLogicResponseDto = { + id: record.id, + Type: record.Type, + DefaultSkip: record.DefaultSkip, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toSkipLogicDto = (record: any): SkipLogicResponseDto => { + if (record === null) { + return null; + } + + const dto: SkipLogicResponseDto = { + id: record.id, + Type: record.Type, + DefaultSkip: record.DefaultSkip, + Rules: record.Rules ? record.Rules.map((rule: any) => ({ + id: rule.id, + OperationId: rule.OperationId, + SkipWhenTrue: rule.SkipWhenTrue, + LogicId: rule.LogicId, + CreatedAt: rule.CreatedAt, + UpdatedAt: rule.UpdatedAt + })) : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toCalculationLogicDto = (record: any): CalculationLogicResponseDto => { + if (record === null) { + return null; + } + + const dto: CalculationLogicResponseDto = { + id: record.id, + Type: record.Type, + DefaultSkip: record.DefaultSkip, + FallbackValue: record.FallbackValue, + Rules: record.Rules ? record.Rules.map((rule: any) => ({ + id: rule.id, + ConditionForOperationId: rule.ConditionForOperationId, + OperationId: rule.OperationId, + LogicId: rule.LogicId, + CreatedAt: rule.CreatedAt, + UpdatedAt: rule.UpdatedAt + })) : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toValidationLogicDto = (record: any): ValidationLogicResponseDto => { + if (record === null) { + return null; + } + + const dto: ValidationLogicResponseDto = { + id: record.id, + Type: record.Type, + DefaultSkip: record.DefaultSkip, + Rules: record.Rules ? record.Rules.map((rule: any) => ({ + id: rule.id, + OperationId: rule.OperationId, + ErrorWhenFalse: rule.ErrorWhenFalse, + ErrorMessage: rule.ErrorMessage, + LogicId: rule.LogicId, + CreatedAt: rule.CreatedAt, + UpdatedAt: rule.UpdatedAt + })) : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toLegacyLogicDto = (record: any): LegacyLogicResponseDto => { + if (record === null) { + return null; + } + + const dto: LegacyLogicResponseDto = { + id: record.id, + Type: record.Type, + DefaultSkip: record.DefaultSkip, + Rules: record.Rules ? record.Rules.map((rule: any) => ({ + id: rule.id, + OperationId: rule.OperationId, + LogicId: rule.LogicId, + CreatedAt: rule.CreatedAt, + UpdatedAt: rule.UpdatedAt + })) : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toArrayBaseDto(records: any[]): BaseLogicResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => LogicMapper.toBaseDto(record)); + } + + static toArraySkipLogicDto(records: any[]): SkipLogicResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => LogicMapper.toSkipLogicDto(record)); + } + + static toArrayCalculationLogicDto(records: any[]): CalculationLogicResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => LogicMapper.toCalculationLogicDto(record)); + } + + static toArrayValidationLogicDto(records: any[]): ValidationLogicResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => LogicMapper.toValidationLogicDto(record)); + } + + static toArrayLegacyLogicDto(records: any[]): LegacyLogicResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => LogicMapper.toLegacyLogicDto(record)); + } +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/operation.mapper.ts b/src/database/sql/typeorm/mappers/operation.mapper.ts new file mode 100644 index 0000000..1669da6 --- /dev/null +++ b/src/database/sql/typeorm/mappers/operation.mapper.ts @@ -0,0 +1,165 @@ +import { + BaseOperationResponseDto, + LogicalOperationResponseDto, + MathematicalOperationResponseDto, + CompositionOperationResponseDto, + IterateOperationResponseDto, + FunctionExpressionOperationResponseDto +} from "../../../../domain.types/forms/operation.domain.types"; + +export class OperationMapper { + static toBaseDto = (record: any): BaseOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: BaseOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toLogicalOperationDto = (record: any): LogicalOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: LogicalOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + OperatorType: record.OperatorType, + LeftOperand: record.LeftOperand, + RightOperand: record.RightOperand, + Parameters: record.Parameters, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toMathematicalOperationDto = (record: any): MathematicalOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: MathematicalOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + OperatorType: record.OperatorType, + LeftOperand: record.LeftOperand, + RightOperand: record.RightOperand, + Parameters: record.Parameters, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toCompositionOperationDto = (record: any): CompositionOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: CompositionOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + OperatorType: record.OperatorType, + LeftOperand: record.LeftOperand, + RightOperand: record.RightOperand, + Parameters: record.Parameters, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toIterateOperationDto = (record: any): IterateOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: IterateOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + ArrayOperand: record.ArrayOperand, + ItemAlias: record.ItemAlias, + OperationId: record.OperationId, + Operation: record.Operation ? { + id: record.Operation.id, + Name: record.Operation.Name, + Description: record.Operation.Description + } : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toFunctionExpressionOperationDto = (record: any): FunctionExpressionOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: FunctionExpressionOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + FunctionName: record.FunctionName, + Parameters: record.Parameters, + Expression: record.Expression, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toArrayBaseDto(records: any[]): BaseOperationResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => OperationMapper.toBaseDto(record)); + } + + static toArrayLogicalOperationDto(records: any[]): LogicalOperationResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => OperationMapper.toLogicalOperationDto(record)); + } + + static toArrayMathematicalOperationDto(records: any[]): MathematicalOperationResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => OperationMapper.toMathematicalOperationDto(record)); + } + + static toArrayCompositionOperationDto(records: any[]): CompositionOperationResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => OperationMapper.toCompositionOperationDto(record)); + } + + static toArrayIterateOperationDto(records: any[]): IterateOperationResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => OperationMapper.toIterateOperationDto(record)); + } + + static toArrayFunctionExpressionOperationDto(records: any[]): FunctionExpressionOperationResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => OperationMapper.toFunctionExpressionOperationDto(record)); + } +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/rule.mapper.ts b/src/database/sql/typeorm/mappers/rule.mapper.ts new file mode 100644 index 0000000..c3e8586 --- /dev/null +++ b/src/database/sql/typeorm/mappers/rule.mapper.ts @@ -0,0 +1,187 @@ +import { + BaseRuleResponseDto, + SkipRuleResponseDto, + CalculationRuleResponseDto, + ValidationRuleResponseDto, + LegacyRuleResponseDto +} from "../../../../domain.types/forms/rule.domain.types"; + +export class RuleMapper { + static toBaseDto = (record: any): BaseRuleResponseDto => { + if (record === null) { + return null; + } + + const dto: BaseRuleResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Priority: record.Priority, + IsActive: record.IsActive, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toSkipRuleDto = (record: any): SkipRuleResponseDto => { + if (record === null) { + return null; + } + + const dto: SkipRuleResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Priority: record.Priority, + IsActive: record.IsActive, + OperationId: record.OperationId, + SkipWhenTrue: record.SkipWhenTrue, + LogicId: record.LogicId, + Operation: record.Operation ? { + id: record.Operation.id, + Name: record.Operation.Name, + Description: record.Operation.Description + } : undefined, + Logic: record.Logic ? { + id: record.Logic.id, + Type: record.Logic.Type, + DefaultSkip: record.Logic.DefaultSkip + } : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toCalculationRuleDto = (record: any): CalculationRuleResponseDto => { + if (record === null) { + return null; + } + + const dto: CalculationRuleResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Priority: record.Priority, + IsActive: record.IsActive, + ConditionForOperationId: record.ConditionForOperationId, + OperationId: record.OperationId, + LogicId: record.LogicId, + ConditionForOperation: record.ConditionForOperation ? { + id: record.ConditionForOperation.id, + Name: record.ConditionForOperation.Name, + Description: record.ConditionForOperation.Description + } : undefined, + Operation: record.Operation ? { + id: record.Operation.id, + Name: record.Operation.Name, + Description: record.Operation.Description + } : undefined, + Logic: record.Logic ? { + id: record.Logic.id, + Type: record.Logic.Type, + DefaultSkip: record.Logic.DefaultSkip, + FallbackValue: record.Logic.FallbackValue + } : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toValidationRuleDto = (record: any): ValidationRuleResponseDto => { + if (record === null) { + return null; + } + + const dto: ValidationRuleResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Priority: record.Priority, + IsActive: record.IsActive, + OperationId: record.OperationId, + ErrorWhenFalse: record.ErrorWhenFalse, + ErrorMessage: record.ErrorMessage, + LogicId: record.LogicId, + Operation: record.Operation ? { + id: record.Operation.id, + Name: record.Operation.Name, + Description: record.Operation.Description + } : undefined, + Logic: record.Logic ? { + id: record.Logic.id, + Type: record.Logic.Type, + DefaultSkip: record.Logic.DefaultSkip + } : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toLegacyRuleDto = (record: any): LegacyRuleResponseDto => { + if (record === null) { + return null; + } + + const dto: LegacyRuleResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Priority: record.Priority, + IsActive: record.IsActive, + OperationId: record.OperationId, + LogicId: record.LogicId, + Operation: record.Operation ? { + id: record.Operation.id, + Name: record.Operation.Name, + Description: record.Operation.Description + } : undefined, + Logic: record.Logic ? { + id: record.Logic.id, + Type: record.Logic.Type, + DefaultSkip: record.Logic.DefaultSkip + } : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toArrayBaseDto(records: any[]): BaseRuleResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => RuleMapper.toBaseDto(record)); + } + + static toArraySkipRuleDto(records: any[]): SkipRuleResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => RuleMapper.toSkipRuleDto(record)); + } + + static toArrayCalculationRuleDto(records: any[]): CalculationRuleResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => RuleMapper.toCalculationRuleDto(record)); + } + + static toArrayValidationRuleDto(records: any[]): ValidationRuleResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => RuleMapper.toValidationRuleDto(record)); + } + + static toArrayLegacyRuleDto(records: any[]): LegacyRuleResponseDto[] { + if (records === null) { + return []; + } + return records.map(record => RuleMapper.toLegacyRuleDto(record)); + } +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/form.field/field.types.ts b/src/database/sql/typeorm/models/form.field/field.types.ts new file mode 100644 index 0000000..4ba1c73 --- /dev/null +++ b/src/database/sql/typeorm/models/form.field/field.types.ts @@ -0,0 +1,55 @@ +// Field Response Types +export enum FieldResponseType { + Text = 'Text', + Number = 'Number', + Boolean = 'Boolean', + Date = 'Date', + DateTime = 'DateTime', + Time = 'Time', + Email = 'Email', + Phone = 'Phone', + URL = 'URL', + Password = 'Password', + TextArea = 'TextArea', + Select = 'Select', + MultiSelect = 'MultiSelect', + Radio = 'Radio', + Checkbox = 'Checkbox', + File = 'File', + Image = 'Image', + Signature = 'Signature', + Location = 'Location', + Rating = 'Rating', + Slider = 'Slider', + Color = 'Color', + RichText = 'RichText', + JSON = 'JSON' +} + +// Field Response Types +// export enum FieldResponseType { +// Text = 'Text', +// Number = 'Number', +// Boolean = 'Boolean', +// Date = 'Date', +// DateTime = 'DateTime', +// Time = 'Time', +// Email = 'Email', +// Phone = 'Phone', +// URL = 'URL', +// Password = 'Password', +// TextArea = 'TextArea', +// Select = 'Select', +// MultiSelect = 'MultiSelect', +// Radio = 'Radio', +// Checkbox = 'Checkbox', +// File = 'File', +// Image = 'Image', +// Signature = 'Signature', +// Location = 'Location', +// Rating = 'Rating', +// Slider = 'Slider', +// Color = 'Color', +// RichText = 'RichText', +// JSON = 'JSON' +// } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/form.field/form.field.model.ts b/src/database/sql/typeorm/models/form.field/form.field.model.ts new file mode 100644 index 0000000..fd072fb --- /dev/null +++ b/src/database/sql/typeorm/models/form.field/form.field.model.ts @@ -0,0 +1,246 @@ +import { Entity, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn, OneToMany } from 'typeorm'; +import { FieldResponseType } from './field.types'; +import { SkipLogicEntity } from '../logic/skip.logic.model'; +import { CalculationLogicEntity } from '../logic/calculation.logic.model'; +import { ValidationLogicEntity } from '../logic/validation.logic.model'; +import { BaseEntity } from '../base.entity'; +import { QueryResponseType } from '../../../../../domain.types/forms/query.response.types'; +import { FormTemplate } from '../form.template/form.template.model'; +import { FormSection } from '../form.section/form.section.model'; +import { QuestionResponse } from '../question.response/question.response.model'; +import { InputUnitList } from '../input.unit.list/input.unit.list.model'; + +// Form Field Interface +// export interface FormField { +// FieldId: string; +// Name: string; +// Label: string; +// Title?: string; +// Description?: string; +// DisplayCode?: string; +// ResponseType: FieldResponseType; +// QueryResponseType?: QueryResponseType; +// Required: boolean; +// Value?: any; +// Score?: number; +// Sequence?: number; +// ExpectedAnswer?: string; +// Hint?: string; +// Options?: string; +// ImageResourceId?: string; +// RangeMin?: number; +// RangeMax?: number; +// DefaultExpectedUnit?: string; +// PageBreakAfter?: boolean; +// SkipLogic?: SkipLogicEntity | null; +// CalculateLogic?: CalculationLogicEntity | null; +// ValidateLogic?: ValidationLogicEntity | null; +// } + +// Comprehensive Form Field Entity combining Question and FormField models +@Entity({ name: 'form_fields' }) +export class FormFieldEntity extends BaseEntity { + // Basic Form Field Properties + @Column({ + type: 'varchar', + length: 255, + nullable: false + }) + Name: string; + + @Column({ + type: 'varchar', + length: 255, + nullable: false + }) + Label: string; + + // Question Model Properties + @Column({ + type: 'varchar', + length: 128, + nullable: true + }) + Title?: string; + + @Column({ + type: 'varchar', + length: 512, + nullable: true + }) + Description?: string; + + @Column({ + type: 'varchar', + length: 128, + nullable: true + }) + DisplayCode?: string; + + // Response Types + @Column({ + type: 'varchar', + length: 50, + nullable: false + }) + ResponseType: FieldResponseType; + + @Column({ + type: 'enum', + enum: QueryResponseType, + nullable: true + }) + QueryResponseType?: QueryResponseType; + + // Form Field Properties + @Column({ + type: 'boolean', + nullable: false, + default: false + }) + Required: boolean; + + @Column({ + type: 'text', + nullable: true + }) + Value?: string; // JSON serialized any + + // Question Model Additional Properties + @Column({ + type: 'int', + nullable: true + }) + Score?: number; + + @Column({ + type: 'int', + nullable: true + }) + Sequence?: number; + + @Column({ + type: 'varchar', + nullable: true + }) + ExpectedAnswer?: string; + + @Column({ + type: 'varchar', + length: 512, + nullable: true + }) + Hint?: string; + + @Column({ + type: 'json', + nullable: true + }) + Options?: string; + + @Column({ + type: 'uuid', + nullable: true + }) + ImageResourceId?: string; + + @Column({ + type: 'int', + nullable: true + }) + RangeMin?: number; + + @Column({ + type: 'int', + nullable: true + }) + RangeMax?: number; + + @Column({ + type: 'varchar', + nullable: true + }) + DefaultExpectedUnit?: string; + + @Column({ + type: 'boolean', + nullable: false, + default: false + }) + PageBreakAfter: boolean; + + // Logic References + @Column({ + type: 'uuid', + nullable: true + }) + SkipLogicId?: string; + + @ManyToOne(() => SkipLogicEntity, { nullable: true }) + @JoinColumn({ name: 'SkipLogicId' }) + SkipLogic?: SkipLogicEntity; + + @Column({ + type: 'uuid', + nullable: true + }) + CalculateLogicId?: string; + + @ManyToOne(() => CalculationLogicEntity, { nullable: true }) + @JoinColumn({ name: 'CalculateLogicId' }) + CalculateLogic?: CalculationLogicEntity; + + @Column({ + type: 'uuid', + nullable: true + }) + ValidateLogicId?: string; + + @ManyToOne(() => ValidationLogicEntity, { nullable: true }) + @JoinColumn({ name: 'ValidateLogicId' }) + ValidateLogic?: ValidationLogicEntity; + + // Template and Section References + @Column({ + type: 'uuid', + nullable: true + }) + TemplateId?: string; + + @Column({ + type: 'uuid', + nullable: true + }) + ParentSectionId?: string; + + // Relationships + @OneToMany(() => QuestionResponse, (response) => response.Question) + Responses?: QuestionResponse[]; + + @ManyToOne(() => FormTemplate, (template) => template.Questions, { nullable: true }) + FormTemplate?: FormTemplate; + + @ManyToOne(() => FormSection, (section) => section.Questions, { nullable: true }) + ParentFormSection?: FormSection; + + @ManyToOne(() => InputUnitList, { nullable: true }) + ExpectedInputUnitList?: InputUnitList; + + // Form Reference + @Column({ + type: 'uuid', + nullable: true + }) + FormId?: string; + + // TODO: Add proper Form relationship when Form entity is created + // @ManyToOne(() => Form, form => form.Fields, { nullable: true }) + // @JoinColumn({ name: 'FormId' }) + // Form?: Form; + + @CreateDateColumn() + CreatedAt: Date; + + @UpdateDateColumn() + UpdatedAt: Date; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/logic/base.logic.model.ts b/src/database/sql/typeorm/models/logic/base.logic.model.ts index 84534ed..70c7956 100644 --- a/src/database/sql/typeorm/models/logic/base.logic.model.ts +++ b/src/database/sql/typeorm/models/logic/base.logic.model.ts @@ -1,36 +1,12 @@ -import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; -import { LogicType } from '../../../../engine/enums'; - -// Base Logic Interface -export interface BaseLogic { - id: string; - Type: LogicType; - FieldId: string; - Enabled: boolean; -} +import { Entity, Column } from 'typeorm'; +import { BaseEntity } from '../base.entity'; +import { LogicType } from './logic.types'; // Base Logic Entity (Abstract - no table) -export abstract class BaseLogicEntity { - @PrimaryGeneratedColumn('uuid') - id!: string; - - @Column({ - type: 'varchar', - length: 255, - nullable: false - }) - FieldId!: string; - - @Column({ - type: 'boolean', - nullable: false, - default: true - }) - Enabled!: boolean; - - @CreateDateColumn() - CreatedAt!: Date; +export abstract class BaseLogicEntity extends BaseEntity { + @Column({ type: 'varchar', length: 255, nullable: false }) + FieldId: string; - @UpdateDateColumn() - UpdatedAt!: Date; + @Column({ type: 'boolean', nullable: false, default: true }) + Enabled: boolean; } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/logic/calculation.logic.model.ts b/src/database/sql/typeorm/models/logic/calculation.logic.model.ts new file mode 100644 index 0000000..f9b25d7 --- /dev/null +++ b/src/database/sql/typeorm/models/logic/calculation.logic.model.ts @@ -0,0 +1,18 @@ +import { Entity, Column, OneToMany } from 'typeorm'; +import { LogicType } from './logic.types'; +import { CalculationRuleEntity } from '../rule/calculation.rule.model'; +import { BaseLogicEntity } from './base.logic.model'; + +// Calculation Logic Entity +@Entity({ name: 'calculation_logics' }) +export class CalculationLogicEntity extends BaseLogicEntity { + + @Column({ type: 'varchar', length: 50, nullable: false, default: LogicType.Calculation }) + Type: LogicType.Calculation; + + @OneToMany(() => CalculationRuleEntity, rule => rule.LogicId) + Rules!: CalculationRuleEntity[]; + + @Column({ type: 'text', nullable: true }) + FallbackValue?: string; // JSON serialized value +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/logic/legacy.logic.model.ts b/src/database/sql/typeorm/models/logic/legacy.logic.model.ts new file mode 100644 index 0000000..6255ac9 --- /dev/null +++ b/src/database/sql/typeorm/models/logic/legacy.logic.model.ts @@ -0,0 +1,38 @@ +import { Entity, Column } from 'typeorm'; +import { BaseLogicEntity } from './base.logic.model'; +import { LogicType } from './logic.types'; + +// Legacy Logic for backward compatibility - Separate Table +@Entity({ name: 'legacy_logics' }) +export class LegacyLogicEntity extends BaseLogicEntity { + @Column({ + type: 'varchar', + length: 50, + nullable: false + }) + Type: LogicType; + + @Column({ + type: 'text', + nullable: true + }) + Rules: string; // JSON serialized Rule[] + + @Column({ + type: 'uuid', + nullable: true + }) + FallbackRuleId?: string; + + @Column({ + type: 'text', + nullable: true + }) + FallbackValue?: string; // JSON serialized value + + @Column({ + type: 'boolean', + nullable: true + }) + DefaultSkip?: boolean; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/logic/logic.types.ts b/src/database/sql/typeorm/models/logic/logic.types.ts new file mode 100644 index 0000000..0b68feb --- /dev/null +++ b/src/database/sql/typeorm/models/logic/logic.types.ts @@ -0,0 +1,6 @@ +// Logic Types +export enum LogicType { + Skip = 'Skip', + Calculation = 'Calculation', + Validation = 'Validation' +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/logic/skip.logic.model.ts b/src/database/sql/typeorm/models/logic/skip.logic.model.ts new file mode 100644 index 0000000..92846c8 --- /dev/null +++ b/src/database/sql/typeorm/models/logic/skip.logic.model.ts @@ -0,0 +1,19 @@ +import { Entity, Column, OneToMany } from 'typeorm'; +import { BaseEntity } from '../base.entity'; +import { LogicType } from './logic.types'; +import { SkipRuleEntity } from '../rule/skip.rule.model'; + +// Skip Logic Entity +@Entity({ name: 'skip_logics' }) +export class SkipLogicEntity extends BaseEntity { + + @Column({ type: 'varchar', length: 50, nullable: false, default: LogicType.Skip }) + Type: LogicType.Skip; + + // TODO: Add proper SkipRule relationship when SkipRule entity is created + @OneToMany(() => SkipRuleEntity, rule => rule.LogicId) + Rules: SkipRuleEntity[]; + + @Column({ type: 'boolean', nullable: true }) + DefaultSkip?: boolean; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/logic/validation.logic.model.ts b/src/database/sql/typeorm/models/logic/validation.logic.model.ts new file mode 100644 index 0000000..bed02c2 --- /dev/null +++ b/src/database/sql/typeorm/models/logic/validation.logic.model.ts @@ -0,0 +1,16 @@ +import { Entity, Column, OneToMany } from 'typeorm'; +import { BaseEntity } from '../base.entity'; +import { LogicType } from './logic.types'; +import { ValidationRuleEntity } from '../rule/validation.rule.model'; + + +// Validation Logic Entity +@Entity({ name: 'validation_logics' }) +export class ValidationLogicEntity extends BaseEntity { + + @Column({ type: 'varchar', length: 50, nullable: false, default: LogicType.Validation }) + Type: LogicType.Validation; + + @OneToMany(() => ValidationRuleEntity, rule => rule.LogicId) + Rules: ValidationRuleEntity[]; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/base.operation.model.ts b/src/database/sql/typeorm/models/operation/base.operation.model.ts new file mode 100644 index 0000000..f7d8af9 --- /dev/null +++ b/src/database/sql/typeorm/models/operation/base.operation.model.ts @@ -0,0 +1,12 @@ +import { Entity, Column } from 'typeorm'; +import { BaseEntity } from '../base.entity'; +import { OperationType } from './operation.types'; + +// Base Operation Entity (Abstract - no table) +export abstract class BaseOperationEntity extends BaseEntity { + @Column({ type: 'varchar', length: 255, nullable: true }) + Name?: string; + + @Column({ type: 'text', nullable: true }) + Description?: string; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/composition.operation.model.ts b/src/database/sql/typeorm/models/operation/composition.operation.model.ts new file mode 100644 index 0000000..4e80d5f --- /dev/null +++ b/src/database/sql/typeorm/models/operation/composition.operation.model.ts @@ -0,0 +1,16 @@ +import { Entity, Column } from 'typeorm'; +import { BaseOperationEntity } from './base.operation.model'; +import { OperationType, CompositionOperatorType } from './operation.types'; + +// Composition Operation Entity +@Entity({ name: 'composition_operations' }) +export class CompositionOperationEntity extends BaseOperationEntity { + @Column({ type: 'varchar', length: 50, nullable: false, default: OperationType.Composition }) + Type: OperationType.Composition; + + @Column({ type: 'varchar', length: 50, nullable: false }) + Operator: CompositionOperatorType; + + @Column({ type: 'text', nullable: false }) + Children: string; // JSON serialized Operation[] (stored as IDs) +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/function.expression.operation.model.ts b/src/database/sql/typeorm/models/operation/function.expression.operation.model.ts new file mode 100644 index 0000000..32b70ab --- /dev/null +++ b/src/database/sql/typeorm/models/operation/function.expression.operation.model.ts @@ -0,0 +1,17 @@ +import { Entity, Column } from 'typeorm'; +import { BaseOperationEntity } from './base.operation.model'; +import { OperationType } from './operation.types'; + + +// Function Expression Operation Entity +@Entity({ name: 'function_expression_operations' }) +export class FunctionExpressionOperationEntity extends BaseOperationEntity { + @Column({ type: 'varchar', length: 50, nullable: false, default: OperationType.FunctionExpression }) + Type: OperationType.FunctionExpression; + + @Column({ type: 'text', nullable: false }) + Expression: string; + + @Column({ type: 'text', nullable: false }) + Variables: string; // JSON serialized Record +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/iterate.operation.model.ts b/src/database/sql/typeorm/models/operation/iterate.operation.model.ts new file mode 100644 index 0000000..e2aab2d --- /dev/null +++ b/src/database/sql/typeorm/models/operation/iterate.operation.model.ts @@ -0,0 +1,24 @@ +import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { BaseOperationEntity } from './base.operation.model'; +import { OperationType } from './operation.types'; + + +// Iterate Operation Entity +@Entity({ name: 'iterate_operations' }) +export class IterateOperationEntity extends BaseOperationEntity { + @Column({ type: 'varchar', length: 50, nullable: false, default: OperationType.Iterate }) + Type: OperationType.Iterate; + + @Column({ type: 'text', nullable: false }) + ArrayOperand: string; // JSON serialized Operand + + @Column({ type: 'varchar', length: 100, nullable: false }) + ItemAlias: string; + + @Column({ type: 'uuid', nullable: false }) + OperationId: string; // Reference to child operation + + @ManyToOne(() => BaseOperationEntity, { nullable: false }) + @JoinColumn({ name: 'OperationId' }) + Operation: BaseOperationEntity; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/logical.operation.model.ts b/src/database/sql/typeorm/models/operation/logical.operation.model.ts new file mode 100644 index 0000000..b5b41d7 --- /dev/null +++ b/src/database/sql/typeorm/models/operation/logical.operation.model.ts @@ -0,0 +1,17 @@ +import { Entity, Column } from 'typeorm'; +import { BaseOperationEntity } from './base.operation.model'; +import { OperationType, LogicalOperatorType } from './operation.types'; + + +// Logical Operation Entity +@Entity({ name: 'logical_operations' }) +export class LogicalOperationEntity extends BaseOperationEntity { + @Column({ type: 'varchar', length: 50, nullable: false, default: OperationType.Logical }) + Type: OperationType.Logical; + + @Column({ type: 'varchar', length: 50, nullable: false }) + Operator: LogicalOperatorType; + + @Column({ type: 'text', nullable: false }) + Operands: string; // JSON serialized Operand[] +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/mathematical.operation.model.ts b/src/database/sql/typeorm/models/operation/mathematical.operation.model.ts new file mode 100644 index 0000000..d355772 --- /dev/null +++ b/src/database/sql/typeorm/models/operation/mathematical.operation.model.ts @@ -0,0 +1,18 @@ +import { Entity, Column } from 'typeorm'; +import { BaseOperationEntity } from './base.operation.model'; +import { OperationType, MathematicalOperatorType } from './operation.types'; + + + +// Mathematical Operation Entity +@Entity({ name: 'mathematical_operations' }) +export class MathematicalOperationEntity extends BaseOperationEntity { + @Column({ type: 'varchar', length: 50, nullable: false, default: OperationType.Mathematical }) + Type: OperationType.Mathematical; + + @Column({ type: 'varchar', length: 50, nullable: false }) + Operator: MathematicalOperatorType; + + @Column({ type: 'text', nullable: false }) + Operands: string; // JSON serialized Operand[] +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/operation.types.ts b/src/database/sql/typeorm/models/operation/operation.types.ts new file mode 100644 index 0000000..cfa4f05 --- /dev/null +++ b/src/database/sql/typeorm/models/operation/operation.types.ts @@ -0,0 +1,47 @@ +// Operation Types +export enum OperationType { + Logical = 'Logical', + Mathematical = 'Mathematical', + Composition = 'Composition', + Iterate = 'Iterate', + FunctionExpression = 'FunctionExpression' +} + +// Logical Operator Types +export enum LogicalOperatorType { + Equal = 'Equal', + NotEqual = 'NotEqual', + GreaterThan = 'GreaterThan', + GreaterThanOrEqual = 'GreaterThanOrEqual', + LessThan = 'LessThan', + LessThanOrEqual = 'LessThanOrEqual', + In = 'In', + NotIn = 'NotIn', + Contains = 'Contains', + DoesNotContain = 'DoesNotContain', + Between = 'Between', + IsTrue = 'IsTrue', + IsFalse = 'IsFalse', + Exists = 'Exists', + HasConsecutiveOccurrences = 'HasConsecutiveOccurrences', + RangesOverlap = 'RangesOverlap', + None = 'None' +} + +// Mathematical Operator Types +export enum MathematicalOperatorType { + Add = 'Add', + Subtract = 'Subtract', + Divide = 'Divide', + Multiply = 'Multiply', + Percentage = 'Percentage', + None = 'None' +} + +// Composition Operator Types +export enum CompositionOperatorType { + And = 'And', + Or = 'Or', + Xor = 'Xor', + None = 'None' +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/rule/base.rule.model.ts b/src/database/sql/typeorm/models/rule/base.rule.model.ts new file mode 100644 index 0000000..cebe1b1 --- /dev/null +++ b/src/database/sql/typeorm/models/rule/base.rule.model.ts @@ -0,0 +1,13 @@ +import { Entity, Column } from 'typeorm'; +import { BaseEntity } from '../base.entity'; + + + +// Base Rule Entity (Abstract - no table) +export abstract class BaseRuleEntity extends BaseEntity { + @Column({ type: 'varchar', length: 255, nullable: false }) + Name: string; + + @Column({ type: 'text', nullable: true }) + Description?: string; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/rule/calculation.rule.model.ts b/src/database/sql/typeorm/models/rule/calculation.rule.model.ts new file mode 100644 index 0000000..9c0c733 --- /dev/null +++ b/src/database/sql/typeorm/models/rule/calculation.rule.model.ts @@ -0,0 +1,29 @@ +import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { BaseRuleEntity } from './base.rule.model'; +import { BaseOperationEntity } from '../operation/base.operation.model'; +import { CalculationLogicEntity } from '../logic/calculation.logic.model'; + +// Calculation Rule Entity +@Entity({ name: 'calculation_rules' }) +export class CalculationRuleEntity extends BaseRuleEntity { + @Column({ type: 'uuid', nullable: true }) + ConditionForOperationId?: string; + + @ManyToOne(() => BaseOperationEntity, { nullable: true }) + @JoinColumn({ name: 'ConditionForOperationId' }) + ConditionForOperation?: BaseOperationEntity; + + @Column({ type: 'uuid', nullable: false }) + OperationId: string; + + @ManyToOne(() => BaseOperationEntity, { nullable: false }) + @JoinColumn({ name: 'OperationId' }) + Operation: BaseOperationEntity; + + @Column({ type: 'uuid', nullable: true }) + LogicId?: string; + + @ManyToOne(() => CalculationLogicEntity, { nullable: true }) + @JoinColumn({ name: 'LogicId' }) + Logic?: CalculationLogicEntity; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/rule/legacy.rule.model.ts b/src/database/sql/typeorm/models/rule/legacy.rule.model.ts new file mode 100644 index 0000000..56f4301 --- /dev/null +++ b/src/database/sql/typeorm/models/rule/legacy.rule.model.ts @@ -0,0 +1,30 @@ +import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { BaseRuleEntity } from './base.rule.model'; +import { BaseOperationEntity } from '../operation/base.operation.model'; + +// Legacy Rule for backward compatibility - Separate Table +@Entity({ name: 'legacy_rules' }) +export class LegacyRuleEntity extends BaseRuleEntity { + @Column({ + type: 'uuid', + nullable: false + }) + OperationId: string; + + @ManyToOne(() => BaseOperationEntity, { nullable: false }) + @JoinColumn({ name: 'OperationId' }) + Operation: BaseOperationEntity; + + @Column({ + type: 'varchar', + length: 500, + nullable: true + }) + ErrorMessage?: string; + + @Column({ + type: 'uuid', + nullable: true + }) + LogicId?: string; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/rule/skip.rule.model.ts b/src/database/sql/typeorm/models/rule/skip.rule.model.ts new file mode 100644 index 0000000..9e69afa --- /dev/null +++ b/src/database/sql/typeorm/models/rule/skip.rule.model.ts @@ -0,0 +1,26 @@ +import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { BaseRuleEntity } from './base.rule.model'; +import { BaseOperationEntity } from '../operation/base.operation.model'; +import { SkipLogicEntity } from '../logic/skip.logic.model'; + + +// Skip Rule Entity +@Entity({ name: 'skip_rules' }) +export class SkipRuleEntity extends BaseRuleEntity { + @Column({ type: 'uuid', nullable: false }) + OperationId: string; + + @ManyToOne(() => BaseOperationEntity, { nullable: false }) + @JoinColumn({ name: 'OperationId' }) + Operation: BaseOperationEntity; + + @Column({ type: 'boolean', nullable: false, default: true }) + SkipWhenTrue: boolean; + + @Column({ type: 'uuid', nullable: true }) + LogicId?: string; + + @ManyToOne(() => SkipLogicEntity, { nullable: true }) + @JoinColumn({ name: 'LogicId' }) + Logic?: SkipLogicEntity; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/rule/validation.rule.model.ts b/src/database/sql/typeorm/models/rule/validation.rule.model.ts new file mode 100644 index 0000000..ede9068 --- /dev/null +++ b/src/database/sql/typeorm/models/rule/validation.rule.model.ts @@ -0,0 +1,29 @@ +import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { BaseRuleEntity } from './base.rule.model'; +import { BaseOperationEntity } from '../operation/base.operation.model'; +import { ValidationLogicEntity } from '../logic/validation.logic.model'; + + +// Validation Rule Entity +@Entity({ name: 'validation_rules' }) +export class ValidationRuleEntity extends BaseRuleEntity { + @Column({ type: 'uuid', nullable: false }) + OperationId: string; + + @ManyToOne(() => BaseOperationEntity, { nullable: false }) + @JoinColumn({ name: 'OperationId' }) + Operation: BaseOperationEntity; + + @Column({ type: 'boolean', nullable: false, default: false }) + ErrorWhenFalse: boolean; + + @Column({ type: 'varchar', length: 500, nullable: false }) + ErrorMessage: string; + + @Column({ type: 'uuid', nullable: true }) + LogicId?: string; + + @ManyToOne(() => ValidationLogicEntity, { nullable: true }) + @JoinColumn({ name: 'LogicId' }) + Logic?: ValidationLogicEntity; +} \ No newline at end of file diff --git a/src/domain.types/forms/form.field.domain.types.ts b/src/domain.types/forms/form.field.domain.types.ts new file mode 100644 index 0000000..f2e5b3d --- /dev/null +++ b/src/domain.types/forms/form.field.domain.types.ts @@ -0,0 +1,198 @@ +import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; +import { FieldResponseType } from "../../database/sql/typeorm/models/form.field/field.types"; +import { QueryResponseType } from "./query.response.types"; + +// Form Field Option Interface +export interface FormFieldOption { + Text: string; + Sequence: string; + ImageUrl: string; +} + +// Base Form Field DTOs +export interface FormFieldCreateModel { + Name: string; + Label: string; + Title?: string; + Description?: string; + DisplayCode?: string; + ResponseType: FieldResponseType; + QueryResponseType?: QueryResponseType; + Required: boolean; + Value?: string; + Score?: number; + Sequence?: number; + ExpectedAnswer?: string; + Hint?: string; + Options?: string; // JSON serialized + ImageResourceId?: string; + RangeMin?: number; + RangeMax?: number; + DefaultExpectedUnit?: string; + PageBreakAfter?: boolean; + SkipLogicId?: string; + CalculateLogicId?: string; + ValidateLogicId?: string; + TemplateId?: string; + ParentSectionId?: string; + FormId?: string; +} + +export interface FormFieldUpdateModel { + Name?: string; + Label?: string; + Title?: string; + Description?: string; + DisplayCode?: string; + ResponseType?: FieldResponseType; + QueryResponseType?: QueryResponseType; + Required?: boolean; + Value?: string; + Score?: number; + Sequence?: number; + ExpectedAnswer?: string; + Hint?: string; + Options?: string; + ImageResourceId?: string; + RangeMin?: number; + RangeMax?: number; + DefaultExpectedUnit?: string; + PageBreakAfter?: boolean; + SkipLogicId?: string; + CalculateLogicId?: string; + ValidateLogicId?: string; + TemplateId?: string; + ParentSectionId?: string; + FormId?: string; +} + +export interface FormFieldResponseDto { + id: string; + Name: string; + Label: string; + Title?: string; + Description?: string; + DisplayCode?: string; + ResponseType: FieldResponseType; + QueryResponseType?: QueryResponseType; + Required: boolean; + Value?: string; + Score?: number; + Sequence?: number; + ExpectedAnswer?: string; + Hint?: string; + Options?: string; + ImageResourceId?: string; + RangeMin?: number; + RangeMax?: number; + DefaultExpectedUnit?: string; + PageBreakAfter: boolean; + SkipLogicId?: string; + CalculateLogicId?: string; + ValidateLogicId?: string; + TemplateId?: string; + ParentSectionId?: string; + FormId?: string; + SkipLogic?: { + id: string; + Type: string; + DefaultSkip?: boolean; + }; + CalculateLogic?: { + id: string; + Type: string; + DefaultSkip?: boolean; + }; + ValidateLogic?: { + id: string; + Type: string; + DefaultSkip?: boolean; + }; + FormTemplate?: { + id: string; + Title: string; + Description?: string; + DisplayCode: string; + }; + ParentFormSection?: { + id: string; + SectionIdentifier: string; + Title: string; + Description?: string; + DisplayCode: string; + }; + ExpectedInputUnitList?: { + id: string; + Name: string; + Description?: string; + }; + Responses?: { + id: string; + ResponseValue: string; + CreatedAt: Date; + }[]; + CreatedAt: Date; + UpdatedAt?: Date; +} + +// Form Field Search DTOs +export interface FormFieldSearchFilters extends BaseSearchFilters { + id?: string; + name?: string; + label?: string; + title?: string; + description?: string; + displayCode?: string; + responseType?: FieldResponseType; + queryResponseType?: QueryResponseType; + required?: boolean; + score?: number; + sequence?: number; + expectedAnswer?: string; + hint?: string; + rangeMin?: number; + rangeMax?: number; + defaultExpectedUnit?: string; + pageBreakAfter?: boolean; + skipLogicId?: string; + calculateLogicId?: string; + validateLogicId?: string; + templateId?: string; + parentSectionId?: string; + formId?: string; +} + +export interface FormFieldSearchResults extends BaseSearchResults { + Items: FormFieldSearchResponseDto[]; +} + +export interface FormFieldSearchResponseDto extends BaseSearchResults { + id: string; + Name: string; + Label: string; + Title?: string; + Description?: string; + DisplayCode?: string; + ResponseType: FieldResponseType; + QueryResponseType?: QueryResponseType; + Required: boolean; + Value?: string; + Score?: number; + Sequence?: number; + ExpectedAnswer?: string; + Hint?: string; + Options?: string; + ImageResourceId?: string; + RangeMin?: number; + RangeMax?: number; + DefaultExpectedUnit?: string; + PageBreakAfter: boolean; + SkipLogicId?: string; + CalculateLogicId?: string; + ValidateLogicId?: string; + TemplateId?: string; + ParentSectionId?: string; + FormId?: string; + CreatedAt: Date; + UpdatedAt?: Date; +} \ No newline at end of file diff --git a/src/domain.types/forms/logic.domain.types.ts b/src/domain.types/forms/logic.domain.types.ts new file mode 100644 index 0000000..0873389 --- /dev/null +++ b/src/domain.types/forms/logic.domain.types.ts @@ -0,0 +1,141 @@ +import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; +import { LogicType } from "../../database/sql/typeorm/models/logic/logic.types"; + +// Base Logic DTOs +export interface BaseLogicCreateModel { + Type: LogicType; + DefaultSkip?: boolean; +} + +export interface BaseLogicUpdateModel { + Type?: LogicType; + DefaultSkip?: boolean; +} + +export interface BaseLogicResponseDto { + id: string; + Type: LogicType; + DefaultSkip?: boolean; + CreatedAt: Date; + UpdatedAt?: Date; +} + +// Skip Logic DTOs +export interface SkipLogicCreateModel extends BaseLogicCreateModel { + Type: LogicType.Skip; + DefaultSkip?: boolean; +} + +export interface SkipLogicUpdateModel extends BaseLogicUpdateModel { + Type?: LogicType.Skip; + DefaultSkip?: boolean; +} + +export interface SkipLogicResponseDto extends BaseLogicResponseDto { + Type: LogicType.Skip; + Rules?: SkipRuleResponseDto[]; +} + +// Calculation Logic DTOs +export interface CalculationLogicCreateModel extends BaseLogicCreateModel { + Type: LogicType.Calculation; + DefaultSkip?: boolean; + FallbackValue?: string; +} + +export interface CalculationLogicUpdateModel extends BaseLogicUpdateModel { + Type?: LogicType.Calculation; + DefaultSkip?: boolean; + FallbackValue?: string; +} + +export interface CalculationLogicResponseDto extends BaseLogicResponseDto { + Type: LogicType.Calculation; + FallbackValue?: string; + Rules?: CalculationRuleResponseDto[]; +} + +// Validation Logic DTOs +export interface ValidationLogicCreateModel extends BaseLogicCreateModel { + Type: LogicType.Validation; + DefaultSkip?: boolean; +} + +export interface ValidationLogicUpdateModel extends BaseLogicUpdateModel { + Type?: LogicType.Validation; + DefaultSkip?: boolean; +} + +export interface ValidationLogicResponseDto extends BaseLogicResponseDto { + Type: LogicType.Validation; + Rules?: ValidationRuleResponseDto[]; +} + +// Legacy Logic DTOs +export interface LegacyLogicCreateModel extends BaseLogicCreateModel { + Type: LogicType; + DefaultSkip?: boolean; +} + +export interface LegacyLogicUpdateModel extends BaseLogicUpdateModel { + Type?: LogicType; + DefaultSkip?: boolean; +} + +export interface LegacyLogicResponseDto extends BaseLogicResponseDto { + Type: LogicType; + Rules?: LegacyRuleResponseDto[]; +} + +// Logic Search DTOs +export interface LogicSearchFilters extends BaseSearchFilters { + id?: string; + type?: LogicType; + defaultSkip?: boolean; +} + +export interface LogicSearchResults extends BaseSearchResults { + Items: LogicSearchResponseDto[]; +} + +export interface LogicSearchResponseDto extends BaseSearchResults { + id: string; + Type: LogicType; + DefaultSkip?: boolean; + CreatedAt: Date; + UpdatedAt?: Date; +} + +// Import rule DTOs (these will be defined in rule.domain.types.ts) +export interface SkipRuleResponseDto { + id: string; + OperationId: string; + SkipWhenTrue: boolean; + LogicId?: string; + CreatedAt: Date; + UpdatedAt?: Date; +} + +export interface CalculationRuleResponseDto { + id: string; + OperationId: string; + LogicId?: string; + CreatedAt: Date; + UpdatedAt?: Date; +} + +export interface ValidationRuleResponseDto { + id: string; + OperationId: string; + LogicId?: string; + CreatedAt: Date; + UpdatedAt?: Date; +} + +export interface LegacyRuleResponseDto { + id: string; + OperationId: string; + LogicId?: string; + CreatedAt: Date; + UpdatedAt?: Date; +} \ No newline at end of file diff --git a/src/domain.types/forms/operation.domain.types.ts b/src/domain.types/forms/operation.domain.types.ts new file mode 100644 index 0000000..fc511b5 --- /dev/null +++ b/src/domain.types/forms/operation.domain.types.ts @@ -0,0 +1,155 @@ +import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; +import { + OperationType, + LogicalOperatorType, + MathematicalOperatorType, + CompositionOperatorType +} from "../../database/sql/typeorm/models/operation/operation.types"; + +// Base Operation DTOs +export interface BaseOperationCreateModel { + Name?: string; + Description?: string; +} + +export interface BaseOperationUpdateModel { + Name?: string; + Description?: string; +} + +export interface BaseOperationResponseDto { + id: string; + Name?: string; + Description?: string; + CreatedAt: Date; + UpdatedAt?: Date; +} + +// Logical Operation DTOs +export interface LogicalOperationCreateModel extends BaseOperationCreateModel { + OperatorType: LogicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface LogicalOperationUpdateModel extends BaseOperationUpdateModel { + OperatorType?: LogicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface LogicalOperationResponseDto extends BaseOperationResponseDto { + OperatorType: LogicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +// Mathematical Operation DTOs +export interface MathematicalOperationCreateModel extends BaseOperationCreateModel { + OperatorType: MathematicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface MathematicalOperationUpdateModel extends BaseOperationUpdateModel { + OperatorType?: MathematicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface MathematicalOperationResponseDto extends BaseOperationResponseDto { + OperatorType: MathematicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +// Composition Operation DTOs +export interface CompositionOperationCreateModel extends BaseOperationCreateModel { + OperatorType: CompositionOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface CompositionOperationUpdateModel extends BaseOperationUpdateModel { + OperatorType?: CompositionOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface CompositionOperationResponseDto extends BaseOperationResponseDto { + OperatorType: CompositionOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +// Iterate Operation DTOs +export interface IterateOperationCreateModel extends BaseOperationCreateModel { + ArrayOperand: string; + ItemAlias: string; + OperationId: string; +} + +export interface IterateOperationUpdateModel extends BaseOperationUpdateModel { + ArrayOperand?: string; + ItemAlias?: string; + OperationId?: string; +} + +export interface IterateOperationResponseDto extends BaseOperationResponseDto { + ArrayOperand: string; + ItemAlias: string; + OperationId: string; + Operation?: { + id: string; + Name?: string; + Description?: string; + }; +} + +// Function Expression Operation DTOs +export interface FunctionExpressionOperationCreateModel extends BaseOperationCreateModel { + FunctionName?: string; + Parameters?: Record; + Expression?: string; +} + +export interface FunctionExpressionOperationUpdateModel extends BaseOperationUpdateModel { + FunctionName?: string; + Parameters?: Record; + Expression?: string; +} + +export interface FunctionExpressionOperationResponseDto extends BaseOperationResponseDto { + FunctionName?: string; + Parameters?: Record; + Expression?: string; +} + +// Operation Search DTOs +export interface OperationSearchFilters extends BaseSearchFilters { + id?: string; + name?: string; + description?: string; + operatorType?: LogicalOperatorType | MathematicalOperatorType | CompositionOperatorType; +} + +export interface OperationSearchResults extends BaseSearchResults { + Items: OperationSearchResponseDto[]; +} + +export interface OperationSearchResponseDto extends BaseSearchResults { + id: string; + Name?: string; + Description?: string; + CreatedAt: Date; + UpdatedAt?: Date; +} \ No newline at end of file diff --git a/src/domain.types/forms/rule.domain.types.ts b/src/domain.types/forms/rule.domain.types.ts new file mode 100644 index 0000000..bacfd9a --- /dev/null +++ b/src/domain.types/forms/rule.domain.types.ts @@ -0,0 +1,175 @@ +import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; + +// Base Rule DTOs +export interface BaseRuleCreateModel { + Name?: string; + Description?: string; + Priority?: number; + IsActive?: boolean; +} + +export interface BaseRuleUpdateModel { + Name?: string; + Description?: string; + Priority?: number; + IsActive?: boolean; +} + +export interface BaseRuleResponseDto { + id: string; + Name?: string; + Description?: string; + Priority?: number; + IsActive?: boolean; + CreatedAt: Date; + UpdatedAt?: Date; +} + +// Skip Rule DTOs +export interface SkipRuleCreateModel extends BaseRuleCreateModel { + OperationId: string; + SkipWhenTrue: boolean; + LogicId?: string; +} + +export interface SkipRuleUpdateModel extends BaseRuleUpdateModel { + OperationId?: string; + SkipWhenTrue?: boolean; + LogicId?: string; +} + +export interface SkipRuleResponseDto extends BaseRuleResponseDto { + OperationId: string; + SkipWhenTrue: boolean; + LogicId?: string; + Operation?: { + id: string; + Name?: string; + Description?: string; + }; + Logic?: { + id: string; + Type: string; + DefaultSkip?: boolean; + }; +} + +// Calculation Rule DTOs +export interface CalculationRuleCreateModel extends BaseRuleCreateModel { + ConditionForOperationId?: string; + OperationId: string; + LogicId?: string; +} + +export interface CalculationRuleUpdateModel extends BaseRuleUpdateModel { + ConditionForOperationId?: string; + OperationId?: string; + LogicId?: string; +} + +export interface CalculationRuleResponseDto extends BaseRuleResponseDto { + ConditionForOperationId?: string; + OperationId: string; + LogicId?: string; + ConditionForOperation?: { + id: string; + Name?: string; + Description?: string; + }; + Operation?: { + id: string; + Name?: string; + Description?: string; + }; + Logic?: { + id: string; + Type: string; + DefaultSkip?: boolean; + FallbackValue?: string; + }; +} + +// Validation Rule DTOs +export interface ValidationRuleCreateModel extends BaseRuleCreateModel { + OperationId: string; + ErrorWhenFalse: boolean; + ErrorMessage: string; + LogicId?: string; +} + +export interface ValidationRuleUpdateModel extends BaseRuleUpdateModel { + OperationId?: string; + ErrorWhenFalse?: boolean; + ErrorMessage?: string; + LogicId?: string; +} + +export interface ValidationRuleResponseDto extends BaseRuleResponseDto { + OperationId: string; + ErrorWhenFalse: boolean; + ErrorMessage: string; + LogicId?: string; + Operation?: { + id: string; + Name?: string; + Description?: string; + }; + Logic?: { + id: string; + Type: string; + DefaultSkip?: boolean; + }; +} + +// Legacy Rule DTOs +export interface LegacyRuleCreateModel extends BaseRuleCreateModel { + OperationId: string; + LogicId?: string; +} + +export interface LegacyRuleUpdateModel extends BaseRuleUpdateModel { + OperationId?: string; + LogicId?: string; +} + +export interface LegacyRuleResponseDto extends BaseRuleResponseDto { + OperationId: string; + LogicId?: string; + Operation?: { + id: string; + Name?: string; + Description?: string; + }; + Logic?: { + id: string; + Type: string; + DefaultSkip?: boolean; + }; +} + +// Rule Search DTOs +export interface RuleSearchFilters extends BaseSearchFilters { + id?: string; + name?: string; + description?: string; + priority?: number; + isActive?: boolean; + operationId?: string; + logicId?: string; +} + +export interface RuleSearchResults extends BaseSearchResults { + Items: RuleSearchResponseDto[]; +} + +export interface RuleSearchResponseDto extends BaseSearchResults { + id: string; + Name?: string; + Description?: string; + Priority?: number; + IsActive?: boolean; + OperationId?: string; + LogicId?: string; + CreatedAt: Date; + UpdatedAt?: Date; +} \ No newline at end of file From 682a7d762502103a01d161a9b4a9fd19436c1aa2 Mon Sep 17 00:00:00 2001 From: inflection-prashant Date: Tue, 15 Jul 2025 17:42:24 +0530 Subject: [PATCH 06/29] complete CRUD request for all tables rules,operations and rules and additional tables like favourite template and approval --- .../Create favorite template.bru | 47 ++ .../Delete favorite template.bru | 24 + .../Get favorite template by id.bru | 29 ++ .../Search favorite templates.bru | 54 ++ .../Update favorite template.bru | 39 ++ .../Create calculation logic.bru | 54 ++ .../Delete calculation logic.bru | 24 + .../Get calculation logic by id.bru | 32 ++ .../Search calculation logic.bru | 56 ++ .../Update calculation logic.bru | 46 ++ .../Field Logic/Calculation Logic/folder.bru | 4 + .../Skip Logic/Create skip logic.bru | 51 ++ .../Skip Logic/Delete skip logic.bru | 24 + .../Skip Logic/Get skip logic by id.bru | 30 ++ .../Skip Logic/Search skip logic.bru | 55 ++ .../Skip Logic/Update skip logic.bru | 43 ++ .../Field Logic/Skip Logic/folder.bru | 4 + .../Create validation logic.bru | 51 ++ .../Delete validation logic.bru | 24 + .../Get validation logic by id.bru | 30 ++ .../Search validation logic.bru | 55 ++ .../Update validation logic.bru | 43 ++ .../Field Logic/Validation Logic/folder.bru | 4 + bruno/form-service/Field Logic/folder.bru | 4 + .../Create composition operation.bru | 54 ++ .../Delete composition operation.bru | 24 + .../Get composition operation by id.bru | 31 ++ .../Search composition operations.bru | 56 ++ .../Update composition operation.bru | 46 ++ .../Composition Operations/folder.bru | 4 + .../Create function expression operation.bru | 57 ++ .../Delete function expression operation.bru | 24 + ...et function expression operation by id.bru | 32 ++ .../Search function expression operations.bru | 56 ++ .../Update function expression operation.bru | 49 ++ .../Function Expression Operations/folder.bru | 4 + .../Create iterate operation.bru | 59 +++ .../Delete iterate operation.bru | 24 + .../Get iterate operation by id.bru | 34 ++ .../Search iterate operations.bru | 57 ++ .../Update iterate operation.bru | 52 ++ .../Iterate Operations/folder.bru | 4 + .../Create logical operation.bru | 53 ++ .../Delete logical operation.bru | 24 + .../Get logical operation by id.bru | 32 ++ .../Search logical operations.bru | 55 ++ .../Update logical operation.bru | 46 ++ .../Logical Operations/folder.bru | 4 + .../Create mathematical operation.bru | 56 ++ .../Delete mathematical operation.bru | 23 + .../Get mathematical operation by id.bru | 33 ++ .../Search mathematical operations.bru | 56 ++ .../Update mathematical operation.bru | 49 ++ .../Mathematical Operations/folder.bru | 4 + bruno/form-service/Field Operation/folder.bru | 4 + .../Create calculation rule.bru | 55 ++ .../Delete calculation rule.bru | 24 + .../Get calculation rule by id.bru | 32 ++ .../Search calculation rules.bru | 57 ++ .../Update calculation rule.bru | 49 ++ .../Field Rules/Calculation Rules/folder.bru | 4 + .../Skip Rules/Create skip rule.bru | 58 +++ .../Skip Rules/Delete skip rule.bru | 24 + .../Skip Rules/Get skip rule by id.bru | 32 ++ .../Skip Rules/Search skip rules.bru | 57 ++ .../Skip Rules/Update skip rule.bru | 50 ++ .../Field Rules/Skip Rules/folder.bru | 4 + .../Create validation rule.bru | 61 +++ .../Delete validation rule.bru | 24 + .../Get validation rule by id.bru | 33 ++ .../Search validation rules.bru | 58 +++ .../Update validation rule.bru | 53 ++ .../Field Rules/Validation Rules/folder.bru | 4 + bruno/form-service/Field Rules/folder.bru | 4 + .../Form Field/Create a new form field.bru | 83 +++ .../Form Field/Create boolean form field.bru | 43 ++ .../Form Field/Create number form field.bru | 44 ++ .../Form Field/Create text form field.bru | 43 ++ .../Delete form field.bru} | 15 +- .../Get form field by id.bru} | 17 +- .../Get form fields by template id.bru | 25 + .../Form Field/Update form field.bru | 69 +++ .../{Question => Form Field}/folder.bru | 4 +- .../Form Submission/Generic search.bru | 4 + bruno/form-service/Form Submission/folder.bru | 2 +- .../Create form template approval.bru | 51 ++ .../Delete form template approval.bru | 24 + .../Get form template approval by id.bru | 30 ++ .../Search form template approvals.bru | 53 ++ .../Update form template approval.bru | 42 ++ .../Form section/Create new form section.bru | 5 +- .../Form section/Generic search.bru | 4 + bruno/form-service/Form section/folder.bru | 2 +- .../Form template/Generic search.bru | 4 + .../Form template/create a form template.bru | 2 +- bruno/form-service/Form template/folder.bru | 2 +- bruno/form-service/Health Check Request.bru | 2 +- .../Create input unit list.bru | 77 +++ .../Delete input unit list.bru | 24 + .../Get input unit list by id.bru | 32 ++ .../Search input unit lists.bru | 56 ++ .../Update input unit list.bru | 77 +++ .../Question Response/Generic search.bru | 4 + .../form-service/Question Response/folder.bru | 2 +- .../Question/Create a new question.bru | 384 -------------- .../form-service/Question/Generic search.bru | 28 - .../Question/Get Question By Template Id.bru | 11 - .../Question/Update a question record.bru | 74 --- .../Create template folder.bru | 52 ++ .../Delete template folder.bru | 24 + .../Get template folder by id.bru | 31 ++ .../Search template folders.bru | 55 ++ .../Update template folder.bru | 45 ++ .../User Login Session/folder.bru | 2 +- bruno/form-service/User/Generic search.bru | 4 + bruno/form-service/User/folder.bru | 2 +- .../environments/forms-service.bru | 1 + package.json | 4 +- .../calculation.logic.controller.ts | 84 +++ .../calculation.logic.router.ts | 19 + .../calculation.logic.validator.ts | 89 ++++ .../skip.logic/skip.logic.controller.ts | 83 +++ .../skip.logic/skip.logic.router.ts | 19 + .../skip.logic/skip.logic.validator.ts | 85 +++ .../validation.logic.controller.ts | 83 +++ .../validation.logic.router.ts | 19 + .../validation.logic.validator.ts | 85 +++ .../composition.operation.controller.ts | 84 +++ .../composition.operation.router.ts | 19 + .../composition.operation.validator.ts | 87 ++++ ...unction.expression.operation.controller.ts | 84 +++ .../function.expression.operation.router.ts | 19 + ...function.expression.operation.validator.ts | 89 ++++ .../iterate.operation.controller.ts | 83 +++ .../iterate.operation.router.ts | 19 + .../iterate.operation.validator.ts | 90 ++++ .../logical.operation.controller.ts | 84 +++ .../logical.operation.router.ts | 19 + .../logical.operation.validator.ts | 84 +++ .../mathematical.operation.controller.ts | 83 +++ .../mathematical.operation.router.ts | 19 + .../mathematical.operation.validator.ts | 88 ++++ .../calculation.rule.controller.ts | 83 +++ .../calculation.rule.router.ts | 19 + .../calculation.rule.validator.ts | 100 ++++ .../skip.rule/skip.rule.controller.ts | 83 +++ .../field.rules/skip.rule/skip.rule.router.ts | 19 + .../skip.rule/skip.rule.validator.ts | 100 ++++ .../validation.rule.controller.ts | 83 +++ .../validation.rule/validation.rule.router.ts | 19 + .../validation.rule.validator.ts | 104 ++++ src/api/form.field/form.field.auth.ts | 1 + .../form.field.controller.ts} | 47 +- .../form.field.router.ts} | 8 +- .../form.field.validator.ts} | 41 +- src/api/form.submission/form.validator.ts | 4 +- src/api/form.template/form.template.auth.ts | 0 .../question.response.controller.ts | 2 +- .../question.response.validator.ts | 2 +- src/api/question/question.auth.ts | 0 src/app.ts | 3 + .../calculation.logic.repo.interface.ts | 16 + .../field.logic/logic.repo.interface.ts | 45 ++ .../skip.logic/skip.logic.repo.interface.ts | 16 + .../validation.logic.repo.interface.ts | 16 + .../composition.operation.repo.interface.ts | 18 + ...ion.expression.operation.repo.interface.ts | 16 + .../iterate.operation.repo.interface.ts | 16 + .../logical.operation.repo.interface.ts | 16 + .../mathematical.operation.repo.interface.ts | 16 + .../calculation.rule.repo.interface.ts | 11 + .../skip.rule/skip.rule.repo.interface.ts | 11 + .../validation.rule.repo.interface.ts | 11 + .../form.field/form.field.repo.interface.ts | 18 + .../question/question.repo.interface.ts | 18 - .../sql/typeorm/database.connector.typeorm.ts | 488 ++++-------------- .../mappers/calculation.logic.mapper.ts | 47 ++ .../mappers/calculation.rule.mapper.ts | 61 +++ .../mappers/composition.operation.mapper.ts | 41 ++ .../sql/typeorm/mappers/form.field.mapper.ts | 90 ++-- .../function.expression.operation.mapper.ts | 43 ++ .../mappers/iterate.operation.mapper.ts | 50 ++ .../sql/typeorm/mappers/logic.mapper.ts | 152 ------ .../mappers/logical.operation.mapper.ts | 41 ++ .../mappers/mathematical.operation.mapper.ts | 43 ++ .../sql/typeorm/mappers/operation.mapper.ts | 165 ------ .../sql/typeorm/mappers/question.mapper.ts | 125 ----- .../sql/typeorm/mappers/rule.mapper.ts | 187 ------- .../sql/typeorm/mappers/skip.logic.mapper.ts | 48 ++ .../sql/typeorm/mappers/skip.rule.mapper.ts | 55 ++ .../mappers/validation.logic.mapper.ts | 45 ++ .../typeorm/mappers/validation.rule.mapper.ts | 57 ++ .../typeorm/models/form.field/field.types.ts | 55 -- .../models/form.field/form.field.model.ts | 298 +++-------- .../models/form.section/form.section.model.ts | 10 +- .../form.submission/form.submission.model.ts | 10 +- .../form.template.approval.model.ts | 2 +- .../form.template/form.template.model.ts | 25 +- .../typeorm/models/logic/base.logic.model.ts | 4 +- .../models/logic/calculation.logic.model.ts | 11 +- .../models/logic/legacy.logic.model.ts | 38 -- .../typeorm/models/logic/skip.logic.model.ts | 8 +- .../models/logic/validation.logic.model.ts | 10 +- .../models/operation/base.operation.model.ts | 5 +- .../operation/composition.operation.model.ts | 9 +- .../function.expression.operation.model.ts | 11 +- .../operation/iterate.operation.model.ts | 26 +- .../operation/logical.operation.model.ts | 8 +- .../operation/mathematical.operation.model.ts | 12 +- .../question.response.model.ts | 16 +- .../typeorm/models/question/question.model.ts | 92 ---- .../typeorm/models/rule/base.rule.model.ts | 6 + .../models/rule/calculation.rule.model.ts | 15 +- .../typeorm/models/rule/legacy.rule.model.ts | 30 -- .../typeorm/models/rule/skip.rule.model.ts | 7 +- .../models/rule/validation.rule.model.ts | 9 +- .../field.logic.calculation.repo.ts | 158 ++++++ .../field.logic/field.logic.skip.repo.ts | 162 ++++++ .../field.logic.validation.repo.ts | 155 ++++++ .../field.operations.composition.repo.ts | 162 ++++++ ...eld.operations.function.expression.repo.ts | 162 ++++++ .../field.operations.iterate.repo.ts | 165 ++++++ .../field.operations.logical.repo.ts | 162 ++++++ .../field.operations.mathematical.repo.ts | 166 ++++++ .../field.rules.calculation.repo.ts | 160 ++++++ .../field.rules/field.rules.skip.repo.ts | 161 ++++++ .../field.rules.validation.repo.ts | 171 ++++++ .../form.field/form.field.repo.ts | 280 ++++++++++ .../form.section/form.section.repo.ts | 102 ++-- .../form.submission/form.submission.repo.ts | 414 +++++++-------- .../form.template/form.template.repo.ts | 99 ++-- .../question.response.repo.ts | 16 +- .../repositories/question/question.repo.ts | 280 ---------- .../template.folder/template.folder.repo.ts | 1 + src/database/sql/typeorm/typeorm.injector.ts | 27 +- .../forms/calculation.logic.domain.types.ts | 25 + .../forms/calculation.rule.domain.types.ts | 48 ++ .../composition.operation.domain.types.ts | 24 + src/domain.types/forms/field.enums.ts | 27 + .../forms/form.field.domain.types.ts | 221 +++----- .../forms/form.submission.domain.types.ts | 15 +- .../forms/form.submission.enums.ts | 8 + .../forms/form.template.domain.types.ts | 2 +- src/domain.types/forms/form.template.enums.ts | 14 + ...ction.expression.operation.domain.types.ts | 20 + .../forms/iterate.operation.domain.types.ts | 25 + src/domain.types/forms/logic.domain.types.ts | 49 +- .../forms/logic.enums.ts} | 2 +- .../forms/logical.operation.domain.types.ts | 24 + .../mathematical.operation.domain.types.ts | 24 + .../forms/operation.domain.types.ts | 95 ++-- .../forms/operation.enums.ts} | 2 +- .../forms/response.domain.types.ts | 2 +- src/domain.types/forms/rule.domain.types.ts | 24 - .../forms/skip.logic.domain.types.ts | 22 + .../forms/skip.rule.domain.types.ts | 42 ++ .../forms/validation.logic.domain.types.ts | 22 + .../forms/validation.rule.domain.types.ts | 45 ++ .../miscellaneous/base.search.types.ts | 2 +- .../field.logic/calculation.logic.service.ts | 34 ++ .../field.logic/skip.logic.service.ts | 34 ++ .../field.logic/validation.logic.service.ts | 34 ++ .../composition.operation.service.ts | 35 ++ .../function.expression.operation.service.ts | 34 ++ .../iterate.operation.service.ts | 34 ++ .../logical.operation.service.ts | 34 ++ .../mathematical.operation.service.ts | 34 ++ .../field.rules/calculation.rule.service.ts | 32 ++ src/services/field.rules/skip.rule.service.ts | 32 ++ .../field.rules/validation.rule.service.ts | 32 ++ .../form.field.service.ts} | 48 +- .../form.section/form.section.service.ts | 24 +- .../form.submission.service.ts | 86 +-- .../form.template/form.template.service.ts | 58 +-- src/startup/router.ts | 32 +- 275 files changed, 10316 insertions(+), 3197 deletions(-) create mode 100644 bruno/form-service/Favorite Template/Create favorite template.bru create mode 100644 bruno/form-service/Favorite Template/Delete favorite template.bru create mode 100644 bruno/form-service/Favorite Template/Get favorite template by id.bru create mode 100644 bruno/form-service/Favorite Template/Search favorite templates.bru create mode 100644 bruno/form-service/Favorite Template/Update favorite template.bru create mode 100644 bruno/form-service/Field Logic/Calculation Logic/Create calculation logic.bru create mode 100644 bruno/form-service/Field Logic/Calculation Logic/Delete calculation logic.bru create mode 100644 bruno/form-service/Field Logic/Calculation Logic/Get calculation logic by id.bru create mode 100644 bruno/form-service/Field Logic/Calculation Logic/Search calculation logic.bru create mode 100644 bruno/form-service/Field Logic/Calculation Logic/Update calculation logic.bru create mode 100644 bruno/form-service/Field Logic/Calculation Logic/folder.bru create mode 100644 bruno/form-service/Field Logic/Skip Logic/Create skip logic.bru create mode 100644 bruno/form-service/Field Logic/Skip Logic/Delete skip logic.bru create mode 100644 bruno/form-service/Field Logic/Skip Logic/Get skip logic by id.bru create mode 100644 bruno/form-service/Field Logic/Skip Logic/Search skip logic.bru create mode 100644 bruno/form-service/Field Logic/Skip Logic/Update skip logic.bru create mode 100644 bruno/form-service/Field Logic/Skip Logic/folder.bru create mode 100644 bruno/form-service/Field Logic/Validation Logic/Create validation logic.bru create mode 100644 bruno/form-service/Field Logic/Validation Logic/Delete validation logic.bru create mode 100644 bruno/form-service/Field Logic/Validation Logic/Get validation logic by id.bru create mode 100644 bruno/form-service/Field Logic/Validation Logic/Search validation logic.bru create mode 100644 bruno/form-service/Field Logic/Validation Logic/Update validation logic.bru create mode 100644 bruno/form-service/Field Logic/Validation Logic/folder.bru create mode 100644 bruno/form-service/Field Logic/folder.bru create mode 100644 bruno/form-service/Field Operation/Composition Operations/Create composition operation.bru create mode 100644 bruno/form-service/Field Operation/Composition Operations/Delete composition operation.bru create mode 100644 bruno/form-service/Field Operation/Composition Operations/Get composition operation by id.bru create mode 100644 bruno/form-service/Field Operation/Composition Operations/Search composition operations.bru create mode 100644 bruno/form-service/Field Operation/Composition Operations/Update composition operation.bru create mode 100644 bruno/form-service/Field Operation/Composition Operations/folder.bru create mode 100644 bruno/form-service/Field Operation/Function Expression Operations/Create function expression operation.bru create mode 100644 bruno/form-service/Field Operation/Function Expression Operations/Delete function expression operation.bru create mode 100644 bruno/form-service/Field Operation/Function Expression Operations/Get function expression operation by id.bru create mode 100644 bruno/form-service/Field Operation/Function Expression Operations/Search function expression operations.bru create mode 100644 bruno/form-service/Field Operation/Function Expression Operations/Update function expression operation.bru create mode 100644 bruno/form-service/Field Operation/Function Expression Operations/folder.bru create mode 100644 bruno/form-service/Field Operation/Iterate Operations/Create iterate operation.bru create mode 100644 bruno/form-service/Field Operation/Iterate Operations/Delete iterate operation.bru create mode 100644 bruno/form-service/Field Operation/Iterate Operations/Get iterate operation by id.bru create mode 100644 bruno/form-service/Field Operation/Iterate Operations/Search iterate operations.bru create mode 100644 bruno/form-service/Field Operation/Iterate Operations/Update iterate operation.bru create mode 100644 bruno/form-service/Field Operation/Iterate Operations/folder.bru create mode 100644 bruno/form-service/Field Operation/Logical Operations/Create logical operation.bru create mode 100644 bruno/form-service/Field Operation/Logical Operations/Delete logical operation.bru create mode 100644 bruno/form-service/Field Operation/Logical Operations/Get logical operation by id.bru create mode 100644 bruno/form-service/Field Operation/Logical Operations/Search logical operations.bru create mode 100644 bruno/form-service/Field Operation/Logical Operations/Update logical operation.bru create mode 100644 bruno/form-service/Field Operation/Logical Operations/folder.bru create mode 100644 bruno/form-service/Field Operation/Mathematical Operations/Create mathematical operation.bru create mode 100644 bruno/form-service/Field Operation/Mathematical Operations/Delete mathematical operation.bru create mode 100644 bruno/form-service/Field Operation/Mathematical Operations/Get mathematical operation by id.bru create mode 100644 bruno/form-service/Field Operation/Mathematical Operations/Search mathematical operations.bru create mode 100644 bruno/form-service/Field Operation/Mathematical Operations/Update mathematical operation.bru create mode 100644 bruno/form-service/Field Operation/Mathematical Operations/folder.bru create mode 100644 bruno/form-service/Field Operation/folder.bru create mode 100644 bruno/form-service/Field Rules/Calculation Rules/Create calculation rule.bru create mode 100644 bruno/form-service/Field Rules/Calculation Rules/Delete calculation rule.bru create mode 100644 bruno/form-service/Field Rules/Calculation Rules/Get calculation rule by id.bru create mode 100644 bruno/form-service/Field Rules/Calculation Rules/Search calculation rules.bru create mode 100644 bruno/form-service/Field Rules/Calculation Rules/Update calculation rule.bru create mode 100644 bruno/form-service/Field Rules/Calculation Rules/folder.bru create mode 100644 bruno/form-service/Field Rules/Skip Rules/Create skip rule.bru create mode 100644 bruno/form-service/Field Rules/Skip Rules/Delete skip rule.bru create mode 100644 bruno/form-service/Field Rules/Skip Rules/Get skip rule by id.bru create mode 100644 bruno/form-service/Field Rules/Skip Rules/Search skip rules.bru create mode 100644 bruno/form-service/Field Rules/Skip Rules/Update skip rule.bru create mode 100644 bruno/form-service/Field Rules/Skip Rules/folder.bru create mode 100644 bruno/form-service/Field Rules/Validation Rules/Create validation rule.bru create mode 100644 bruno/form-service/Field Rules/Validation Rules/Delete validation rule.bru create mode 100644 bruno/form-service/Field Rules/Validation Rules/Get validation rule by id.bru create mode 100644 bruno/form-service/Field Rules/Validation Rules/Search validation rules.bru create mode 100644 bruno/form-service/Field Rules/Validation Rules/Update validation rule.bru create mode 100644 bruno/form-service/Field Rules/Validation Rules/folder.bru create mode 100644 bruno/form-service/Field Rules/folder.bru create mode 100644 bruno/form-service/Form Field/Create a new form field.bru create mode 100644 bruno/form-service/Form Field/Create boolean form field.bru create mode 100644 bruno/form-service/Form Field/Create number form field.bru create mode 100644 bruno/form-service/Form Field/Create text form field.bru rename bruno/form-service/{Question/Delete a question record.bru => Form Field/Delete form field.bru} (51%) rename bruno/form-service/{Question/Get question by id.bru => Form Field/Get form field by id.bru} (71%) create mode 100644 bruno/form-service/Form Field/Get form fields by template id.bru create mode 100644 bruno/form-service/Form Field/Update form field.bru rename bruno/form-service/{Question => Form Field}/folder.bru (53%) create mode 100644 bruno/form-service/Form Template Approval/Create form template approval.bru create mode 100644 bruno/form-service/Form Template Approval/Delete form template approval.bru create mode 100644 bruno/form-service/Form Template Approval/Get form template approval by id.bru create mode 100644 bruno/form-service/Form Template Approval/Search form template approvals.bru create mode 100644 bruno/form-service/Form Template Approval/Update form template approval.bru create mode 100644 bruno/form-service/Input Unit List/Create input unit list.bru create mode 100644 bruno/form-service/Input Unit List/Delete input unit list.bru create mode 100644 bruno/form-service/Input Unit List/Get input unit list by id.bru create mode 100644 bruno/form-service/Input Unit List/Search input unit lists.bru create mode 100644 bruno/form-service/Input Unit List/Update input unit list.bru delete mode 100644 bruno/form-service/Question/Create a new question.bru delete mode 100644 bruno/form-service/Question/Generic search.bru delete mode 100644 bruno/form-service/Question/Get Question By Template Id.bru delete mode 100644 bruno/form-service/Question/Update a question record.bru create mode 100644 bruno/form-service/Template Folder/Create template folder.bru create mode 100644 bruno/form-service/Template Folder/Delete template folder.bru create mode 100644 bruno/form-service/Template Folder/Get template folder by id.bru create mode 100644 bruno/form-service/Template Folder/Search template folders.bru create mode 100644 bruno/form-service/Template Folder/Update template folder.bru create mode 100644 src/api/field.logic/calculation.logic/calculation.logic.controller.ts create mode 100644 src/api/field.logic/calculation.logic/calculation.logic.router.ts create mode 100644 src/api/field.logic/calculation.logic/calculation.logic.validator.ts create mode 100644 src/api/field.logic/skip.logic/skip.logic.controller.ts create mode 100644 src/api/field.logic/skip.logic/skip.logic.router.ts create mode 100644 src/api/field.logic/skip.logic/skip.logic.validator.ts create mode 100644 src/api/field.logic/validation.logic/validation.logic.controller.ts create mode 100644 src/api/field.logic/validation.logic/validation.logic.router.ts create mode 100644 src/api/field.logic/validation.logic/validation.logic.validator.ts create mode 100644 src/api/field.operations/composition.operation/composition.operation.controller.ts create mode 100644 src/api/field.operations/composition.operation/composition.operation.router.ts create mode 100644 src/api/field.operations/composition.operation/composition.operation.validator.ts create mode 100644 src/api/field.operations/function.expression.operation/function.expression.operation.controller.ts create mode 100644 src/api/field.operations/function.expression.operation/function.expression.operation.router.ts create mode 100644 src/api/field.operations/function.expression.operation/function.expression.operation.validator.ts create mode 100644 src/api/field.operations/iterate.operation/iterate.operation.controller.ts create mode 100644 src/api/field.operations/iterate.operation/iterate.operation.router.ts create mode 100644 src/api/field.operations/iterate.operation/iterate.operation.validator.ts create mode 100644 src/api/field.operations/logical.operation/logical.operation.controller.ts create mode 100644 src/api/field.operations/logical.operation/logical.operation.router.ts create mode 100644 src/api/field.operations/logical.operation/logical.operation.validator.ts create mode 100644 src/api/field.operations/mathematical.operation/mathematical.operation.controller.ts create mode 100644 src/api/field.operations/mathematical.operation/mathematical.operation.router.ts create mode 100644 src/api/field.operations/mathematical.operation/mathematical.operation.validator.ts create mode 100644 src/api/field.rules/calculation.rule/calculation.rule.controller.ts create mode 100644 src/api/field.rules/calculation.rule/calculation.rule.router.ts create mode 100644 src/api/field.rules/calculation.rule/calculation.rule.validator.ts create mode 100644 src/api/field.rules/skip.rule/skip.rule.controller.ts create mode 100644 src/api/field.rules/skip.rule/skip.rule.router.ts create mode 100644 src/api/field.rules/skip.rule/skip.rule.validator.ts create mode 100644 src/api/field.rules/validation.rule/validation.rule.controller.ts create mode 100644 src/api/field.rules/validation.rule/validation.rule.router.ts create mode 100644 src/api/field.rules/validation.rule/validation.rule.validator.ts create mode 100644 src/api/form.field/form.field.auth.ts rename src/api/{question/question.controller.ts => form.field/form.field.controller.ts} (71%) rename src/api/{question/question.router.ts => form.field/form.field.router.ts} (77%) rename src/api/{question/question.validator.ts => form.field/form.field.validator.ts} (91%) delete mode 100644 src/api/form.template/form.template.auth.ts delete mode 100644 src/api/question/question.auth.ts create mode 100644 src/database/repository.interfaces/field.logic/calculation.logic/calculation.logic.repo.interface.ts create mode 100644 src/database/repository.interfaces/field.logic/logic.repo.interface.ts create mode 100644 src/database/repository.interfaces/field.logic/skip.logic/skip.logic.repo.interface.ts create mode 100644 src/database/repository.interfaces/field.logic/validation.logic/validation.logic.repo.interface.ts create mode 100644 src/database/repository.interfaces/field.operations/composition.operation/composition.operation.repo.interface.ts create mode 100644 src/database/repository.interfaces/field.operations/function.expression.operation/function.expression.operation.repo.interface.ts create mode 100644 src/database/repository.interfaces/field.operations/iterate.operation/iterate.operation.repo.interface.ts create mode 100644 src/database/repository.interfaces/field.operations/logical.operation/logical.operation.repo.interface.ts create mode 100644 src/database/repository.interfaces/field.operations/mathematical.operation/mathematical.operation.repo.interface.ts create mode 100644 src/database/repository.interfaces/field.rules/calculation.rule/calculation.rule.repo.interface.ts create mode 100644 src/database/repository.interfaces/field.rules/skip.rule/skip.rule.repo.interface.ts create mode 100644 src/database/repository.interfaces/field.rules/validation.rule/validation.rule.repo.interface.ts create mode 100644 src/database/repository.interfaces/form.field/form.field.repo.interface.ts delete mode 100644 src/database/repository.interfaces/question/question.repo.interface.ts create mode 100644 src/database/sql/typeorm/mappers/calculation.logic.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/calculation.rule.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/composition.operation.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/function.expression.operation.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/iterate.operation.mapper.ts delete mode 100644 src/database/sql/typeorm/mappers/logic.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/logical.operation.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/mathematical.operation.mapper.ts delete mode 100644 src/database/sql/typeorm/mappers/operation.mapper.ts delete mode 100644 src/database/sql/typeorm/mappers/question.mapper.ts delete mode 100644 src/database/sql/typeorm/mappers/rule.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/skip.logic.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/skip.rule.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/validation.logic.mapper.ts create mode 100644 src/database/sql/typeorm/mappers/validation.rule.mapper.ts delete mode 100644 src/database/sql/typeorm/models/form.field/field.types.ts delete mode 100644 src/database/sql/typeorm/models/logic/legacy.logic.model.ts delete mode 100644 src/database/sql/typeorm/models/question/question.model.ts delete mode 100644 src/database/sql/typeorm/models/rule/legacy.rule.model.ts create mode 100644 src/database/sql/typeorm/repositories/field.logic/field.logic.calculation.repo.ts create mode 100644 src/database/sql/typeorm/repositories/field.logic/field.logic.skip.repo.ts create mode 100644 src/database/sql/typeorm/repositories/field.logic/field.logic.validation.repo.ts create mode 100644 src/database/sql/typeorm/repositories/field.operations/field.operations.composition.repo.ts create mode 100644 src/database/sql/typeorm/repositories/field.operations/field.operations.function.expression.repo.ts create mode 100644 src/database/sql/typeorm/repositories/field.operations/field.operations.iterate.repo.ts create mode 100644 src/database/sql/typeorm/repositories/field.operations/field.operations.logical.repo.ts create mode 100644 src/database/sql/typeorm/repositories/field.operations/field.operations.mathematical.repo.ts create mode 100644 src/database/sql/typeorm/repositories/field.rules/field.rules.calculation.repo.ts create mode 100644 src/database/sql/typeorm/repositories/field.rules/field.rules.skip.repo.ts create mode 100644 src/database/sql/typeorm/repositories/field.rules/field.rules.validation.repo.ts create mode 100644 src/database/sql/typeorm/repositories/form.field/form.field.repo.ts delete mode 100644 src/database/sql/typeorm/repositories/question/question.repo.ts create mode 100644 src/domain.types/forms/calculation.logic.domain.types.ts create mode 100644 src/domain.types/forms/calculation.rule.domain.types.ts create mode 100644 src/domain.types/forms/composition.operation.domain.types.ts create mode 100644 src/domain.types/forms/field.enums.ts create mode 100644 src/domain.types/forms/form.submission.enums.ts create mode 100644 src/domain.types/forms/form.template.enums.ts create mode 100644 src/domain.types/forms/function.expression.operation.domain.types.ts create mode 100644 src/domain.types/forms/iterate.operation.domain.types.ts rename src/{database/sql/typeorm/models/logic/logic.types.ts => domain.types/forms/logic.enums.ts} (87%) create mode 100644 src/domain.types/forms/logical.operation.domain.types.ts create mode 100644 src/domain.types/forms/mathematical.operation.domain.types.ts rename src/{database/sql/typeorm/models/operation/operation.types.ts => domain.types/forms/operation.enums.ts} (98%) create mode 100644 src/domain.types/forms/skip.logic.domain.types.ts create mode 100644 src/domain.types/forms/skip.rule.domain.types.ts create mode 100644 src/domain.types/forms/validation.logic.domain.types.ts create mode 100644 src/domain.types/forms/validation.rule.domain.types.ts create mode 100644 src/services/field.logic/calculation.logic.service.ts create mode 100644 src/services/field.logic/skip.logic.service.ts create mode 100644 src/services/field.logic/validation.logic.service.ts create mode 100644 src/services/field.operations/composition.operation.service.ts create mode 100644 src/services/field.operations/function.expression.operation.service.ts create mode 100644 src/services/field.operations/iterate.operation.service.ts create mode 100644 src/services/field.operations/logical.operation.service.ts create mode 100644 src/services/field.operations/mathematical.operation.service.ts create mode 100644 src/services/field.rules/calculation.rule.service.ts create mode 100644 src/services/field.rules/skip.rule.service.ts create mode 100644 src/services/field.rules/validation.rule.service.ts rename src/services/{question/question.service.ts => form.field/form.field.service.ts} (71%) diff --git a/bruno/form-service/Favorite Template/Create favorite template.bru b/bruno/form-service/Favorite Template/Create favorite template.bru new file mode 100644 index 0000000..030bab7 --- /dev/null +++ b/bruno/form-service/Favorite Template/Create favorite template.bru @@ -0,0 +1,47 @@ +meta { + name: Create Favorite Template + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/favorite-templates + body: json + auth: none +} + +body:json { + { + "UserId": "{{USER_ID}}", + "TemplateId": "{{TEMPLATE_ID}}" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("FAVORITE_TEMPLATE_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Favorite Template is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('UserId'); + expect(jsonRes.Data.UserId).equals('{{USER_ID}}'); + expect(jsonRes.Data).to.have.property('FormTemplateId'); + expect(jsonRes.Data.FormTemplateId).equals('{{FORM_TEMPLATE_ID}}'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Favorite Template/Delete favorite template.bru b/bruno/form-service/Favorite Template/Delete favorite template.bru new file mode 100644 index 0000000..9d6d230 --- /dev/null +++ b/bruno/form-service/Favorite Template/Delete favorite template.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Favorite Template + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/favorite-templates/{{FAVORITE_TEMPLATE_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Favorite Template is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Favorite Template/Get favorite template by id.bru b/bruno/form-service/Favorite Template/Get favorite template by id.bru new file mode 100644 index 0000000..61ddd99 --- /dev/null +++ b/bruno/form-service/Favorite Template/Get favorite template by id.bru @@ -0,0 +1,29 @@ +meta { + name: Get Favorite Template by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/favorite-templates/{{FAVORITE_TEMPLATE_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Favorite Template has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{FAVORITE_TEMPLATE_ID}}'); + expect(jsonRes.Data).to.have.property('UserId'); + expect(jsonRes.Data).to.have.property('FormTemplateId'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Favorite Template/Search favorite templates.bru b/bruno/form-service/Favorite Template/Search favorite templates.bru new file mode 100644 index 0000000..9d4f139 --- /dev/null +++ b/bruno/form-service/Favorite Template/Search favorite templates.bru @@ -0,0 +1,54 @@ +meta { + name: Search Favorite Templates + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/favorite-templates/search?pageIndex=0&itemsPerPage=10&orderBy=CreatedAt&userId={{USER_ID}} + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + userId: {{USER_ID}} + ~Order: DESC + ~IsActive: true +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Favorite Templates search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Favorite Templates have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('UserId'); + expect(item).to.have.property('FormTemplateId'); + expect(item).to.have.property('IsActive'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Favorite Template/Update favorite template.bru b/bruno/form-service/Favorite Template/Update favorite template.bru new file mode 100644 index 0000000..84f2da2 --- /dev/null +++ b/bruno/form-service/Favorite Template/Update favorite template.bru @@ -0,0 +1,39 @@ +meta { + name: Update Favorite Template + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/favorite-templates/{{FAVORITE_TEMPLATE_ID}} + body: json + auth: none +} + +body:json { + { + "IsActive": false + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Favorite Template is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{FAVORITE_TEMPLATE_ID}}'); + expect(jsonRes.Data).to.have.property('UserId'); + expect(jsonRes.Data.UserId).equals('{{USER_ID}}'); + expect(jsonRes.Data).to.have.property('FormTemplateId'); + expect(jsonRes.Data.FormTemplateId).equals('{{FORM_TEMPLATE_ID}}'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(false); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/Calculation Logic/Create calculation logic.bru b/bruno/form-service/Field Logic/Calculation Logic/Create calculation logic.bru new file mode 100644 index 0000000..f3216e6 --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/Create calculation logic.bru @@ -0,0 +1,54 @@ +meta { + name: Create Calculation Logic + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-calculation-logic + body: json + auth: none +} + +body:json { + { + "Type": "Calculation", + "FieldId": "{{FORM_FIELD_ID}}", + "Enabled": true, + "DefaultSkip": false, + "FallbackValue": "0" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("CALCULATION_LOGIC_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Logic is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Calculation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data.FieldId).equals('5b1410d5-47dd-424b-ac85-336e8d2a4d36'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(true); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data.DefaultSkip).equals(false); + expect(jsonRes.Data).to.have.property('FallbackValue'); + expect(jsonRes.Data.FallbackValue).equals('0'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Calculation Logic/Delete calculation logic.bru b/bruno/form-service/Field Logic/Calculation Logic/Delete calculation logic.bru new file mode 100644 index 0000000..d831d92 --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/Delete calculation logic.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Calculation Logic + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-calculation-logic/{{CALCULATION_LOGIC_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Logic is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Logic/Calculation Logic/Get calculation logic by id.bru b/bruno/form-service/Field Logic/Calculation Logic/Get calculation logic by id.bru new file mode 100644 index 0000000..0c16460 --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/Get calculation logic by id.bru @@ -0,0 +1,32 @@ +meta { + name: Get Calculation Logic by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-calculation-logic/{{CALCULATION_LOGIC_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Logic has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Calculation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data).to.have.property('FallbackValue'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Calculation Logic/Search calculation logic.bru b/bruno/form-service/Field Logic/Calculation Logic/Search calculation logic.bru new file mode 100644 index 0000000..3f213a0 --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/Search calculation logic.bru @@ -0,0 +1,56 @@ +meta { + name: Search Calculation Logic + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-calculation-logic/search?enabled=true&type=Calculation&PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +params:query { + enabled: true + type: Calculation + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain calculation logic", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Type'); + expect(firstItem.Type).equals('Calculation'); + expect(firstItem).to.have.property('FieldId'); + expect(firstItem).to.have.property('Enabled'); + expect(firstItem).to.have.property('DefaultSkip'); + expect(firstItem).to.have.property('FallbackValue'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Logic/Calculation Logic/Update calculation logic.bru b/bruno/form-service/Field Logic/Calculation Logic/Update calculation logic.bru new file mode 100644 index 0000000..d476876 --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/Update calculation logic.bru @@ -0,0 +1,46 @@ +meta { + name: Update Calculation Logic + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-calculation-logic/{{CALCULATION_LOGIC_ID}} + body: json + auth: none +} + +body:json { + { + "Type": "Calculation", + "FieldId": "{{FORM_FIELD_ID}}", + "Enabled": false, + "DefaultSkip": true, + "FallbackValue": "100" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Logic is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Calculation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data.FieldId).equals('5b1410d5-47dd-424b-ac85-336e8d2a4d36'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(false); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data.DefaultSkip).equals(true); + expect(jsonRes.Data).to.have.property('FallbackValue'); + expect(jsonRes.Data.FallbackValue).equals('100'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Calculation Logic/folder.bru b/bruno/form-service/Field Logic/Calculation Logic/folder.bru new file mode 100644 index 0000000..f4cdd67 --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Calculation Logic + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/Skip Logic/Create skip logic.bru b/bruno/form-service/Field Logic/Skip Logic/Create skip logic.bru new file mode 100644 index 0000000..d333184 --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/Create skip logic.bru @@ -0,0 +1,51 @@ +meta { + name: Create Skip Logic + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-skip-logic + body: json + auth: none +} + +body:json { + { + "Type": "Skip", + "FieldId": "{{FORM_FIELD_ID}}", + "Enabled": true, + "DefaultSkip": false + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("SKIP_LOGIC_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Logic is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Skip'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data.FieldId).equals('5b1410d5-47dd-424b-ac85-336e8d2a4d36'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(true); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data.DefaultSkip).equals(false); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Skip Logic/Delete skip logic.bru b/bruno/form-service/Field Logic/Skip Logic/Delete skip logic.bru new file mode 100644 index 0000000..6437c0b --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/Delete skip logic.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Skip Logic + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-skip-logic/{{SKIP_LOGIC_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Logic is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Logic/Skip Logic/Get skip logic by id.bru b/bruno/form-service/Field Logic/Skip Logic/Get skip logic by id.bru new file mode 100644 index 0000000..a3cee6d --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/Get skip logic by id.bru @@ -0,0 +1,30 @@ +meta { + name: Get Skip Logic by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-skip-logic/{{SKIP_LOGIC_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Logic has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Skip'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/Skip Logic/Search skip logic.bru b/bruno/form-service/Field Logic/Skip Logic/Search skip logic.bru new file mode 100644 index 0000000..4598a3b --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/Search skip logic.bru @@ -0,0 +1,55 @@ +meta { + name: Search Skip Logic + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-skip-logic/search?type=Skip&enabled=true&PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +params:query { + type: Skip + enabled: true + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain skip logic", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Type'); + expect(firstItem.Type).equals('Skip'); + expect(firstItem).to.have.property('FieldId'); + expect(firstItem).to.have.property('Enabled'); + expect(firstItem).to.have.property('DefaultSkip'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Logic/Skip Logic/Update skip logic.bru b/bruno/form-service/Field Logic/Skip Logic/Update skip logic.bru new file mode 100644 index 0000000..69c1d35 --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/Update skip logic.bru @@ -0,0 +1,43 @@ +meta { + name: Update Skip Logic + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-skip-logic/{{SKIP_LOGIC_ID}} + body: json + auth: none +} + +body:json { + { + "Type": "Skip", + "FieldId": "5b1410d5-47dd-424b-ac85-336e8d2a4d36", + "Enabled": false, + "DefaultSkip": true + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Logic is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Skip'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data.FieldId).equals('5b1410d5-47dd-424b-ac85-336e8d2a4d36'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(false); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data.DefaultSkip).equals(true); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/Skip Logic/folder.bru b/bruno/form-service/Field Logic/Skip Logic/folder.bru new file mode 100644 index 0000000..1220479 --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Skip Logic + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/Validation Logic/Create validation logic.bru b/bruno/form-service/Field Logic/Validation Logic/Create validation logic.bru new file mode 100644 index 0000000..06668e5 --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/Create validation logic.bru @@ -0,0 +1,51 @@ +meta { + name: Create Validation Logic + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-validation-logic + body: json + auth: none +} + +body:json { + { + "Type": "Validation", + "FieldId": "{{FORM_FIELD_ID}}", + "Enabled": true, + "DefaultSkip": false + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("VALIDATION_LOGIC_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Logic is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Validation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data.FieldId).equals('5b1410d5-47dd-424b-ac85-336e8d2a4d36'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(true); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data.DefaultSkip).equals(false); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Validation Logic/Delete validation logic.bru b/bruno/form-service/Field Logic/Validation Logic/Delete validation logic.bru new file mode 100644 index 0000000..b7ac078 --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/Delete validation logic.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Validation Logic + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-validation-logic/{{VALIDATION_LOGIC_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Logic is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Logic/Validation Logic/Get validation logic by id.bru b/bruno/form-service/Field Logic/Validation Logic/Get validation logic by id.bru new file mode 100644 index 0000000..30fca05 --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/Get validation logic by id.bru @@ -0,0 +1,30 @@ +meta { + name: Get Validation Logic by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-validation-logic/{{VALIDATION_LOGIC_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Logic has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Validation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/Validation Logic/Search validation logic.bru b/bruno/form-service/Field Logic/Validation Logic/Search validation logic.bru new file mode 100644 index 0000000..7a569a2 --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/Search validation logic.bru @@ -0,0 +1,55 @@ +meta { + name: Search Validation Logic + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-validation-logic/search?type=Validation + body: none + auth: none +} + +params:query { + type: Validation + ~enabled: true + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain validation logic", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Type'); + expect(firstItem.Type).equals('Validation'); + expect(firstItem).to.have.property('FieldId'); + expect(firstItem).to.have.property('Enabled'); + expect(firstItem).to.have.property('DefaultSkip'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Logic/Validation Logic/Update validation logic.bru b/bruno/form-service/Field Logic/Validation Logic/Update validation logic.bru new file mode 100644 index 0000000..52bac89 --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/Update validation logic.bru @@ -0,0 +1,43 @@ +meta { + name: Update Validation Logic + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-validation-logic/{{VALIDATION_LOGIC_ID}} + body: json + auth: none +} + +body:json { + { + "Type": "Validation", + "FieldId": "5b1410d5-47dd-424b-ac85-336e8d2a4d36", + "Enabled": false, + "DefaultSkip": true + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Logic is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Validation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data.FieldId).equals('5b1410d5-47dd-424b-ac85-336e8d2a4d36'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(false); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data.DefaultSkip).equals(true); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/Validation Logic/folder.bru b/bruno/form-service/Field Logic/Validation Logic/folder.bru new file mode 100644 index 0000000..16b5331 --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Validation Logic + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/folder.bru b/bruno/form-service/Field Logic/folder.bru new file mode 100644 index 0000000..557147b --- /dev/null +++ b/bruno/form-service/Field Logic/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Field Logic + seq: 1 +} diff --git a/bruno/form-service/Field Operation/Composition Operations/Create composition operation.bru b/bruno/form-service/Field Operation/Composition Operations/Create composition operation.bru new file mode 100644 index 0000000..641b3ba --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/Create composition operation.bru @@ -0,0 +1,54 @@ +meta { + name: Create Composition Operation + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-composition-operations + body: json + auth: none +} + +body:json { + { + "Name": "Multi-Condition Check", + "Description": "Check multiple conditions using AND composition", + "Type": "Composition", + "Operator": "And", + "Operands": "[{\"id\": \"condition1\", \"type\": \"boolean\"}, {\"id\": \"condition2\", \"type\": \"boolean\"}]" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("COMPOSITION_OPERATION_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Composition Operation is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Multi-Condition Check'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Check multiple conditions using AND composition'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Composition'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('And'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data.Operands).equals('[{"id": "condition1", "type": "boolean"}, {"id": "condition2", "type": "boolean"}]'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Composition Operations/Delete composition operation.bru b/bruno/form-service/Field Operation/Composition Operations/Delete composition operation.bru new file mode 100644 index 0000000..5ec9b0f --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/Delete composition operation.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Composition Operation + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-composition-operations/{{COMPOSITION_OPERATION_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Composition Operation is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Operation/Composition Operations/Get composition operation by id.bru b/bruno/form-service/Field Operation/Composition Operations/Get composition operation by id.bru new file mode 100644 index 0000000..757e65a --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/Get composition operation by id.bru @@ -0,0 +1,31 @@ +meta { + name: Get Composition Operation by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-composition-operations/{{COMPOSITION_OPERATION_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Composition Operation is retrieved with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Composition'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Composition Operations/Search composition operations.bru b/bruno/form-service/Field Operation/Composition Operations/Search composition operations.bru new file mode 100644 index 0000000..912ee23 --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/Search composition operations.bru @@ -0,0 +1,56 @@ +meta { + name: Search Composition Operations + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-composition-operations/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~name: Multi + ~operatorType: And +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Composition Operations search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Composition Operations have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('Type'); + expect(item.Type).equals('Composition'); + expect(item).to.have.property('Operator'); + expect(item).to.have.property('Operands'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Operation/Composition Operations/Update composition operation.bru b/bruno/form-service/Field Operation/Composition Operations/Update composition operation.bru new file mode 100644 index 0000000..d2ec263 --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/Update composition operation.bru @@ -0,0 +1,46 @@ +meta { + name: Update Composition Operation + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-composition-operations/{{COMPOSITION_OPERATION_ID}} + body: json + auth: none +} + +body:json { + { + "Name": "Updated Multi-Condition Check", + "Description": "Updated description for multi-condition check using OR composition", + "Type": "Composition", + "Operator": "Or", + "Operands": "[{\"id\": \"updatedCondition1\", \"type\": \"boolean\"}, {\"id\": \"updatedCondition2\", \"type\": \"boolean\"}]" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Composition Operation is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Multi-Condition Check'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated description for multi-condition check using OR composition'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Composition'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('Or'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data.Operands).equals('[{"id": "updatedCondition1", "type": "boolean"}, {"id": "updatedCondition2", "type": "boolean"}]'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Composition Operations/folder.bru b/bruno/form-service/Field Operation/Composition Operations/folder.bru new file mode 100644 index 0000000..6c5dcfa --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Composition Operations + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Function Expression Operations/Create function expression operation.bru b/bruno/form-service/Field Operation/Function Expression Operations/Create function expression operation.bru new file mode 100644 index 0000000..dcbfdc4 --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/Create function expression operation.bru @@ -0,0 +1,57 @@ +meta { + name: Create Function Expression Operation + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-function-expression-operations + body: json + auth: none +} + +body:json { + { + "Name": "Custom Calculation Function", + "Description": "Custom mathematical function for complex calculations", + "Type": "FunctionExpression", + "Expression": "amount * taxRate + (includeShipping ? shippingCost : 0)", + "Variables": "{\"amount\": \"number\", \"taxRate\": \"number\", \"includeShipping\": \"boolean\", \"shippingCost\": \"number\"}", + "ResultDataType": "number" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("FUNCTION_EXPRESSION_OPERATION_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Function Expression Operation is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Custom Calculation Function'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Custom mathematical function for complex calculations'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('FunctionExpression'); + expect(jsonRes.Data).to.have.property('Expression'); + expect(jsonRes.Data.Expression).equals('amount * taxRate + (includeShipping ? shippingCost : 0)'); + expect(jsonRes.Data).to.have.property('Variables'); + expect(jsonRes.Data.Variables).equals('{"amount": "number", "taxRate": "number", "includeShipping": "boolean", "shippingCost": "number"}'); + expect(jsonRes.Data).to.have.property('ResultDataType'); + expect(jsonRes.Data.ResultDataType).equals('number'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Function Expression Operations/Delete function expression operation.bru b/bruno/form-service/Field Operation/Function Expression Operations/Delete function expression operation.bru new file mode 100644 index 0000000..15d893a --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/Delete function expression operation.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Function Expression Operation + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-function-expression-operations/{{FUNCTION_EXPRESSION_OPERATION_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Function Expression Operation is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Operation/Function Expression Operations/Get function expression operation by id.bru b/bruno/form-service/Field Operation/Function Expression Operations/Get function expression operation by id.bru new file mode 100644 index 0000000..196c6e8 --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/Get function expression operation by id.bru @@ -0,0 +1,32 @@ +meta { + name: Get Function Expression Operation by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-function-expression-operations/{{FUNCTION_EXPRESSION_OPERATION_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Function Expression Operation is retrieved with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('FunctionExpression'); + expect(jsonRes.Data).to.have.property('Expression'); + expect(jsonRes.Data).to.have.property('Variables'); + expect(jsonRes.Data).to.have.property('ResultDataType'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Function Expression Operations/Search function expression operations.bru b/bruno/form-service/Field Operation/Function Expression Operations/Search function expression operations.bru new file mode 100644 index 0000000..f673b25 --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/Search function expression operations.bru @@ -0,0 +1,56 @@ +meta { + name: Search Function Expression Operations + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-function-expression-operations/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~name: Custom +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Function Expression Operations search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Function Expression Operations have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('Type'); + expect(item.Type).equals('FunctionExpression'); + expect(item).to.have.property('Expression'); + expect(item).to.have.property('Variables'); + expect(item).to.have.property('ResultDataType'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Operation/Function Expression Operations/Update function expression operation.bru b/bruno/form-service/Field Operation/Function Expression Operations/Update function expression operation.bru new file mode 100644 index 0000000..6f2c1cc --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/Update function expression operation.bru @@ -0,0 +1,49 @@ +meta { + name: Update Function Expression Operation + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-function-expression-operations/{{FUNCTION_EXPRESSION_OPERATION_ID}} + body: json + auth: none +} + +body:json { + { + "Name": "Updated Custom Calculation Function", + "Description": "Updated custom mathematical function for complex calculations", + "Type": "FunctionExpression", + "Expression": "amount * taxRate - (amount * discountRate) + (includeShipping ? shippingCost : 0)", + "Variables": "{\"amount\": \"number\", \"taxRate\": \"number\", \"discountRate\": \"number\", \"includeShipping\": \"boolean\", \"shippingCost\": \"number\"}", + "ResultDataType": "number" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Function Expression Operation is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Custom Calculation Function'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated custom mathematical function for complex calculations'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('FunctionExpression'); + expect(jsonRes.Data).to.have.property('Expression'); + expect(jsonRes.Data.Expression).equals('amount * taxRate - (amount * discountRate) + (includeShipping ? shippingCost : 0)'); + expect(jsonRes.Data).to.have.property('Variables'); + expect(jsonRes.Data.Variables).equals('{"amount": "number", "taxRate": "number", "discountRate": "number", "includeShipping": "boolean", "shippingCost": "number"}'); + expect(jsonRes.Data).to.have.property('ResultDataType'); + expect(jsonRes.Data.ResultDataType).equals('number'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Function Expression Operations/folder.bru b/bruno/form-service/Field Operation/Function Expression Operations/folder.bru new file mode 100644 index 0000000..c8c8258 --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Function Expression Operations + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Iterate Operations/Create iterate operation.bru b/bruno/form-service/Field Operation/Iterate Operations/Create iterate operation.bru new file mode 100644 index 0000000..2b8ab87 --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/Create iterate operation.bru @@ -0,0 +1,59 @@ +meta { + name: Create Iterate Operation + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-iterate-operations + body: json + auth: none +} + +body:json { + { + "Name": "Array Sum Operation", + "Description": "Sum all elements in an array", + "CollectionField": "items", + "ResultField": "total", + "OperationId": "5b1410d5-47dd-424b-ac85-336e8d2a4d36", + "FilterExpression": "item.value > 0" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("ITERATE_OPERATION_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Iterate Operation is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Array Sum Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Sum all elements in an array'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Iterate'); + expect(jsonRes.Data).to.have.property('CollectionField'); + expect(jsonRes.Data.CollectionField).equals('items'); + expect(jsonRes.Data).to.have.property('ResultField'); + expect(jsonRes.Data.ResultField).equals('total'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data.OperationId).equals('5b1410d5-47dd-424b-ac85-336e8d2a4d36'); + expect(jsonRes.Data).to.have.property('FilterExpression'); + expect(jsonRes.Data.FilterExpression).equals('item.value > 0'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Iterate Operations/Delete iterate operation.bru b/bruno/form-service/Field Operation/Iterate Operations/Delete iterate operation.bru new file mode 100644 index 0000000..5877b88 --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/Delete iterate operation.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Iterate Operation + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-iterate-operations/{{ITERATE_OPERATION_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Iterate Operation is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Operation/Iterate Operations/Get iterate operation by id.bru b/bruno/form-service/Field Operation/Iterate Operations/Get iterate operation by id.bru new file mode 100644 index 0000000..587ded7 --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/Get iterate operation by id.bru @@ -0,0 +1,34 @@ +meta { + name: Get Iterate Operation by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/field-iterate-operations/{{ITERATE_OPERATION_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Iterate Operation has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{ITERATE_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Iterate'); + expect(jsonRes.Data).to.have.property('CollectionField'); + expect(jsonRes.Data).to.have.property('ResultField'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('FilterExpression'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Iterate Operations/Search iterate operations.bru b/bruno/form-service/Field Operation/Iterate Operations/Search iterate operations.bru new file mode 100644 index 0000000..d718d4b --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/Search iterate operations.bru @@ -0,0 +1,57 @@ +meta { + name: Search Iterate Operations + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-iterate-operations/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~name: Array +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Iterate Operations search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Iterate Operations have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('Type'); + expect(item.Type).equals('Iterate'); + expect(item).to.have.property('CollectionField'); + expect(item).to.have.property('ResultField'); + expect(item).to.have.property('OperationId'); + expect(item).to.have.property('FilterExpression'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Operation/Iterate Operations/Update iterate operation.bru b/bruno/form-service/Field Operation/Iterate Operations/Update iterate operation.bru new file mode 100644 index 0000000..7942bf9 --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/Update iterate operation.bru @@ -0,0 +1,52 @@ +meta { + name: Update Iterate Operation + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/field-iterate-operations/{{ITERATE_OPERATION_ID}} + body: json + auth: none +} + +body:json { + { + "Name": "Updated Array Sum Operation", + "Description": "Updated description for array sum operation", + "CollectionField": "products", + "ResultField": "sum", + "OperationId": "5b1410d5-47dd-424b-ac85-336e8d2a4d36", + "FilterExpression": "product.price > 10" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Iterate Operation is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{ITERATE_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Array Sum Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated description for array sum operation'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Iterate'); + expect(jsonRes.Data).to.have.property('CollectionField'); + expect(jsonRes.Data.CollectionField).equals('products'); + expect(jsonRes.Data).to.have.property('ResultField'); + expect(jsonRes.Data.ResultField).equals('sum'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data.OperationId).equals('5b1410d5-47dd-424b-ac85-336e8d2a4d36'); + expect(jsonRes.Data).to.have.property('FilterExpression'); + expect(jsonRes.Data.FilterExpression).equals('product.price > 10'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Iterate Operations/folder.bru b/bruno/form-service/Field Operation/Iterate Operations/folder.bru new file mode 100644 index 0000000..2a22f59 --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Iterate Operations + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Logical Operations/Create logical operation.bru b/bruno/form-service/Field Operation/Logical Operations/Create logical operation.bru new file mode 100644 index 0000000..560fb54 --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/Create logical operation.bru @@ -0,0 +1,53 @@ +meta { + name: Create Logical Operation + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-logical-operations + body: json + auth: none +} + +body:json { + { + "Name": "Age Check Operation", + "Description": "Check if age is greater than 18", + "Operator": "GreaterThan", + "Operands": "[{\"type\":\"field\",\"value\":\"age\"},{\"type\":\"number\",\"value\":18}]" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("LOGICAL_OPERATION_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Logical Operation is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Age Check Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Check if age is greater than 18'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Logical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('GreaterThan'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data.Operands).equals('[{"type":"field","value":"age"},{"type":"number","value":18}]'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Logical Operations/Delete logical operation.bru b/bruno/form-service/Field Operation/Logical Operations/Delete logical operation.bru new file mode 100644 index 0000000..15a2f1f --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/Delete logical operation.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Logical Operation + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-logical-operations/{{LOGICAL_OPERATION_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Logical Operation is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Operation/Logical Operations/Get logical operation by id.bru b/bruno/form-service/Field Operation/Logical Operations/Get logical operation by id.bru new file mode 100644 index 0000000..9e53fff --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/Get logical operation by id.bru @@ -0,0 +1,32 @@ +meta { + name: Get Logical Operation by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/field-logical-operations/{{LOGICAL_OPERATION_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Logical Operation has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{LOGICAL_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Logical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Logical Operations/Search logical operations.bru b/bruno/form-service/Field Operation/Logical Operations/Search logical operations.bru new file mode 100644 index 0000000..9d1863d --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/Search logical operations.bru @@ -0,0 +1,55 @@ +meta { + name: Search Logical Operations + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-logical-operations/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~name: Age +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Logical Operations search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Logical Operations have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('Type'); + expect(item.Type).equals('Logical'); + expect(item).to.have.property('Operator'); + expect(item).to.have.property('Operands'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Operation/Logical Operations/Update logical operation.bru b/bruno/form-service/Field Operation/Logical Operations/Update logical operation.bru new file mode 100644 index 0000000..5425d1a --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/Update logical operation.bru @@ -0,0 +1,46 @@ +meta { + name: Update Logical Operation + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/field-logical-operations/{{LOGICAL_OPERATION_ID}} + body: json + auth: none +} + +body:json { + { + "Name": "Updated Age Check Operation", + "Description": "Updated description for age check operation", + "Operator": "GreaterThanOrEqual", + "Operands": "[{\"type\":\"field\",\"value\":\"age\"},{\"type\":\"number\",\"value\":21}]" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Logical Operation is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{LOGICAL_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Age Check Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated description for age check operation'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Logical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('GreaterThanOrEqual'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data.Operands).equals('[{"type":"field","value":"age"},{"type":"number","value":21}]'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Logical Operations/folder.bru b/bruno/form-service/Field Operation/Logical Operations/folder.bru new file mode 100644 index 0000000..557ce04 --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Logical Operations + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Mathematical Operations/Create mathematical operation.bru b/bruno/form-service/Field Operation/Mathematical Operations/Create mathematical operation.bru new file mode 100644 index 0000000..dfeb316 --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/Create mathematical operation.bru @@ -0,0 +1,56 @@ +meta { + name: Create Mathematical Operation + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-mathematical-operations + body: json + auth: none +} + +body:json { + { + "Name": "Addition Operation", + "Description": "Add two numbers together", + "Operator": "Add", + "Operands": "[{\"type\":\"number\",\"value\":5},{\"type\":\"number\",\"value\":3}]", + "ResultDataType": "Float" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("MATHEMATICAL_OPERATION_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Mathematical Operation is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Addition Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Add two numbers together'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Mathematical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('Add'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data.Operands).equals('[{"type":"number","value":5},{"type":"number","value":3}]'); + expect(jsonRes.Data).to.have.property('ResultDataType'); + expect(jsonRes.Data.ResultDataType).equals('Float'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Mathematical Operations/Delete mathematical operation.bru b/bruno/form-service/Field Operation/Mathematical Operations/Delete mathematical operation.bru new file mode 100644 index 0000000..101fcce --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/Delete mathematical operation.bru @@ -0,0 +1,23 @@ +meta { + name: Delete Mathematical Operation + type: http + seq: 4 +} + +delete { + url: {{BASE_URL}}/field-mathematical-operations/{{MATHEMATICAL_OPERATION_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Mathematical Operation is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Mathematical Operations/Get mathematical operation by id.bru b/bruno/form-service/Field Operation/Mathematical Operations/Get mathematical operation by id.bru new file mode 100644 index 0000000..5bc0e9f --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/Get mathematical operation by id.bru @@ -0,0 +1,33 @@ +meta { + name: Get Mathematical Operation by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/field-mathematical-operations/{{MATHEMATICAL_OPERATION_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Mathematical Operation has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{MATHEMATICAL_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Mathematical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data).to.have.property('ResultDataType'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Mathematical Operations/Search mathematical operations.bru b/bruno/form-service/Field Operation/Mathematical Operations/Search mathematical operations.bru new file mode 100644 index 0000000..b27b6d0 --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/Search mathematical operations.bru @@ -0,0 +1,56 @@ +meta { + name: Search Mathematical Operations + type: http + seq: 5 +} + +get { + url: {{BASE_URL}}/field-mathematical-operations/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~name: Addition +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Mathematical Operations search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Mathematical Operations have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('Type'); + expect(item.Type).equals('Mathematical'); + expect(item).to.have.property('Operator'); + expect(item).to.have.property('Operands'); + expect(item).to.have.property('ResultDataType'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Operation/Mathematical Operations/Update mathematical operation.bru b/bruno/form-service/Field Operation/Mathematical Operations/Update mathematical operation.bru new file mode 100644 index 0000000..f5ab45a --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/Update mathematical operation.bru @@ -0,0 +1,49 @@ +meta { + name: Update Mathematical Operation + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/field-mathematical-operations/{{MATHEMATICAL_OPERATION_ID}} + body: json + auth: none +} + +body:json { + { + "Name": "Updated Addition Operation", + "Description": "Updated description for addition operation", + "Operator": "Subtract", + "Operands": "[{\"type\":\"number\",\"value\":10},{\"type\":\"number\",\"value\":4}]", + "ResultDataType": "Integer" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Mathematical Operation is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{MATHEMATICAL_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Addition Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated description for addition operation'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Mathematical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('Subtract'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data.Operands).equals('[{"type":"number","value":10},{"type":"number","value":4}]'); + expect(jsonRes.Data).to.have.property('ResultDataType'); + expect(jsonRes.Data.ResultDataType).equals('Integer'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Mathematical Operations/folder.bru b/bruno/form-service/Field Operation/Mathematical Operations/folder.bru new file mode 100644 index 0000000..28f37ac --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Mathematical Operations + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/folder.bru b/bruno/form-service/Field Operation/folder.bru new file mode 100644 index 0000000..9da6e1d --- /dev/null +++ b/bruno/form-service/Field Operation/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Field Operation + seq: 2 +} diff --git a/bruno/form-service/Field Rules/Calculation Rules/Create calculation rule.bru b/bruno/form-service/Field Rules/Calculation Rules/Create calculation rule.bru new file mode 100644 index 0000000..f2752fd --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/Create calculation rule.bru @@ -0,0 +1,55 @@ +meta { + name: Create Calculation Rule + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-calculation-rules + body: json + auth: none +} + +body:json { + { + "Name": "Total Price Calculation", + "Description": "Calculate total price based on quantity and unit price", + "ConditionForOperationId": "7c1410d5-47dd-424b-ac85-336e8d2a4d37", + "OperationId": "7c1410d5-47dd-424b-ac85-336e8d2a4d37", + "LogicId": "15af8cd4-828d-48af-9d21-f4630083f487" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("CALCULATION_RULE_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Rule is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Total Price Calculation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Calculate total price based on quantity and unit price'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(1); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('ConditionForOperationId'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Calculation Rules/Delete calculation rule.bru b/bruno/form-service/Field Rules/Calculation Rules/Delete calculation rule.bru new file mode 100644 index 0000000..7c13840 --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/Delete calculation rule.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Calculation Rule + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-calculation-rules/{{CALCULATION_RULE_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Rule is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Rules/Calculation Rules/Get calculation rule by id.bru b/bruno/form-service/Field Rules/Calculation Rules/Get calculation rule by id.bru new file mode 100644 index 0000000..0cf1b99 --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/Get calculation rule by id.bru @@ -0,0 +1,32 @@ +meta { + name: Get Calculation Rule by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-calculation-rules/{{CALCULATION_RULE_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Rule has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('ConditionForOperationId'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Rules/Calculation Rules/Search calculation rules.bru b/bruno/form-service/Field Rules/Calculation Rules/Search calculation rules.bru new file mode 100644 index 0000000..29d0ac7 --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/Search calculation rules.bru @@ -0,0 +1,57 @@ +meta { + name: Search Calculation Rules + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-calculation-rules/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~name: Price + ~isActive: true +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain calculation rules", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Name'); + expect(firstItem).to.have.property('Description'); + expect(firstItem).to.have.property('Priority'); + expect(firstItem).to.have.property('IsActive'); + expect(firstItem).to.have.property('ConditionForOperationId'); + expect(firstItem).to.have.property('OperationId'); + expect(firstItem).to.have.property('LogicId'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Rules/Calculation Rules/Update calculation rule.bru b/bruno/form-service/Field Rules/Calculation Rules/Update calculation rule.bru new file mode 100644 index 0000000..3e694ec --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/Update calculation rule.bru @@ -0,0 +1,49 @@ +meta { + name: Update Calculation Rule + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-calculation-rules/{{CALCULATION_RULE_ID}} + body: json + auth: none +} + +body:json { + { + "Name": "Updated Total Price Calculation", + "Description": "Updated calculation for total price", + "Priority": 2, + "IsActive": false, + "ConditionForOperationId": "7c1410d5-47dd-424b-ac85-336e8d2a4d37", + "OperationId": "7c1410d5-47dd-424b-ac85-336e8d2a4d37", + "LogicId": "15af8cd4-828d-48af-9d21-f4630083f487" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Rule is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Total Price Calculation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated calculation for total price'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(2); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(false); + expect(jsonRes.Data).to.have.property('ConditionForOperationId'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Calculation Rules/folder.bru b/bruno/form-service/Field Rules/Calculation Rules/folder.bru new file mode 100644 index 0000000..8a72244 --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Calculation Rules + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Rules/Skip Rules/Create skip rule.bru b/bruno/form-service/Field Rules/Skip Rules/Create skip rule.bru new file mode 100644 index 0000000..c1a8d23 --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/Create skip rule.bru @@ -0,0 +1,58 @@ +meta { + name: Create Skip Rule + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-skip-rules + body: json + auth: none +} + +body:json { + { + "Name": "Age Skip Rule", + "Description": "Skip form section if user is under 18", + "Priority": 1, + "IsActive": true, + "OperationId": "2ad466ac-7f4f-4ee1-8f54-784f2eccf1b9", + "SkipWhenTrue": true, + "LogicId": "221a286a-51cb-419d-8a31-4aa0700d43b8" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("SKIP_RULE_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Rule is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Age Skip Rule'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Skip form section if user is under 18'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(1); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('SkipWhenTrue'); + expect(jsonRes.Data.SkipWhenTrue).equals(true); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Skip Rules/Delete skip rule.bru b/bruno/form-service/Field Rules/Skip Rules/Delete skip rule.bru new file mode 100644 index 0000000..27f13b5 --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/Delete skip rule.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Skip Rule + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-skip-rules/{{SKIP_RULE_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Rule is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Rules/Skip Rules/Get skip rule by id.bru b/bruno/form-service/Field Rules/Skip Rules/Get skip rule by id.bru new file mode 100644 index 0000000..2632a89 --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/Get skip rule by id.bru @@ -0,0 +1,32 @@ +meta { + name: Get Skip Rule by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-skip-rules/{{SKIP_RULE_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Rule has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('SkipWhenTrue'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Rules/Skip Rules/Search skip rules.bru b/bruno/form-service/Field Rules/Skip Rules/Search skip rules.bru new file mode 100644 index 0000000..ee689f1 --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/Search skip rules.bru @@ -0,0 +1,57 @@ +meta { + name: Search Skip Rules + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-skip-rules/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~name: Age + ~isActive: true +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain skip rules", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Name'); + expect(firstItem).to.have.property('Description'); + expect(firstItem).to.have.property('Priority'); + expect(firstItem).to.have.property('IsActive'); + expect(firstItem).to.have.property('OperationId'); + expect(firstItem).to.have.property('SkipWhenTrue'); + expect(firstItem).to.have.property('LogicId'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Rules/Skip Rules/Update skip rule.bru b/bruno/form-service/Field Rules/Skip Rules/Update skip rule.bru new file mode 100644 index 0000000..f9f3e54 --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/Update skip rule.bru @@ -0,0 +1,50 @@ +meta { + name: Update Skip Rule + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-skip-rules/{{SKIP_RULE_ID}} + body: json + auth: none +} + +body:json { + { + "Name": "Updated Age Skip Rule", + "Description": "Updated description for age skip rule", + "Priority": 2, + "IsActive": false, + "OperationId": "2ad466ac-7f4f-4ee1-8f54-784f2eccf1b9", + "SkipWhenTrue": false, + "LogicId": "221a286a-51cb-419d-8a31-4aa0700d43b8" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Rule is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Age Skip Rule'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated description for age skip rule'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(2); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(false); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('SkipWhenTrue'); + expect(jsonRes.Data.SkipWhenTrue).equals(false); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Skip Rules/folder.bru b/bruno/form-service/Field Rules/Skip Rules/folder.bru new file mode 100644 index 0000000..52bfbcb --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Skip Rules + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Rules/Validation Rules/Create validation rule.bru b/bruno/form-service/Field Rules/Validation Rules/Create validation rule.bru new file mode 100644 index 0000000..daa8501 --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/Create validation rule.bru @@ -0,0 +1,61 @@ +meta { + name: Create Validation Rule + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-validation-rules + body: json + auth: none +} + +body:json { + { + "Name": "Email Validation Rule", + "Description": "Validate email format and domain", + "Priority": 1, + "IsActive": true, + "OperationId": "14aff0d6-0d9e-4337-90a8-ed0078a91a44", + "ErrorWhenFalse": true, + "ErrorMessage": "Please enter a valid email address", + "LogicId": "15f0cedf-3f46-4498-a090-5a4360e26a21" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("VALIDATION_RULE_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Rule is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Email Validation Rule'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Validate email format and domain'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(1); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('ErrorWhenFalse'); + expect(jsonRes.Data.ErrorWhenFalse).equals(true); + expect(jsonRes.Data).to.have.property('ErrorMessage'); + expect(jsonRes.Data.ErrorMessage).equals('Please enter a valid email address'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Validation Rules/Delete validation rule.bru b/bruno/form-service/Field Rules/Validation Rules/Delete validation rule.bru new file mode 100644 index 0000000..50f7e86 --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/Delete validation rule.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Validation Rule + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-validation-rules/{{VALIDATION_RULE_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Rule is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Rules/Validation Rules/Get validation rule by id.bru b/bruno/form-service/Field Rules/Validation Rules/Get validation rule by id.bru new file mode 100644 index 0000000..b6a40dd --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/Get validation rule by id.bru @@ -0,0 +1,33 @@ +meta { + name: Get Validation Rule by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-validation-rules/{{VALIDATION_RULE_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Rule has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('ErrorWhenFalse'); + expect(jsonRes.Data).to.have.property('ErrorMessage'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Rules/Validation Rules/Search validation rules.bru b/bruno/form-service/Field Rules/Validation Rules/Search validation rules.bru new file mode 100644 index 0000000..f988d3b --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/Search validation rules.bru @@ -0,0 +1,58 @@ +meta { + name: Search Validation Rules + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-validation-rules/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~name: Email + ~isActive: true +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain validation rules", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Name'); + expect(firstItem).to.have.property('Description'); + expect(firstItem).to.have.property('Priority'); + expect(firstItem).to.have.property('IsActive'); + expect(firstItem).to.have.property('OperationId'); + expect(firstItem).to.have.property('ErrorWhenFalse'); + expect(firstItem).to.have.property('ErrorMessage'); + expect(firstItem).to.have.property('LogicId'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Rules/Validation Rules/Update validation rule.bru b/bruno/form-service/Field Rules/Validation Rules/Update validation rule.bru new file mode 100644 index 0000000..dd19539 --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/Update validation rule.bru @@ -0,0 +1,53 @@ +meta { + name: Update Validation Rule + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-validation-rules/{{VALIDATION_RULE_ID}} + body: json + auth: none +} + +body:json { + { + "Name": "Updated Email Validation Rule", + "Description": "Updated email validation with enhanced checks", + "Priority": 2, + "IsActive": false, + "OperationId": "14aff0d6-0d9e-4337-90a8-ed0078a91a44", + "ErrorWhenFalse": false, + "ErrorMessage": "Please enter a valid email address with proper domain", + "LogicId": "15f0cedf-3f46-4498-a090-5a4360e26a21" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Rule is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Email Validation Rule'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated email validation with enhanced checks'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(2); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(false); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('ErrorWhenFalse'); + expect(jsonRes.Data.ErrorWhenFalse).equals(false); + expect(jsonRes.Data).to.have.property('ErrorMessage'); + expect(jsonRes.Data.ErrorMessage).equals('Please enter a valid email address with proper domain'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Validation Rules/folder.bru b/bruno/form-service/Field Rules/Validation Rules/folder.bru new file mode 100644 index 0000000..8bbbeb3 --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Validation Rules + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Rules/folder.bru b/bruno/form-service/Field Rules/folder.bru new file mode 100644 index 0000000..34f7ffc --- /dev/null +++ b/bruno/form-service/Field Rules/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Field Rules + seq: 3 +} diff --git a/bruno/form-service/Form Field/Create a new form field.bru b/bruno/form-service/Form Field/Create a new form field.bru new file mode 100644 index 0000000..a7070f1 --- /dev/null +++ b/bruno/form-service/Form Field/Create a new form field.bru @@ -0,0 +1,83 @@ +meta { + name: Create a new form field + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/form-fields + body: json + auth: none +} + +body:json { + { + "ParentTemplateId": "{{TEMPLATE_ID}}", + "ParentSectionId": "{{SECTION_ID}}", + "Title": "What is your favorite color?", + "Description": "Please select your favorite color from the options below", + "ResponseType": "SingleChoiceSelection", + "Score": 5, + "CorrectAnswer": "Blue", + "IsRequired": true, + "Hint": "Choose the color you like the most", + "QuestionImageUrl": "https://example.com/color-image.jpg", + "RangeMin": 1, + "RangeMax": 10, + "Options": [ + { + "Text": "Red", + "Sequence": "1", + "ImageUrl": "https://example.com/red.jpg" + }, + { + "Text": "Blue", + "Sequence": "2", + "ImageUrl": "https://example.com/blue.jpg" + }, + { + "Text": "Green", + "Sequence": "3", + "ImageUrl": "https://example.com/green.jpg" + }, + { + "Text": "Yellow", + "Sequence": "4", + "ImageUrl": "https://example.com/yellow.jpg" + } + ] + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("FORM_FIELD_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form field is created", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Title'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data).to.have.property('ResponseType'); + expect(jsonRes.Data).to.have.property('Score'); + expect(jsonRes.Data).to.have.property('CorrectAnswer'); + expect(jsonRes.Data).to.have.property('IsRequired'); + expect(jsonRes.Data).to.have.property('Hint'); + expect(jsonRes.Data).to.have.property('Options'); + expect(jsonRes.Data).to.have.property('QuestionImageUrl'); + expect(jsonRes.Data).to.have.property('RangeMin'); + expect(jsonRes.Data).to.have.property('RangeMax'); + }); +} diff --git a/bruno/form-service/Form Field/Create boolean form field.bru b/bruno/form-service/Form Field/Create boolean form field.bru new file mode 100644 index 0000000..f6d4ca9 --- /dev/null +++ b/bruno/form-service/Form Field/Create boolean form field.bru @@ -0,0 +1,43 @@ +meta { + name: Create boolean form field + type: http + seq: 9 +} + +post { + url: {{BASE_URL}}/form-fields + body: json + auth: none +} + +body:json { + { + "ParentTemplateId": "{{TEMPLATE_ID}}", + "ParentSectionId": "{{SECTION_ID}}", + "Title": "Do you agree to the terms and conditions?", + "Description": "Please read the terms and conditions and indicate your agreement", + "ResponseType": "Boolean", + "Score": 1, + "CorrectAnswer": "true", + "IsRequired": true, + "Hint": "Select Yes to agree or No to disagree", + "QuestionImageUrl": "https://example.com/terms-field.jpg", + "RangeMin": 0, + "RangeMax": 1 + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Boolean form field is created", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.ResponseType).to.equal('Boolean'); + expect(jsonRes.Data.CorrectAnswer).to.equal('true'); + }); +} diff --git a/bruno/form-service/Form Field/Create number form field.bru b/bruno/form-service/Form Field/Create number form field.bru new file mode 100644 index 0000000..14ffd01 --- /dev/null +++ b/bruno/form-service/Form Field/Create number form field.bru @@ -0,0 +1,44 @@ +meta { + name: Create number form field + type: http + seq: 8 +} + +post { + url: {{BASE_URL}}/form-fields + body: json + auth: none +} + +body:json { + { + "ParentTemplateId": "{{TEMPLATE_ID}}", + "ParentSectionId": "{{SECTION_ID}}", + "Title": "What is your age?", + "Description": "Please enter your current age in years", + "ResponseType": "Integer", + "Score": 2, + "CorrectAnswer": "25", + "IsRequired": true, + "Hint": "Enter a number between 1 and 120", + "QuestionImageUrl": "https://example.com/age-field.jpg", + "RangeMin": 1, + "RangeMax": 120 + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Number form field is created", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.ResponseType).to.equal('Integer'); + expect(jsonRes.Data.RangeMin).to.equal(1); + expect(jsonRes.Data.RangeMax).to.equal(120); + }); +} diff --git a/bruno/form-service/Form Field/Create text form field.bru b/bruno/form-service/Form Field/Create text form field.bru new file mode 100644 index 0000000..a9b1ad0 --- /dev/null +++ b/bruno/form-service/Form Field/Create text form field.bru @@ -0,0 +1,43 @@ +meta { + name: Create text form field + type: http + seq: 7 +} + +post { + url: {{BASE_URL}}/form-fields + body: json + auth: none +} + +body:json { + { + "ParentTemplateId": "{{TEMPLATE_ID}}", + "ParentSectionId": "{{SECTION_ID}}", + "Title": "What is your full name?", + "Description": "Please enter your complete name as it appears on official documents", + "ResponseType": "Text", + "Score": 3, + "CorrectAnswer": "John Doe", + "IsRequired": true, + "Hint": "Enter your first and last name", + "QuestionImageUrl": "https://example.com/name-field.jpg", + "RangeMin": 2, + "RangeMax": 50 + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Text form field is created", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.ResponseType).to.equal('Text'); + expect(jsonRes.Data.IsRequired).to.equal(true); + }); +} diff --git a/bruno/form-service/Question/Delete a question record.bru b/bruno/form-service/Form Field/Delete form field.bru similarity index 51% rename from bruno/form-service/Question/Delete a question record.bru rename to bruno/form-service/Form Field/Delete form field.bru index 5108ce6..8c2875d 100644 --- a/bruno/form-service/Question/Delete a question record.bru +++ b/bruno/form-service/Form Field/Delete form field.bru @@ -1,24 +1,23 @@ meta { - name: Delete a question record + name: Delete form field type: http - seq: 5 + seq: 6 } delete { - url: {{BASE_URL}}/questions/{{QUESTION_ID}} - body: none + url: {{BASE_URL}}/form-fields/{{FORM_FIELD_ID}} auth: none } script:post-response { - test("Request is successfull", function () { + test("Request is successful", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); expect(jsonRes.Status).to.eql('Success'); }); - test("Question is deleted", function () { + test("Form field is deleted", function () { var jsonRes = res.getBody(); - expect(jsonRes.Data).equals(true); + expect(jsonRes.Data).to.equal(true); }); -} +} \ No newline at end of file diff --git a/bruno/form-service/Question/Get question by id.bru b/bruno/form-service/Form Field/Get form field by id.bru similarity index 71% rename from bruno/form-service/Question/Get question by id.bru rename to bruno/form-service/Form Field/Get form field by id.bru index bcf49c3..dc7fb64 100644 --- a/bruno/form-service/Question/Get question by id.bru +++ b/bruno/form-service/Form Field/Get form field by id.bru @@ -1,38 +1,35 @@ meta { - name: Get question by id + name: Get form field by id type: http seq: 2 } get { - url: {{BASE_URL}}/questions/{{QUESTION_ID}} - body: json + url: {{BASE_URL}}/form-fields/{{FORM_FIELD_ID}} auth: none } script:post-response { - test("Request is successfull", function () { - expect(res.getStatus()).to.equal(201); + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); expect(jsonRes.Status).to.eql('Success'); }); - test("Question is created", function () { + test("Form field data is returned", function () { var jsonRes = res.getBody(); expect(jsonRes.Data).to.have.property('id'); - expect(jsonRes.Data).to.have.property('ParentTemplateId'); - expect(jsonRes.Data).to.have.property('ParentSectionId'); expect(jsonRes.Data).to.have.property('Title'); expect(jsonRes.Data).to.have.property('Description'); expect(jsonRes.Data).to.have.property('DisplayCode'); expect(jsonRes.Data).to.have.property('ResponseType'); expect(jsonRes.Data).to.have.property('Score'); expect(jsonRes.Data).to.have.property('CorrectAnswer'); + expect(jsonRes.Data).to.have.property('IsRequired'); expect(jsonRes.Data).to.have.property('Hint'); expect(jsonRes.Data).to.have.property('Options'); expect(jsonRes.Data).to.have.property('QuestionImageUrl'); expect(jsonRes.Data).to.have.property('RangeMin'); expect(jsonRes.Data).to.have.property('RangeMax'); - }); -} +} \ No newline at end of file diff --git a/bruno/form-service/Form Field/Get form fields by template id.bru b/bruno/form-service/Form Field/Get form fields by template id.bru new file mode 100644 index 0000000..db6ad47 --- /dev/null +++ b/bruno/form-service/Form Field/Get form fields by template id.bru @@ -0,0 +1,25 @@ +meta { + name: Get form fields by template id + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/form-fields/by-template-id/{{TEMPLATE_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form fields data is returned", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Title'); + expect(jsonRes.Data).to.have.property('ResponseType'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Form Field/Update form field.bru b/bruno/form-service/Form Field/Update form field.bru new file mode 100644 index 0000000..ffb7807 --- /dev/null +++ b/bruno/form-service/Form Field/Update form field.bru @@ -0,0 +1,69 @@ +meta { + name: Update form field + type: http + seq: 4 +} + +put { + url: {{BASE_URL}}/form-fields/{{FORM_FIELD_ID}} + body: json + auth: none +} + +body:json { + { + "Title": "Updated: What is your favorite color?", + "Description": "Updated: Please select your favorite color from the options below", + "ResponseType": "SingleChoiceSelection", + "Score": 10, + "CorrectAnswer": "Green", + "IsRequired": true, + "Hint": "Updated: Choose the color you like the most", + "QuestionImageUrl": "https://example.com/updated-color-image.jpg", + "RangeMin": 1, + "RangeMax": 15, + "Options": [ + { + "Text": "Red", + "Sequence": "1", + "ImageUrl": "https://example.com/red.jpg" + }, + { + "Text": "Blue", + "Sequence": "2", + "ImageUrl": "https://example.com/blue.jpg" + }, + { + "Text": "Green", + "Sequence": "3", + "ImageUrl": "https://example.com/green.jpg" + }, + { + "Text": "Yellow", + "Sequence": "4", + "ImageUrl": "https://example.com/yellow.jpg" + }, + { + "Text": "Purple", + "Sequence": "5", + "ImageUrl": "https://example.com/purple.jpg" + } + ] + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form field is updated", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.Title).to.include('Updated:'); + expect(jsonRes.Data.Score).to.equal(10); + expect(jsonRes.Data.CorrectAnswer).to.equal('Green'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Question/folder.bru b/bruno/form-service/Form Field/folder.bru similarity index 53% rename from bruno/form-service/Question/folder.bru rename to bruno/form-service/Form Field/folder.bru index f3913b1..e09b2f5 100644 --- a/bruno/form-service/Question/folder.bru +++ b/bruno/form-service/Form Field/folder.bru @@ -1,6 +1,6 @@ meta { - name: Question - seq: 4 + name: Form Field + seq: 8 } auth { diff --git a/bruno/form-service/Form Submission/Generic search.bru b/bruno/form-service/Form Submission/Generic search.bru index 4330521..5f66fe3 100644 --- a/bruno/form-service/Form Submission/Generic search.bru +++ b/bruno/form-service/Form Submission/Generic search.bru @@ -11,6 +11,10 @@ get { } params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC id: {{SUBMISSION_ID}} } diff --git a/bruno/form-service/Form Submission/folder.bru b/bruno/form-service/Form Submission/folder.bru index 6f609f8..cdb2e43 100644 --- a/bruno/form-service/Form Submission/folder.bru +++ b/bruno/form-service/Form Submission/folder.bru @@ -1,6 +1,6 @@ meta { name: Form Submission - seq: 5 + seq: 9 } auth { diff --git a/bruno/form-service/Form Template Approval/Create form template approval.bru b/bruno/form-service/Form Template Approval/Create form template approval.bru new file mode 100644 index 0000000..8b252bf --- /dev/null +++ b/bruno/form-service/Form Template Approval/Create form template approval.bru @@ -0,0 +1,51 @@ +meta { + name: Create Form Template Approval + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/form-template-approvals + body: json + auth: none +} + +body:json { + { + "TemplateId": "{{TEMPLATE_ID}}", + "ApproverUserId": "{{USER_ID}}", + "Approved": false, + "ReviewComments": "Initial approval request" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("FORM_TEMPLATE_APPROVAL_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form Template Approval is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('TemplateId'); + expect(jsonRes.Data.TemplateId).equals('{{TEMPLATE_ID}}'); + expect(jsonRes.Data).to.have.property('ApproverUserId'); + expect(jsonRes.Data.ApproverUserId).equals('{{USER_ID}}'); + expect(jsonRes.Data).to.have.property('Approved'); + expect(jsonRes.Data.Approved).equals(false); + expect(jsonRes.Data).to.have.property('ReviewComments'); + expect(jsonRes.Data.ReviewComments).equals('Initial approval request'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Form Template Approval/Delete form template approval.bru b/bruno/form-service/Form Template Approval/Delete form template approval.bru new file mode 100644 index 0000000..546cf0b --- /dev/null +++ b/bruno/form-service/Form Template Approval/Delete form template approval.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Form Template Approval + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/form-template-approvals/{{FORM_TEMPLATE_APPROVAL_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form Template Approval is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Form Template Approval/Get form template approval by id.bru b/bruno/form-service/Form Template Approval/Get form template approval by id.bru new file mode 100644 index 0000000..4d39403 --- /dev/null +++ b/bruno/form-service/Form Template Approval/Get form template approval by id.bru @@ -0,0 +1,30 @@ +meta { + name: Get Form Template Approval by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/form-template-approvals/{{FORM_TEMPLATE_APPROVAL_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form Template Approval has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{FORM_TEMPLATE_APPROVAL_ID}}'); + expect(jsonRes.Data).to.have.property('TemplateId'); + expect(jsonRes.Data).to.have.property('ApproverUserId'); + expect(jsonRes.Data).to.have.property('Approved'); + expect(jsonRes.Data).to.have.property('ReviewComments'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Form Template Approval/Search form template approvals.bru b/bruno/form-service/Form Template Approval/Search form template approvals.bru new file mode 100644 index 0000000..a11565a --- /dev/null +++ b/bruno/form-service/Form Template Approval/Search form template approvals.bru @@ -0,0 +1,53 @@ +meta { + name: Search Form Template Approvals + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/form-template-approvals/search?pageIndex=0&itemsPerPage=10&orderBy=CreatedAt&order=DESC + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~status: Pending +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form Template Approvals search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Form Template Approvals have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('TemplateId'); + expect(item).to.have.property('ApproverUserId'); + expect(item).to.have.property('Approved'); + expect(item).to.have.property('ReviewComments'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Form Template Approval/Update form template approval.bru b/bruno/form-service/Form Template Approval/Update form template approval.bru new file mode 100644 index 0000000..a8c6600 --- /dev/null +++ b/bruno/form-service/Form Template Approval/Update form template approval.bru @@ -0,0 +1,42 @@ +meta { + name: Update Form Template Approval + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/form-template-approvals/{{FORM_TEMPLATE_APPROVAL_ID}} + body: json + auth: none +} + +body:json { + { + "ApproverUserId": "{{USER_ID}}", + "TemplateId": "{{TEMPLATE_ID}}", + "Approved": false, + "ReviewComments": "Template approved after review" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form Template Approval is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{FORM_TEMPLATE_APPROVAL_ID}}'); + expect(jsonRes.Data).to.have.property('TemplateId'); + expect(jsonRes.Data).to.have.property('ApproverUserId'); + expect(jsonRes.Data).to.have.property('Approved'); + expect(jsonRes.Data.Approved).equals(true); + expect(jsonRes.Data).to.have.property('ReviewComments'); + expect(jsonRes.Data.ReviewComments).equals('Template approved after review'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Form section/Create new form section.bru b/bruno/form-service/Form section/Create new form section.bru index bed61fd..1888bd2 100644 --- a/bruno/form-service/Form section/Create new form section.bru +++ b/bruno/form-service/Form section/Create new form section.bru @@ -21,12 +21,13 @@ body:json { // "ParentSectionId": "2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6" //any uuid value // } { - "ParentFormTemplateId": "7d6fe0be-3f6c-4a8e-8231-b8a7568d2c9d", + "ParentFormTemplateId": "{{TEMPLATE_ID}}", // "SectionIdentifier": "new form section 1" "Title": "New form section ", "Description": "sf esdrg", "Sequence": 12, - "ParentSectionId": "44071647-f61e-448b-8dec-ae2c8d3017c6" + "ParentSectionId": "6d0c3b91-7337-49cd-8a01-a0c3a2bd2601" + } } diff --git a/bruno/form-service/Form section/Generic search.bru b/bruno/form-service/Form section/Generic search.bru index 321e89b..aab7339 100644 --- a/bruno/form-service/Form section/Generic search.bru +++ b/bruno/form-service/Form section/Generic search.bru @@ -11,6 +11,10 @@ get { } params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC parentFormTemplateId: {{TEMPLATE_ID}} } diff --git a/bruno/form-service/Form section/folder.bru b/bruno/form-service/Form section/folder.bru index 45bb69b..79ce726 100644 --- a/bruno/form-service/Form section/folder.bru +++ b/bruno/form-service/Form section/folder.bru @@ -1,6 +1,6 @@ meta { name: Form section - seq: 3 + seq: 7 } auth { diff --git a/bruno/form-service/Form template/Generic search.bru b/bruno/form-service/Form template/Generic search.bru index 6444dd5..42f3c8f 100644 --- a/bruno/form-service/Form template/Generic search.bru +++ b/bruno/form-service/Form template/Generic search.bru @@ -11,6 +11,10 @@ get { } params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC id: {{TEMPLATE_ID}} ~title: What is your full name ~description: about testing the assessment diff --git a/bruno/form-service/Form template/create a form template.bru b/bruno/form-service/Form template/create a form template.bru index ce4e194..ffc0745 100644 --- a/bruno/form-service/Form template/create a form template.bru +++ b/bruno/form-service/Form template/create a form template.bru @@ -17,7 +17,7 @@ body:json { "CurrentVersion": 1, "Type": "Questionnaire", // "DisplayCode": "xyz1234", - "OwnerUserId": "ad693c12-3141-45e7-ab3a-59d03306757f", + "OwnerUserId": "{{USER_ID}}", // "RootSectionId": "9618c6a8-0555-4a14-95ec-1946ec09c8e0", "DefaultSectionNumbering": true, "ItemsPerPage":"OneQuestion" diff --git a/bruno/form-service/Form template/folder.bru b/bruno/form-service/Form template/folder.bru index c06640e..deaf2f6 100644 --- a/bruno/form-service/Form template/folder.bru +++ b/bruno/form-service/Form template/folder.bru @@ -1,6 +1,6 @@ meta { name: Form template - seq: 2 + seq: 6 } auth { diff --git a/bruno/form-service/Health Check Request.bru b/bruno/form-service/Health Check Request.bru index 6d56c37..170879d 100644 --- a/bruno/form-service/Health Check Request.bru +++ b/bruno/form-service/Health Check Request.bru @@ -1,7 +1,7 @@ meta { name: Health Check Request type: http - seq: 8 + seq: 12 } get { diff --git a/bruno/form-service/Input Unit List/Create input unit list.bru b/bruno/form-service/Input Unit List/Create input unit list.bru new file mode 100644 index 0000000..4330310 --- /dev/null +++ b/bruno/form-service/Input Unit List/Create input unit list.bru @@ -0,0 +1,77 @@ +meta { + name: Create Input Unit List + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/input-unit-lists + body: json + auth: none +} + +body:json { + { + "Name": "Temperature Units", + "Description": "Common temperature measurement units", + // "DisplayCode": "TEMP_UNITS", + // "IsActive": true, + "Units": [ + { + "Name": "Celsius", + "Symbol": "°C", + "Description": "Celsius temperature scale", + "ConversionFactor": 1.0, + "IsDefault": true + }, + { + "Name": "Fahrenheit", + "Symbol": "°F", + "Description": "Fahrenheit temperature scale", + "ConversionFactor": 1.8, + "IsDefault": false + }, + { + "Name": "Kelvin", + "Symbol": "K", + "Description": "Kelvin temperature scale", + "ConversionFactor": 1.0, + "IsDefault": false + } + ] + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("INPUT_UNIT_LIST_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Input Unit List is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Temperature Units'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Common temperature measurement units'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data.DisplayCode).equals('TEMP_UNITS'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('Units'); + expect(jsonRes.Data.Units).to.be.an('array'); + expect(jsonRes.Data.Units.length).to.equal(3); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Input Unit List/Delete input unit list.bru b/bruno/form-service/Input Unit List/Delete input unit list.bru new file mode 100644 index 0000000..3edfff7 --- /dev/null +++ b/bruno/form-service/Input Unit List/Delete input unit list.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Input Unit List + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/input-unit-lists/{{INPUT_UNIT_LIST_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Input Unit List is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Input Unit List/Get input unit list by id.bru b/bruno/form-service/Input Unit List/Get input unit list by id.bru new file mode 100644 index 0000000..cbebd0d --- /dev/null +++ b/bruno/form-service/Input Unit List/Get input unit list by id.bru @@ -0,0 +1,32 @@ +meta { + name: Get Input Unit List by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/input-unit-lists/{{INPUT_UNIT_LIST_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Input Unit List has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{INPUT_UNIT_LIST_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('Units'); + expect(jsonRes.Data.Units).to.be.an('array'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Input Unit List/Search input unit lists.bru b/bruno/form-service/Input Unit List/Search input unit lists.bru new file mode 100644 index 0000000..7342266 --- /dev/null +++ b/bruno/form-service/Input Unit List/Search input unit lists.bru @@ -0,0 +1,56 @@ +meta { + name: Search Input Unit Lists + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/input-unit-lists/search + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~Name: Temperature + ~IsActive: true +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Input Unit Lists search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Input Unit Lists have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('DisplayCode'); + expect(item).to.have.property('IsActive'); + expect(item).to.have.property('Units'); + expect(item.Units).to.be.an('array'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Input Unit List/Update input unit list.bru b/bruno/form-service/Input Unit List/Update input unit list.bru new file mode 100644 index 0000000..1d83433 --- /dev/null +++ b/bruno/form-service/Input Unit List/Update input unit list.bru @@ -0,0 +1,77 @@ +meta { + name: Update Input Unit List + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/input-unit-lists/{{INPUT_UNIT_LIST_ID}} + body: json + auth: none +} + +body:json { + { + "Name": "Updated Temperature Units", + "Description": "Updated common temperature measurement units", + // "DisplayCode": "TEMP_UNITS_V2", + // "IsActive": true, + "Units": [ + { + "Name": "Celsius", + "Symbol": "°C", + "Description": "Celsius temperature scale", + "ConversionFactor": 1.0, + "IsDefault": true + }, + { + "Name": "Fahrenheit", + "Symbol": "°F", + "Description": "Fahrenheit temperature scale", + "ConversionFactor": 1.8, + "IsDefault": false + }, + { + "Name": "Kelvin", + "Symbol": "K", + "Description": "Kelvin temperature scale", + "ConversionFactor": 1.0, + "IsDefault": false + }, + { + "Name": "Rankine", + "Symbol": "°R", + "Description": "Rankine temperature scale", + "ConversionFactor": 1.8, + "IsDefault": false + } + ] + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Input Unit List is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{INPUT_UNIT_LIST_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Temperature Units'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated common temperature measurement units'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data.DisplayCode).equals('TEMP_UNITS_V2'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('Units'); + expect(jsonRes.Data.Units).to.be.an('array'); + expect(jsonRes.Data.Units.length).to.equal(4); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Question Response/Generic search.bru b/bruno/form-service/Question Response/Generic search.bru index d4f65fa..ef882e2 100644 --- a/bruno/form-service/Question Response/Generic search.bru +++ b/bruno/form-service/Question Response/Generic search.bru @@ -11,6 +11,10 @@ get { } params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC responseType: Text ~formSubmissionId: a8b15839-1592-43b7-b586-89765cf99222 ~questionId: b56027ee-bdba-4182-bed6-d544a5bcc8e4 diff --git a/bruno/form-service/Question Response/folder.bru b/bruno/form-service/Question Response/folder.bru index d9415c7..f81867c 100644 --- a/bruno/form-service/Question Response/folder.bru +++ b/bruno/form-service/Question Response/folder.bru @@ -1,6 +1,6 @@ meta { name: Question Response - seq: 7 + seq: 11 } auth { diff --git a/bruno/form-service/Question/Create a new question.bru b/bruno/form-service/Question/Create a new question.bru deleted file mode 100644 index 5997458..0000000 --- a/bruno/form-service/Question/Create a new question.bru +++ /dev/null @@ -1,384 +0,0 @@ -meta { - name: Create a new question - type: http - seq: 1 -} - -post { - url: {{BASE_URL}}/questions - body: json - auth: none -} - -body:json { - // { - // "ParentTemplateId": "d9e0416b-b142-4492-a522-a7fc57f9c224", //Form Template id - // "ParentSectionId": "36869bba-551f-4aaf-a150-ccafd9567254", //Form Section Id - // "Title": "Question Two", - // "Description": "Question Two description", - // "DisplayCode": "Question Two Display code", - // "ResponseType": "Integer", - // "Score": 11, - // "CorrectAnswer": "Question Two correct answer", - // "Hint": "Question Two hint", - // "Options":"option1,option2", - // "QuestionImageUrl":"this.is.image", - // "RangeMin":2, - // "RangeMax":4 - // }, - // { - // "ParentTemplateId": "d9e0416b-b142-4492-a522-a7fc57f9c224", - // "ParentSectionId": "36869bba-551f-4aaf-a150-ccafd9567254", - // "Title": "range ........!", - // "Description": "Birth Day", - // "ResponseType": "Boolean", - // "Score": 5, - // // "DisplayCode": "2b3b3ea7-d55f-46fb-901f-380a92be0059", - // "CorrectAnswer": "1234", - // "Hint": "date", - // "QuestionImageUrl": "a", - // "RangeMin": 1, - // "RangeMax": 2, - // "Options": "option1, option2" - // } - // { - // "ParentTemplateId": "5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a", - // "ParentSectionId": "a1b31378-59b0-4ca8-8d54-323798bf924e", - // "Title": "qqqqqqqqqqqqqqqq", - // "Description": "qqqqqqqqq", - // "ResponseType": "Text", - // "Score": 1, - // "CorrectAnswer": "qqqqqqqqqqqqqqq", - // "Hint": "qqqqqqqqqqqqq", - // "QuestionImageUrl": "qqqqqqqqqqqq", - // "RangeMin": null, - // "RangeMax": null, - // "Options": [ - // "option1", - // "option2" - // ] - // } - // { - // "ParentTemplateId": "5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a", - // "ParentSectionId": "a1b31378-59b0-4ca8-8d54-323798bf924e", - // "Title": "qqqqqqqqqqqqqqqq", - // "Description": "qqqqqqqqqq", - // "ResponseType": "SinglehoiceSelection", - // "Score": 1, - // "CorrectAnswer": "qqqqqqqqqqqqqqq", - // "Hint": "qqqqqqqqqqqqq", - // "QuestionImageUrl": "qqqqqqqqqqqq", - // "RangeMin": null, - // "RangeMax": null, - // "Options": [ - // { - // "Text": "qqqqq", - // "Sequence": 1 - // }, - // { - // "Text": "qqqq", - // "Sequence": 2 - // }, - // { - // "Text": "qq", - // "Sequence": 3 - // } - // ] - // } - { - "ParentTemplateId": "{{TEMPLATE_ID}}", - "ParentSectionId": "{{PARENT_SECTION_ID}}", - "ResponseType": "SingleChoiceSelection", - "Options": [ - { - "Text": "qqqqq", - "Sequence": "1", - "ImageUrl": "asdasfdsf" - }, - { - "Text": "qqqqq", - "Sequence": "2", - "ImageUrl": "asdasfdsf" - }, - { - "Text": "qqqqq", - "Sequence": "3", - "ImageUrl": "asdasfdsf" - }, - { - "Text": "qqqqq", - "Sequence": "1", - "ImageUrl": "asdasfdsf" - }, - { - "Text": "qqqqq", - "Sequence": "2", - "ImageUrl": "asdasfdsf" - }, - { - "Text": "qqqqq", - "Sequence": "3", - "ImageUrl": "asdasfdsf" - } - ] - } - - - - // { - // "Title": "What is your full name", - // "Description": "description", - // "ResponseType": "SingleChoiceSelection", - // "Options": [ - // { - // "Text": "sdfg", - // "Sequence": "a1", - // "ImageUrl": "wererterytryur" - // }, - // { - // "Text": "sdagdf", - // "Sequence": "a2", - // "ImageUrl": "asdf" - // }, - // { - // "Text": "dzfg", - // "Sequence": "a3", - // "ImageUrl": "zsdfg" - // }, - // { - // "Text": "zdfgdfg", - // "Sequence": "a4", - // "ImageUrl": "dfg" - // } - // ] - // } -} - -script:post-response { - try { - var jsonRes = res.getBody(); - bru.setEnvVar("QUESTION_ID", jsonRes.Data.id); - } - catch (error) { - console.log(error.message); - } - - test("Request is successfull", function () { - expect(res.getStatus()).to.equal(201); - var jsonRes = res.getBody(); - expect(jsonRes.Status).to.eql('Success'); - }); - - test("Question is created", function () { - var jsonRes = res.getBody(); - expect(jsonRes.Data).to.have.property('id'); - expect(jsonRes.Data).to.have.property('ParentTemplateId'); - expect(jsonRes.Data).to.have.property('ParentSectionId'); - expect(jsonRes.Data).to.have.property('Title'); - expect(jsonRes.Data).to.have.property('Description'); - expect(jsonRes.Data).to.have.property('DisplayCode'); - expect(jsonRes.Data).to.have.property('ResponseType'); - expect(jsonRes.Data).to.have.property('Score'); - expect(jsonRes.Data).to.have.property('CorrectAnswer'); - expect(jsonRes.Data).to.have.property('Hint'); - expect(jsonRes.Data).to.have.property('Options'); - expect(jsonRes.Data).to.have.property('QuestionImageUrl'); - expect(jsonRes.Data).to.have.property('RangeMin'); - expect(jsonRes.Data).to.have.property('RangeMax'); - - }); - var template = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldValue
Status{{response.Status}}
Message{{response.Message}}
HttpCode{{response.HttpCode}}
Request Method{{response.Request.Method}}
Request Host{{response.Request.Host}}
Request Body - ParentTemplateId{{response.Request.Body.ParentTemplateId}}
Request Body - ParentSectionId{{response.Request.Body.ParentSectionId}}
Request Body - ResponseType{{response.Request.Body.ResponseType}}
Request Body - Options{{response.Request.Body.Options}}
Request Url{{response.Request.Url}}
Request Params{{response.Request.Params}}
Data ID{{response.Data.id}}
Data Title{{response.Data.Title}}
Data Description{{response.Data.Description}}
Data DisplayCode{{response.Data.DisplayCode}}
Data ResponseType{{response.Data.ResponseType}}
Data Score{{response.Data.Score}}
Data Sequence{{response.Data.Sequence}}
Data CorrectAnswer{{response.Data.CorrectAnswer}}
Data Hint{{response.Data.Hint}}
Data Options{{response.Data.Options}}
Data QuestionImageUrl{{response.Data.QuestionImageUrl}}
Data RangeMin{{response.Data.RangeMin}}
Data RangeMax{{response.Data.RangeMax}}
Data ParentFormSection ID{{response.Data.ParentFormSection.id}}
Data ParentFormSection SectionIdentifier{{response.Data.ParentFormSection.SectionIdentifier}}
Data ParentFormSection Title{{response.Data.ParentFormSection.Title}}
Data ParentFormSection Description{{response.Data.ParentFormSection.Description}}
Data ParentFormSection DisplayCode{{response.Data.ParentFormSection.DisplayCode}}
Data ParentFormSection Sequence{{response.Data.ParentFormSection.Sequence}}
Data ParentFormSection ParentSectionId{{response.Data.ParentFormSection.ParentSectionId}}
Data ParentFormSection CreatedAt{{response.Data.ParentFormSection.CreatedAt}}
Data ParentFormTemplate ID{{response.Data.ParentFormTemplate.id}}
Data ParentFormTemplate Title{{response.Data.ParentFormTemplate.Title}}
Data ParentFormTemplate Description{{response.Data.ParentFormTemplate.Description}}
Data ParentFormTemplate Type{{response.Data.ParentFormTemplate.Type}}
Data ParentFormTemplate DisplayCode{{response.Data.ParentFormTemplate.DisplayCode}}
Data ParentFormTemplate OwnerUserId{{response.Data.ParentFormTemplate.OwnerUserId}}
Data ParentFormTemplate RootSectionId{{response.Data.ParentFormTemplate.RootSectionId}}
Data ParentFormTemplate DefaultSectionNumbering{{response.Data.ParentFormTemplate.DefaultSectionNumbering}}
Data ParentFormTemplate CreatedAt{{response.Data.ParentFormTemplate.CreatedAt}}
Data CreatedAt{{response.Data.CreatedAt}}
Data UpdatedAt{{response.Data.UpdatedAt}}
- `; - - function constructVisualizerPayload() { - return {response: res.getBody()}; - } - - // pm.visualizer.set(template, constructVisualizerPayload()); -} diff --git a/bruno/form-service/Question/Generic search.bru b/bruno/form-service/Question/Generic search.bru deleted file mode 100644 index 5a6e515..0000000 --- a/bruno/form-service/Question/Generic search.bru +++ /dev/null @@ -1,28 +0,0 @@ -meta { - name: Generic search - type: http - seq: 4 -} - -get { - url: {{BASE_URL}}/questions/search?parentTemplateId={{TEMPLATE_ID}} - body: none - auth: none -} - -params:query { - parentTemplateId: {{TEMPLATE_ID}} -} - -script:post-response { - test("Request is successfull", function () { - expect(res.getStatus()).to.equal(200); - var jsonRes = res.getBody(); - expect(jsonRes.Status).to.eql('Success'); - }); - - test("User records are returned", function () { - var jsonRes = res.getBody(); - expect(jsonRes.Data.Items.length).greaterThan(0); - }); -} diff --git a/bruno/form-service/Question/Get Question By Template Id.bru b/bruno/form-service/Question/Get Question By Template Id.bru deleted file mode 100644 index d762e08..0000000 --- a/bruno/form-service/Question/Get Question By Template Id.bru +++ /dev/null @@ -1,11 +0,0 @@ -meta { - name: Get Question By Template Id - type: http - seq: 6 -} - -get { - url: {{BASE_URL}}/questions/by-template-id/{{TEMPLATE_ID}} - body: none - auth: none -} diff --git a/bruno/form-service/Question/Update a question record.bru b/bruno/form-service/Question/Update a question record.bru deleted file mode 100644 index 02368ec..0000000 --- a/bruno/form-service/Question/Update a question record.bru +++ /dev/null @@ -1,74 +0,0 @@ -meta { - name: Update a question record - type: http - seq: 3 -} - -put { - url: {{BASE_URL}}/questions/{{QUESTION_ID}} - body: json - auth: none -} - -body:json { - { - "Title": "Current (referral) cardiac diagnosis/diagnoses?", - "ResponseType": "MultiChoiceSelection", - "RangeMin": 1, - "RangeMax": 2, - // "Options": [ - // "Acute Coronary Syndrome - STEMI,Acute Coronary Syndrome - NSTEMI,Acute Coronary Syndrome - Unstable Angina,Stable Coronary Artery Disease or Stable Angina,Heart Failure,Atrial Fibrillation/Flutter/SVT,Peripheral Arterial Disease,Percutaneous Cardiac Intervention (TAVI/ASD Repair/Valvotomies),Heart Valve replacement,High cardiovascular risk patient (High CVD risk score, Diabetes, Metabolic syndrome, Chronic Kidney Disease, Multiple risk factors),Other" - // ] - "Options": [ - { - "Text": "pppp", - "Sequence": "1", - "ImageUrl": "asdasfdsf" - } - ] - } -} - -script:post-response { - test("Request is successfull", function () { - expect(res.getStatus()).to.equal(200); - var jsonRes = res.getBody(); - expect(jsonRes.Status).to.eql('Success'); - }); - - test("User is updated", function () { - var jsonRes = res.getBody(); - expect(jsonRes.Data).to.have.property('id'); - expect(jsonRes.Data.id).equals('006330fd-c768-4034-90a0-3aa8ce3475f4'); - expect(jsonRes.Data).to.have.property('ParentTemplateId'); - expect(jsonRes.Data.ParentTemplateId).equals('163bac66-4af0-40dc-a09f-bfbe866e7e89'); - expect(jsonRes.Data).to.have.property('ParentSectionId'); - expect(jsonRes.Data.ParentSectionId).equals('c99e1620-df7a-45ce-b374-325002706f5f'); - expect(jsonRes.Data).to.have.property('Title'); - expect(jsonRes.Data.Title).equals('Enter respiratory rate'); - expect(jsonRes.Data).to.have.property('Description'); - expect(jsonRes.Data.Description).equals('description'); - expect(jsonRes.Data).to.have.property('DisplayCode'); - expect(jsonRes.Data.DisplayCode).equals('QUESTION_#RTx8BEH6Zsv8t2V'); - expect(jsonRes.Data).to.have.property('ResponseType'); - expect(jsonRes.Data.ResponseType).equals('MultiChoiceSelection'); - expect(jsonRes.Data).to.have.property('Score'); - expect(jsonRes.Data.Score).equals(1); - expect(jsonRes.Data).to.have.property('Sequence'); - expect(jsonRes.Data.Sequence).equals('A2'); - expect(jsonRes.Data).to.have.property('CorrectAnswer'); - expect(jsonRes.Data.CorrectAnswer).equals('A'); - expect(jsonRes.Data).to.have.property('CorrectAnswer'); - expect(jsonRes.Data.CorrectAnswer).equals('CorrectAnswer'); - expect(jsonRes.Data).to.have.property('Hint'); - expect(jsonRes.Data.Hint).equals('Hint'); - expect(jsonRes.Data).to.have.property('Options'); - expect(jsonRes.Data.Options).equals('["HTML,CSS,JAVASCRIPT,JAVA,PYTHON,C,C++"]'); - expect(jsonRes.Data).to.have.property('QuestionImageUrl'); - expect(jsonRes.Data.QuestionImageUrl).equals('QuestionImageUrl'); - expect(jsonRes.Data).to.have.property('RangeMin'); - expect(jsonRes.Data.RangeMin).equals(1); - expect(jsonRes.Data).to.have.property('RangeMax'); - expect(jsonRes.Data.RangeMax).equals(2); - }); -} diff --git a/bruno/form-service/Template Folder/Create template folder.bru b/bruno/form-service/Template Folder/Create template folder.bru new file mode 100644 index 0000000..9a8666c --- /dev/null +++ b/bruno/form-service/Template Folder/Create template folder.bru @@ -0,0 +1,52 @@ +meta { + name: Create Template Folder + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/template-folders + body: json + auth: none +} + +body:json { + { + "Name": "Survey Forms", + "Description": "Collection of survey form templates", + "ParentFolderId": "c544da4e-4fe5-4a37-aa22-d47cdb547ff7" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("TEMPLATE_FOLDER_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Template Folder is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Survey Forms'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Collection of survey form templates'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data.DisplayCode).equals('SURVEY_FORMS'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('ParentFolderId'); + expect(jsonRes.Data.ParentFolderId).equals(null); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Template Folder/Delete template folder.bru b/bruno/form-service/Template Folder/Delete template folder.bru new file mode 100644 index 0000000..51b0167 --- /dev/null +++ b/bruno/form-service/Template Folder/Delete template folder.bru @@ -0,0 +1,24 @@ +meta { + name: Delete Template Folder + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/template-folders/{{TEMPLATE_FOLDER_ID}} + body: none + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Template Folder is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Template Folder/Get template folder by id.bru b/bruno/form-service/Template Folder/Get template folder by id.bru new file mode 100644 index 0000000..7f3d3b9 --- /dev/null +++ b/bruno/form-service/Template Folder/Get template folder by id.bru @@ -0,0 +1,31 @@ +meta { + name: Get Template Folder by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/template-folders/{{TEMPLATE_FOLDER_ID}} + auth: none +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Template Folder has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{TEMPLATE_FOLDER_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('ParentFolderId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Template Folder/Search template folders.bru b/bruno/form-service/Template Folder/Search template folders.bru new file mode 100644 index 0000000..461799d --- /dev/null +++ b/bruno/form-service/Template Folder/Search template folders.bru @@ -0,0 +1,55 @@ +meta { + name: Search Template Folders + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/template-folders/search?pageIndex=0 + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~Name: Survey + ~IsActive: true +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Template Folders search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Template Folders have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('DisplayCode'); + expect(item).to.have.property('IsActive'); + expect(item).to.have.property('ParentFolderId'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Template Folder/Update template folder.bru b/bruno/form-service/Template Folder/Update template folder.bru new file mode 100644 index 0000000..76f4011 --- /dev/null +++ b/bruno/form-service/Template Folder/Update template folder.bru @@ -0,0 +1,45 @@ +meta { + name: Update Template Folder + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/template-folders/{{TEMPLATE_FOLDER_ID}} + body: json + auth: none +} + +body:json { + { + "Name": "Updated Survey Forms", + "Description": "Updated collection of survey form templates", + "ParentFolderId": "c544da4e-4fe5-4a37-aa22-d47cdb547ff7" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Template Folder is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{TEMPLATE_FOLDER_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Survey Forms'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated collection of survey form templates'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data.DisplayCode).equals('SURVEY_FORMS_V2'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('ParentFolderId'); + expect(jsonRes.Data.ParentFolderId).equals(null); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/User Login Session/folder.bru b/bruno/form-service/User Login Session/folder.bru index 48a884f..568c82d 100644 --- a/bruno/form-service/User Login Session/folder.bru +++ b/bruno/form-service/User Login Session/folder.bru @@ -1,6 +1,6 @@ meta { name: User Login Session - seq: 6 + seq: 10 } auth { diff --git a/bruno/form-service/User/Generic search.bru b/bruno/form-service/User/Generic search.bru index d4d9dcf..13b6f80 100644 --- a/bruno/form-service/User/Generic search.bru +++ b/bruno/form-service/User/Generic search.bru @@ -11,6 +11,10 @@ get { } params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC username: "Username" password: "Password" } diff --git a/bruno/form-service/User/folder.bru b/bruno/form-service/User/folder.bru index b8e707b..1dd252e 100644 --- a/bruno/form-service/User/folder.bru +++ b/bruno/form-service/User/folder.bru @@ -1,6 +1,6 @@ meta { name: User - seq: 1 + seq: 4 } auth { diff --git a/bruno/form-service/environments/forms-service.bru b/bruno/form-service/environments/forms-service.bru index e3d3610..ddf418f 100644 --- a/bruno/form-service/environments/forms-service.bru +++ b/bruno/form-service/environments/forms-service.bru @@ -4,6 +4,7 @@ vars { TEMPLATE_ID: 7d6fe0be-3f6c-4a8e-8231-b8a7568d2c9d SECTION_ID: 1642c8d1-9849-4402-91f8-ae07bda27dd8 QUESTION_ID: + FORM_FIELD_ID: SUBMISSION_ID: RESPONSE_ID: PARENT_SECTION_ID: 44071647-f61e-448b-8dec-ae2c8d3017c6 diff --git a/package.json b/package.json index e2f2147..2ee753a 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "main": "index.js", "scripts": { "test": "mocha", - "build": "tsc" + "build": "tsc", + "start": "ts-node src/index.ts", + "dev": "ts-node src/index.ts" }, "repository": { "type": "git", diff --git a/src/api/field.logic/calculation.logic/calculation.logic.controller.ts b/src/api/field.logic/calculation.logic/calculation.logic.controller.ts new file mode 100644 index 0000000..27d438d --- /dev/null +++ b/src/api/field.logic/calculation.logic/calculation.logic.controller.ts @@ -0,0 +1,84 @@ +import express from 'express'; +import { ResponseHandler } from '../../../common/handlers/response.handler'; +import { BaseController } from '../../base.controller'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import { uuid } from '../../../domain.types/miscellaneous/system.types'; +import { CalculationLogicService } from '../../../services/field.logic/calculation.logic.service'; +import { CalculationLogicValidator } from './calculation.logic.validator'; +import { CalculationLogicCreateModel, CalculationLogicUpdateModel } from '../../../domain.types/forms/calculation.logic.domain.types'; +import { ApiError } from '../../../common/api.error'; +import { Injector } from '../../../startup/injector'; +import { LogicSearchFilters } from '../../../domain.types/forms/logic.domain.types'; + +export class CalculationLogicController extends BaseController { + _service: CalculationLogicService = Injector.Container.resolve(CalculationLogicService); + _validator: CalculationLogicValidator = new CalculationLogicValidator(); + + constructor() { + super(); + } + + // Calculation Logic operations + createCalculationLogic = async (request: express.Request, response: express.Response) => { + try { + const model: CalculationLogicCreateModel = await this._validator.validateCalculationLogicCreateRequest(request); + const record = await this._service.createCalculationLogic(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to create Calculation Logic!', new Error()); + } + const message = 'Calculation Logic created successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getCalculationLogicById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getCalculationLogicById(id); + if (!record) { + throw new ApiError('Calculation Logic not found!', 404); + } + const message = 'Calculation Logic retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + updateCalculationLogic = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: CalculationLogicUpdateModel = await this._validator.validateCalculationLogicUpdateRequest(request); + const updatedRecord = await this._service.updateCalculationLogic(id, model); + const message = 'Calculation Logic updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + deleteCalculationLogic = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.deleteCalculationLogic(id); + const message = 'Calculation Logic deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + searchCalculationLogic = async (request: express.Request, response: express.Response) => { + try { + const filters: LogicSearchFilters = await this._validator.validateLogicSearchRequest(request); + const searchResults = await this._service.searchCalculationLogic(filters); + const message = 'Calculation Logic search completed successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + +} \ No newline at end of file diff --git a/src/api/field.logic/calculation.logic/calculation.logic.router.ts b/src/api/field.logic/calculation.logic/calculation.logic.router.ts new file mode 100644 index 0000000..40c2025 --- /dev/null +++ b/src/api/field.logic/calculation.logic/calculation.logic.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import { CalculationLogicController } from './calculation.logic.controller'; + +/////////////////////////////////////////////////////////////////////////////////// + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new CalculationLogicController(); + + // Calculation Logic routes + router.get('/search', controller.searchCalculationLogic); + router.post('/', controller.createCalculationLogic); + router.put('/:id', controller.updateCalculationLogic); + router.get('/:id', controller.getCalculationLogicById); + router.delete('/:id', controller.deleteCalculationLogic); + + app.use('/api/v1/field-calculation-logic', router); +}; \ No newline at end of file diff --git a/src/api/field.logic/calculation.logic/calculation.logic.validator.ts b/src/api/field.logic/calculation.logic/calculation.logic.validator.ts new file mode 100644 index 0000000..8701803 --- /dev/null +++ b/src/api/field.logic/calculation.logic/calculation.logic.validator.ts @@ -0,0 +1,89 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import BaseValidator from '../../base.validator'; +import { + CalculationLogicCreateModel, + CalculationLogicUpdateModel, +} from '../../../domain.types/forms/calculation.logic.domain.types'; +import { LogicType } from '../../../domain.types/forms/logic.enums'; +import { LogicSearchFilters } from '../../../domain.types/forms/logic.domain.types'; + + +export class CalculationLogicValidator extends BaseValidator { + + // Calculation Logic validation + public validateCalculationLogicCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Type: joi.string().valid(LogicType.Calculation).required(), + FieldId: joi.string().uuid().required(), + Enabled: joi.boolean().optional(), + DefaultSkip: joi.boolean().optional(), + FallbackValue: joi.string().optional(), + }); + await schema.validateAsync(request.body); + return { + Type: request.body.Type, + FieldId: request.body.FieldId, + Enabled: request.body.Enabled ?? true, + DefaultSkip: request.body.DefaultSkip ?? false, + FallbackValue: request.body.FallbackValue + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateCalculationLogicUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Type: joi.string().valid(LogicType.Calculation).optional(), + FieldId: joi.string().uuid().optional(), + Enabled: joi.boolean().optional(), + DefaultSkip: joi.boolean().optional(), + FallbackValue: joi.string().optional() + }); + await schema.validateAsync(request.body); + return { + Type: request.body.Type, + FieldId: request.body.FieldId, + Enabled: request.body.Enabled, + DefaultSkip: request.body.DefaultSkip, + FallbackValue: request.body.FallbackValue + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateLogicSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + type: joi.string().valid(LogicType.Calculation).optional(), + fieldId: joi.string().uuid().optional(), + enabled: joi.boolean().optional(), + defaultSkip: joi.boolean().optional(), + PageIndex: joi.number().integer().min(0).optional(), + ItemsPerPage: joi.number().integer().min(1).max(100).optional(), + OrderBy: joi.string().optional(), + Order: joi.string().valid('ASC', 'DESC').optional(), + }); + await schema.validateAsync(request.query); + return { + id: request.query.id as string, + type: request.query.type as LogicType, + fieldId: request.query.fieldId as string, + enabled: request.query.enabled === 'true' ? true : request.query.enabled === 'false' ? false : undefined, + defaultSkip: request.query.defaultSkip === 'true' ? true : request.query.defaultSkip === 'false' ? false : undefined, + PageIndex: request.query.PageIndex ? parseInt(request.query.PageIndex as string) : 0, + ItemsPerPage: request.query.ItemsPerPage ? parseInt(request.query.ItemsPerPage as string) : 10, + OrderBy: request.query.OrderBy as string, + Order: request.query.Order as 'ASC' | 'DESC', + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; +} \ No newline at end of file diff --git a/src/api/field.logic/skip.logic/skip.logic.controller.ts b/src/api/field.logic/skip.logic/skip.logic.controller.ts new file mode 100644 index 0000000..da9429f --- /dev/null +++ b/src/api/field.logic/skip.logic/skip.logic.controller.ts @@ -0,0 +1,83 @@ +import express from 'express'; +import { ResponseHandler } from '../../../common/handlers/response.handler'; +import { BaseController } from '../../base.controller'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import { uuid } from '../../../domain.types/miscellaneous/system.types'; +import { SkipLogicService } from '../../../services/field.logic/skip.logic.service'; +import { SkipLogicValidator } from './skip.logic.validator'; +import { SkipLogicCreateModel, SkipLogicUpdateModel, LogicSearchFilters } from '../../../domain.types/forms/logic.domain.types'; +import { ApiError } from '../../../common/api.error'; +import { Injector } from '../../../startup/injector'; + +export class SkipLogicController extends BaseController { + _service: SkipLogicService = Injector.Container.resolve(SkipLogicService); + _validator: SkipLogicValidator = new SkipLogicValidator(); + + constructor() { + super(); + } + + // Skip Logic operations + createSkipLogic = async (request: express.Request, response: express.Response) => { + try { + const model: SkipLogicCreateModel = await this._validator.validateSkipLogicCreateRequest(request); + const record = await this._service.createSkipLogic(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to create Skip Logic!', new Error()); + } + const message = 'Skip Logic created successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getSkipLogicById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getSkipLogicById(id); + if (!record) { + throw new ApiError('Skip Logic not found!', 404); + } + const message = 'Skip Logic retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + updateSkipLogic = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: SkipLogicUpdateModel = await this._validator.validateSkipLogicUpdateRequest(request); + const updatedRecord = await this._service.updateSkipLogic(id, model); + const message = 'Skip Logic updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + deleteSkipLogic = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.deleteSkipLogic(id); + const message = 'Skip Logic deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + searchSkipLogic = async (request: express.Request, response: express.Response) => { + try { + const filters: LogicSearchFilters = await this._validator.validateLogicSearchRequest(request); + const searchResults = await this._service.searchSkipLogic(filters); + const message = 'Skip Logic search completed successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + +} \ No newline at end of file diff --git a/src/api/field.logic/skip.logic/skip.logic.router.ts b/src/api/field.logic/skip.logic/skip.logic.router.ts new file mode 100644 index 0000000..c168836 --- /dev/null +++ b/src/api/field.logic/skip.logic/skip.logic.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import { SkipLogicController } from './skip.logic.controller'; + +/////////////////////////////////////////////////////////////////////////////////// + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new SkipLogicController(); + + // Skip Logic routes + router.get('/search', controller.searchSkipLogic); + router.post('/', controller.createSkipLogic); + router.put('/:id', controller.updateSkipLogic); + router.get('/:id', controller.getSkipLogicById); + router.delete('/:id', controller.deleteSkipLogic); + + app.use('/api/v1/field-skip-logic', router); +}; \ No newline at end of file diff --git a/src/api/field.logic/skip.logic/skip.logic.validator.ts b/src/api/field.logic/skip.logic/skip.logic.validator.ts new file mode 100644 index 0000000..76edb44 --- /dev/null +++ b/src/api/field.logic/skip.logic/skip.logic.validator.ts @@ -0,0 +1,85 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import BaseValidator from '../../base.validator'; +import { + SkipLogicCreateModel, + SkipLogicUpdateModel, + LogicSearchFilters +} from '../../../domain.types/forms/logic.domain.types'; +import { LogicType } from '../../../domain.types/forms/logic.enums'; + + +export class SkipLogicValidator extends BaseValidator { + + // Skip Logic validation + public validateSkipLogicCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Type: joi.string().valid(LogicType.Skip).required(), + FieldId: joi.string().uuid().required(), + Enabled: joi.boolean().optional(), + DefaultSkip: joi.boolean().optional(), + }); + await schema.validateAsync(request.body); + return { + Type: request.body.Type, + FieldId: request.body.FieldId, + Enabled: request.body.Enabled ?? true, + DefaultSkip: request.body.DefaultSkip ?? false + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateSkipLogicUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Type: joi.string().valid(LogicType.Skip).optional(), + FieldId: joi.string().uuid().optional(), + Enabled: joi.boolean().optional(), + DefaultSkip: joi.boolean().optional() + }); + await schema.validateAsync(request.body); + return { + Type: request.body.Type, + FieldId: request.body.FieldId, + Enabled: request.body.Enabled, + DefaultSkip: request.body.DefaultSkip + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateLogicSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + type: joi.string().valid(LogicType.Skip).optional(), + fieldId: joi.string().uuid().optional(), + enabled: joi.boolean().optional(), + defaultSkip: joi.boolean().optional(), + PageIndex: joi.number().integer().min(0).optional(), + ItemsPerPage: joi.number().integer().min(1).max(100).optional(), + OrderBy: joi.string().optional(), + Order: joi.string().valid('ASC', 'DESC').optional(), + }); + await schema.validateAsync(request.query); + return { + id: request.query.id as string, + type: request.query.type as LogicType, + fieldId: request.query.fieldId as string, + enabled: request.query.enabled === 'true' ? true : request.query.enabled === 'false' ? false : undefined, + defaultSkip: request.query.defaultSkip === 'true' ? true : request.query.defaultSkip === 'false' ? false : undefined, + PageIndex: request.query.PageIndex ? parseInt(request.query.PageIndex as string) : 0, + ItemsPerPage: request.query.ItemsPerPage ? parseInt(request.query.ItemsPerPage as string) : 10, + OrderBy: request.query.OrderBy as string, + Order: request.query.Order as 'ASC' | 'DESC', + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; +} \ No newline at end of file diff --git a/src/api/field.logic/validation.logic/validation.logic.controller.ts b/src/api/field.logic/validation.logic/validation.logic.controller.ts new file mode 100644 index 0000000..d976e52 --- /dev/null +++ b/src/api/field.logic/validation.logic/validation.logic.controller.ts @@ -0,0 +1,83 @@ +import express from 'express'; +import { ResponseHandler } from '../../../common/handlers/response.handler'; +import { BaseController } from '../../base.controller'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import { uuid } from '../../../domain.types/miscellaneous/system.types'; +import { ValidationLogicService } from '../../../services/field.logic/validation.logic.service'; +import { ValidationLogicCreateModel, ValidationLogicUpdateModel, LogicSearchFilters } from '../../../domain.types/forms/logic.domain.types'; +import { ApiError } from '../../../common/api.error'; +import { Injector } from '../../../startup/injector'; +import { ValidationLogicValidator } from './validation.logic.validator'; + +export class ValidationLogicController extends BaseController { + _service: ValidationLogicService = Injector.Container.resolve(ValidationLogicService); + _validator: ValidationLogicValidator = new ValidationLogicValidator(); + + constructor() { + super(); + } + + // Validation Logic operations + createValidationLogic = async (request: express.Request, response: express.Response) => { + try { + const model: ValidationLogicCreateModel = await this._validator.validateValidationLogicCreateRequest(request); + const record = await this._service.createValidationLogic(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to create Validation Logic!', new Error()); + } + const message = 'Validation Logic created successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getValidationLogicById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getValidationLogicById(id); + if (!record) { + throw new ApiError('Validation Logic not found!', 404); + } + const message = 'Validation Logic retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + updateValidationLogic = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: ValidationLogicUpdateModel = await this._validator.validateValidationLogicUpdateRequest(request); + const updatedRecord = await this._service.updateValidationLogic(id, model); + const message = 'Validation Logic updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + deleteValidationLogic = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.deleteValidationLogic(id); + const message = 'Validation Logic deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + searchValidationLogic = async (request: express.Request, response: express.Response) => { + try { + const filters: LogicSearchFilters = await this._validator.validateLogicSearchRequest(request); + const searchResults = await this._service.searchValidationLogic(filters); + const message = 'Validation Logic search completed successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + +} \ No newline at end of file diff --git a/src/api/field.logic/validation.logic/validation.logic.router.ts b/src/api/field.logic/validation.logic/validation.logic.router.ts new file mode 100644 index 0000000..1498fe1 --- /dev/null +++ b/src/api/field.logic/validation.logic/validation.logic.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import { ValidationLogicController } from './validation.logic.controller'; + +/////////////////////////////////////////////////////////////////////////////////// + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new ValidationLogicController(); + + // Validation Logic routes + router.get('/search', controller.searchValidationLogic); + router.post('/', controller.createValidationLogic); + router.put('/:id', controller.updateValidationLogic); + router.get('/:id', controller.getValidationLogicById); + router.delete('/:id', controller.deleteValidationLogic); + + app.use('/api/v1/field-validation-logic', router); +}; \ No newline at end of file diff --git a/src/api/field.logic/validation.logic/validation.logic.validator.ts b/src/api/field.logic/validation.logic/validation.logic.validator.ts new file mode 100644 index 0000000..f16c398 --- /dev/null +++ b/src/api/field.logic/validation.logic/validation.logic.validator.ts @@ -0,0 +1,85 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import BaseValidator from '../../base.validator'; +import { + ValidationLogicCreateModel, + ValidationLogicUpdateModel, + LogicSearchFilters +} from '../../../domain.types/forms/logic.domain.types'; +import { LogicType } from '../../../domain.types/forms/logic.enums'; + + +export class ValidationLogicValidator extends BaseValidator { + + // Validation Logic validation + public validateValidationLogicCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Type: joi.string().valid(LogicType.Validation).required(), + FieldId: joi.string().uuid().required(), + Enabled: joi.boolean().optional(), + DefaultSkip: joi.boolean().optional(), + }); + await schema.validateAsync(request.body); + return { + Type: request.body.Type, + FieldId: request.body.FieldId, + Enabled: request.body.Enabled ?? true, + DefaultSkip: request.body.DefaultSkip ?? false + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateValidationLogicUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Type: joi.string().valid(LogicType.Validation).optional(), + FieldId: joi.string().uuid().optional(), + Enabled: joi.boolean().optional(), + DefaultSkip: joi.boolean().optional() + }); + await schema.validateAsync(request.body); + return { + Type: request.body.Type, + FieldId: request.body.FieldId, + Enabled: request.body.Enabled, + DefaultSkip: request.body.DefaultSkip + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateLogicSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + type: joi.string().valid(LogicType.Validation).optional(), + fieldId: joi.string().uuid().optional(), + enabled: joi.boolean().optional(), + defaultSkip: joi.boolean().optional(), + PageIndex: joi.number().integer().min(0).optional(), + ItemsPerPage: joi.number().integer().min(1).max(100).optional(), + OrderBy: joi.string().optional(), + Order: joi.string().valid('ASC', 'DESC').optional(), + }); + await schema.validateAsync(request.query); + return { + id: request.query.id as string, + type: request.query.type as LogicType, + fieldId: request.query.fieldId as string, + enabled: request.query.enabled === 'true' ? true : request.query.enabled === 'false' ? false : undefined, + defaultSkip: request.query.defaultSkip === 'true' ? true : request.query.defaultSkip === 'false' ? false : undefined, + PageIndex: request.query.PageIndex ? parseInt(request.query.PageIndex as string) : 0, + ItemsPerPage: request.query.ItemsPerPage ? parseInt(request.query.ItemsPerPage as string) : 10, + OrderBy: request.query.OrderBy as string, + Order: request.query.Order as 'ASC' | 'DESC', + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; +} \ No newline at end of file diff --git a/src/api/field.operations/composition.operation/composition.operation.controller.ts b/src/api/field.operations/composition.operation/composition.operation.controller.ts new file mode 100644 index 0000000..295041a --- /dev/null +++ b/src/api/field.operations/composition.operation/composition.operation.controller.ts @@ -0,0 +1,84 @@ +import express from 'express'; +import { ResponseHandler } from '../../../common/handlers/response.handler'; +import { BaseController } from '../../base.controller'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import { uuid } from '../../../domain.types/miscellaneous/system.types'; +// import { CompositionOperationService } from '../../../services/field.operations/composition.operation.service'; +import { CompositionOperationCreateModel, CompositionOperationUpdateModel, OperationSearchFilters } from '../../../domain.types/forms/operation.domain.types'; +import { ApiError } from '../../../common/api.error'; +import { Injector } from '../../../startup/injector'; +import { CompositionOperationValidator } from './composition.operation.validator'; +import { CompositionOperationService } from '../../../services/field.operations/composition.operation.service'; + +export class CompositionOperationController extends BaseController { + _service: CompositionOperationService = Injector.Container.resolve(CompositionOperationService); + _validator: CompositionOperationValidator = new CompositionOperationValidator(); + + constructor() { + super(); + } + + // Composition Operation operations + createCompositionOperation = async (request: express.Request, response: express.Response) => { + try { + const model: CompositionOperationCreateModel = await this._validator.validateCompositionOperationCreateRequest(request); + const record = await this._service.createCompositionOperation(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to create Composition Operation!', new Error()); + } + const message = 'Composition Operation created successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getCompositionOperationById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getCompositionOperationById(id); + if (!record) { + throw new ApiError('Composition Operation not found!', 404); + } + const message = 'Composition Operation retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + updateCompositionOperation = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: CompositionOperationUpdateModel = await this._validator.validateCompositionOperationUpdateRequest(request); + const updatedRecord = await this._service.updateCompositionOperation(id, model); + const message = 'Composition Operation updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + deleteCompositionOperation = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.deleteCompositionOperation(id); + const message = 'Composition Operation deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + searchCompositionOperation = async (request: express.Request, response: express.Response) => { + try { + const filters: OperationSearchFilters = await this._validator.validateOperationSearchRequest(request); + const searchResults = await this._service.searchCompositionOperation(filters); + const message = 'Composition Operation search completed successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + +} \ No newline at end of file diff --git a/src/api/field.operations/composition.operation/composition.operation.router.ts b/src/api/field.operations/composition.operation/composition.operation.router.ts new file mode 100644 index 0000000..2a2d290 --- /dev/null +++ b/src/api/field.operations/composition.operation/composition.operation.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import { CompositionOperationController } from './composition.operation.controller'; + +/////////////////////////////////////////////////////////////////////////////////// + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new CompositionOperationController(); + + // Composition Operation routes + router.get('/search', controller.searchCompositionOperation); + router.post('/', controller.createCompositionOperation); + router.put('/:id', controller.updateCompositionOperation); + router.get('/:id', controller.getCompositionOperationById); + router.delete('/:id', controller.deleteCompositionOperation); + + app.use('/api/v1/field-composition-operations', router); +}; \ No newline at end of file diff --git a/src/api/field.operations/composition.operation/composition.operation.validator.ts b/src/api/field.operations/composition.operation/composition.operation.validator.ts new file mode 100644 index 0000000..a917215 --- /dev/null +++ b/src/api/field.operations/composition.operation/composition.operation.validator.ts @@ -0,0 +1,87 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import BaseValidator from '../../base.validator'; +import { + CompositionOperationCreateModel, + CompositionOperationUpdateModel, + OperationSearchFilters +} from '../../../domain.types/forms/operation.domain.types'; +import { CompositionOperatorType, OperationType } from '../../../domain.types/forms/operation.enums'; + + +export class CompositionOperationValidator extends BaseValidator { + + // Composition Operation validation + public validateCompositionOperationCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Type: joi.string().valid(OperationType.Composition).optional(), + Operator: joi.string().valid(...Object.values(CompositionOperatorType)).required(), + Operands: joi.string().required(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Type: OperationType.Composition, // Always set to Composition for this operation type + Operator: request.body.Operator, + Operands: request.body.Operands + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateCompositionOperationUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Type: joi.string().valid(OperationType.Composition).optional(), + Operator: joi.string().valid(...Object.values(CompositionOperatorType)).optional(), + Operands: joi.string().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Type: OperationType.Composition, // Always set to Composition for this operation type + Operator: request.body.Operator, + Operands: request.body.Operands + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateOperationSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + name: joi.string().optional(), + description: joi.string().optional(), + operator: joi.string().valid(...Object.values(CompositionOperatorType)).optional(), + PageIndex: joi.number().integer().min(0).optional(), + ItemsPerPage: joi.number().integer().min(1).max(100).optional(), + OrderBy: joi.string().optional(), + Order: joi.string().valid('ASC', 'DESC').optional(), + }); + await schema.validateAsync(request.query); + return { + id: request.query.id as string, + name: request.query.name as string, + description: request.query.description as string, + operator: request.query.operator as CompositionOperatorType, + PageIndex: request.query.PageIndex ? parseInt(request.query.PageIndex as string) : 0, + ItemsPerPage: request.query.ItemsPerPage ? parseInt(request.query.ItemsPerPage as string) : 10, + OrderBy: request.query.OrderBy as string, + Order: request.query.Order as 'ASC' | 'DESC', + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; +} \ No newline at end of file diff --git a/src/api/field.operations/function.expression.operation/function.expression.operation.controller.ts b/src/api/field.operations/function.expression.operation/function.expression.operation.controller.ts new file mode 100644 index 0000000..499ab67 --- /dev/null +++ b/src/api/field.operations/function.expression.operation/function.expression.operation.controller.ts @@ -0,0 +1,84 @@ +import express from 'express'; +import { ResponseHandler } from '../../../common/handlers/response.handler'; +import { BaseController } from '../../base.controller'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import { uuid } from '../../../domain.types/miscellaneous/system.types'; +// import { FunctionExpressionOperationService } from '../../../services/field.operations/function.expression.operation.service'; +import { FunctionExpressionOperationCreateModel, FunctionExpressionOperationUpdateModel, OperationSearchFilters } from '../../../domain.types/forms/operation.domain.types'; +import { ApiError } from '../../../common/api.error'; +import { Injector } from '../../../startup/injector'; +import { FunctionExpressionOperationValidator } from './function.expression.operation.validator'; +import { FunctionExpressionOperationService } from '../../../services/field.operations/function.expression.operation.service'; + +export class FunctionExpressionOperationController extends BaseController { + _service: FunctionExpressionOperationService = Injector.Container.resolve(FunctionExpressionOperationService); + _validator: FunctionExpressionOperationValidator = new FunctionExpressionOperationValidator(); + + constructor() { + super(); + } + + // Function Expression Operation operations + createFunctionExpressionOperation = async (request: express.Request, response: express.Response) => { + try { + const model: FunctionExpressionOperationCreateModel = await this._validator.validateFunctionExpressionOperationCreateRequest(request); + const record = await this._service.createFunctionExpressionOperation(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to create Function Expression Operation!', new Error()); + } + const message = 'Function Expression Operation created successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getFunctionExpressionOperationById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getFunctionExpressionOperationById(id); + if (!record) { + throw new ApiError('Function Expression Operation not found!', 404); + } + const message = 'Function Expression Operation retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + updateFunctionExpressionOperation = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: FunctionExpressionOperationUpdateModel = await this._validator.validateFunctionExpressionOperationUpdateRequest(request); + const updatedRecord = await this._service.updateFunctionExpressionOperation(id, model); + const message = 'Function Expression Operation updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + deleteFunctionExpressionOperation = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.deleteFunctionExpressionOperation(id); + const message = 'Function Expression Operation deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + searchFunctionExpressionOperation = async (request: express.Request, response: express.Response) => { + try { + const filters: OperationSearchFilters = await this._validator.validateOperationSearchRequest(request); + const searchResults = await this._service.searchFunctionExpressionOperation(filters); + const message = 'Function Expression Operation search completed successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + +} \ No newline at end of file diff --git a/src/api/field.operations/function.expression.operation/function.expression.operation.router.ts b/src/api/field.operations/function.expression.operation/function.expression.operation.router.ts new file mode 100644 index 0000000..209051d --- /dev/null +++ b/src/api/field.operations/function.expression.operation/function.expression.operation.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import { FunctionExpressionOperationController } from './function.expression.operation.controller'; + +/////////////////////////////////////////////////////////////////////////////////// + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new FunctionExpressionOperationController(); + + // Function Expression Operation routes + router.get('/search', controller.searchFunctionExpressionOperation); + router.post('/', controller.createFunctionExpressionOperation); + router.put('/:id', controller.updateFunctionExpressionOperation); + router.get('/:id', controller.getFunctionExpressionOperationById); + router.delete('/:id', controller.deleteFunctionExpressionOperation); + + app.use('/api/v1/field-function-expression-operations', router); +}; \ No newline at end of file diff --git a/src/api/field.operations/function.expression.operation/function.expression.operation.validator.ts b/src/api/field.operations/function.expression.operation/function.expression.operation.validator.ts new file mode 100644 index 0000000..9f1786b --- /dev/null +++ b/src/api/field.operations/function.expression.operation/function.expression.operation.validator.ts @@ -0,0 +1,89 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import BaseValidator from '../../base.validator'; +import { + FunctionExpressionOperationCreateModel, + FunctionExpressionOperationUpdateModel, + OperationSearchFilters +} from '../../../domain.types/forms/operation.domain.types'; +import { OperationType } from '../../../domain.types/forms/operation.enums'; + + +export class FunctionExpressionOperationValidator extends BaseValidator { + + // Function Expression Operation validation + public validateFunctionExpressionOperationCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Type: joi.string().valid(OperationType.FunctionExpression).optional(), + Expression: joi.string().required(), + Variables: joi.string().required(), + ResultDataType: joi.string().required(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Type: OperationType.FunctionExpression, // Always set to FunctionExpression for this operation type + Expression: request.body.Expression, + Variables: request.body.Variables, + ResultDataType: request.body.ResultDataType + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateFunctionExpressionOperationUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Type: joi.string().valid(OperationType.FunctionExpression).optional(), + Expression: joi.string().optional(), + Variables: joi.string().optional(), + ResultDataType: joi.string().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Type: OperationType.FunctionExpression, // Always set to FunctionExpression for this operation type + Expression: request.body.Expression, + Variables: request.body.Variables, + ResultDataType: request.body.ResultDataType + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateOperationSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + name: joi.string().optional(), + description: joi.string().optional(), + PageIndex: joi.number().integer().min(0).optional(), + ItemsPerPage: joi.number().integer().min(1).max(100).optional(), + OrderBy: joi.string().optional(), + Order: joi.string().valid('ASC', 'DESC').optional(), + }); + await schema.validateAsync(request.query); + return { + id: request.query.id as string, + name: request.query.name as string, + description: request.query.description as string, + PageIndex: request.query.PageIndex ? parseInt(request.query.PageIndex as string) : 0, + ItemsPerPage: request.query.ItemsPerPage ? parseInt(request.query.ItemsPerPage as string) : 10, + OrderBy: request.query.OrderBy as string, + Order: request.query.Order as 'ASC' | 'DESC', + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; +} \ No newline at end of file diff --git a/src/api/field.operations/iterate.operation/iterate.operation.controller.ts b/src/api/field.operations/iterate.operation/iterate.operation.controller.ts new file mode 100644 index 0000000..c763553 --- /dev/null +++ b/src/api/field.operations/iterate.operation/iterate.operation.controller.ts @@ -0,0 +1,83 @@ +import express from 'express'; +import { ResponseHandler } from '../../../common/handlers/response.handler'; +import { BaseController } from '../../base.controller'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import { uuid } from '../../../domain.types/miscellaneous/system.types'; +import { IterateOperationService } from '../../../services/field.operations/iterate.operation.service'; +import { IterateOperationCreateModel, IterateOperationUpdateModel, OperationSearchFilters } from '../../../domain.types/forms/operation.domain.types'; +import { ApiError } from '../../../common/api.error'; +import { Injector } from '../../../startup/injector'; +import { IterateOperationValidator } from './iterate.operation.validator'; + +export class IterateOperationController extends BaseController { + _service: IterateOperationService = Injector.Container.resolve(IterateOperationService); + _validator: IterateOperationValidator = new IterateOperationValidator(); + + constructor() { + super(); + } + + // Iterate Operation operations + createIterateOperation = async (request: express.Request, response: express.Response) => { + try { + const model: IterateOperationCreateModel = await this._validator.validateIterateOperationCreateRequest(request); + const record = await this._service.createIterateOperation(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to create Iterate Operation!', new Error()); + } + const message = 'Iterate Operation created successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getIterateOperationById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getIterateOperationById(id); + if (!record) { + throw new ApiError('Iterate Operation not found!', 404); + } + const message = 'Iterate Operation retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + updateIterateOperation = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: IterateOperationUpdateModel = await this._validator.validateIterateOperationUpdateRequest(request); + const updatedRecord = await this._service.updateIterateOperation(id, model); + const message = 'Iterate Operation updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + deleteIterateOperation = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.deleteIterateOperation(id); + const message = 'Iterate Operation deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + searchIterateOperation = async (request: express.Request, response: express.Response) => { + try { + const filters: OperationSearchFilters = await this._validator.validateOperationSearchRequest(request); + const searchResults = await this._service.searchIterateOperation(filters); + const message = 'Iterate Operation search completed successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + +} \ No newline at end of file diff --git a/src/api/field.operations/iterate.operation/iterate.operation.router.ts b/src/api/field.operations/iterate.operation/iterate.operation.router.ts new file mode 100644 index 0000000..c9461dd --- /dev/null +++ b/src/api/field.operations/iterate.operation/iterate.operation.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import { IterateOperationController } from './iterate.operation.controller'; + +/////////////////////////////////////////////////////////////////////////////////// + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new IterateOperationController(); + + // Iterate Operation routes + router.get('/search', controller.searchIterateOperation); + router.post('/', controller.createIterateOperation); + router.put('/:id', controller.updateIterateOperation); + router.get('/:id', controller.getIterateOperationById); + router.delete('/:id', controller.deleteIterateOperation); + + app.use('/api/v1/field-iterate-operations', router); +}; \ No newline at end of file diff --git a/src/api/field.operations/iterate.operation/iterate.operation.validator.ts b/src/api/field.operations/iterate.operation/iterate.operation.validator.ts new file mode 100644 index 0000000..d2a3bf8 --- /dev/null +++ b/src/api/field.operations/iterate.operation/iterate.operation.validator.ts @@ -0,0 +1,90 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import BaseValidator from '../../base.validator'; +import { + IterateOperationCreateModel, + IterateOperationUpdateModel, + OperationSearchFilters +} from '../../../domain.types/forms/operation.domain.types'; +import { OperationType } from '../../../domain.types/forms/operation.enums'; + + +export class IterateOperationValidator extends BaseValidator { + + // Iterate Operation validation + public validateIterateOperationCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + CollectionField: joi.string().required(), + ResultField: joi.string().required(), + OperationId: joi.string().uuid().required(), + FilterExpression: joi.string().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Type: OperationType.Iterate, // Always set to Iterate for this operation type + CollectionField: request.body.CollectionField, + ResultField: request.body.ResultField, + OperationId: request.body.OperationId, + FilterExpression: request.body.FilterExpression + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateIterateOperationUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + CollectionField: joi.string().optional(), + ResultField: joi.string().optional(), + OperationId: joi.string().uuid().optional(), + FilterExpression: joi.string().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + CollectionField: request.body.CollectionField, + ResultField: request.body.ResultField, + OperationId: request.body.OperationId, + FilterExpression: request.body.FilterExpression + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateOperationSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + name: joi.string().optional(), + description: joi.string().optional(), + PageIndex: joi.number().integer().min(0).optional(), + ItemsPerPage: joi.number().integer().min(1).max(100).optional(), + OrderBy: joi.string().optional(), + Order: joi.string().valid('ASC', 'DESC').optional(), + }); + await schema.validateAsync(request.query); + return { + id: request.query.id as string, + name: request.query.name as string, + description: request.query.description as string, + PageIndex: request.query.PageIndex ? parseInt(request.query.PageIndex as string) : 0, + ItemsPerPage: request.query.ItemsPerPage ? parseInt(request.query.ItemsPerPage as string) : 10, + OrderBy: request.query.OrderBy as string, + Order: request.query.Order as 'ASC' | 'DESC', + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; +} \ No newline at end of file diff --git a/src/api/field.operations/logical.operation/logical.operation.controller.ts b/src/api/field.operations/logical.operation/logical.operation.controller.ts new file mode 100644 index 0000000..b29f0fb --- /dev/null +++ b/src/api/field.operations/logical.operation/logical.operation.controller.ts @@ -0,0 +1,84 @@ +import express from 'express'; +import { ResponseHandler } from '../../../common/handlers/response.handler'; +import { BaseController } from '../../base.controller'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import { uuid } from '../../../domain.types/miscellaneous/system.types'; +// import { LogicalOperationService } from '../../../services/field.operations/logical.operation.service'; +import { LogicalOperationValidator } from './logical.operation.validator'; +import { LogicalOperationCreateModel, LogicalOperationUpdateModel, OperationSearchFilters } from '../../../domain.types/forms/operation.domain.types'; +import { ApiError } from '../../../common/api.error'; +import { Injector } from '../../../startup/injector'; +import { LogicalOperationService } from '../../../services/field.operations/logical.operation.service'; + +export class LogicalOperationController extends BaseController { + _service: LogicalOperationService = Injector.Container.resolve(LogicalOperationService); + _validator: LogicalOperationValidator = new LogicalOperationValidator(); + + constructor() { + super(); + } + + // Logical Operation operations + createLogicalOperation = async (request: express.Request, response: express.Response) => { + try { + const model: LogicalOperationCreateModel = await this._validator.validateLogicalOperationCreateRequest(request); + const record = await this._service.createLogicalOperation(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to create Logical Operation!', new Error()); + } + const message = 'Logical Operation created successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getLogicalOperationById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getLogicalOperationById(id); + if (!record) { + throw new ApiError('Logical Operation not found!', 404); + } + const message = 'Logical Operation retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + updateLogicalOperation = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: LogicalOperationUpdateModel = await this._validator.validateLogicalOperationUpdateRequest(request); + const updatedRecord = await this._service.updateLogicalOperation(id, model); + const message = 'Logical Operation updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + deleteLogicalOperation = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.deleteLogicalOperation(id); + const message = 'Logical Operation deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + searchLogicalOperation = async (request: express.Request, response: express.Response) => { + try { + const filters: OperationSearchFilters = await this._validator.validateOperationSearchRequest(request); + const searchResults = await this._service.searchLogicalOperation(filters); + const message = 'Logical Operation search completed successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + +} \ No newline at end of file diff --git a/src/api/field.operations/logical.operation/logical.operation.router.ts b/src/api/field.operations/logical.operation/logical.operation.router.ts new file mode 100644 index 0000000..8ce8c09 --- /dev/null +++ b/src/api/field.operations/logical.operation/logical.operation.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import { LogicalOperationController } from './logical.operation.controller'; + +/////////////////////////////////////////////////////////////////////////////////// + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new LogicalOperationController(); + + // Logical Operation routes + router.get('/search', controller.searchLogicalOperation); + router.post('/', controller.createLogicalOperation); + router.put('/:id', controller.updateLogicalOperation); + router.get('/:id', controller.getLogicalOperationById); + router.delete('/:id', controller.deleteLogicalOperation); + + app.use('/api/v1/field-logical-operations', router); +}; \ No newline at end of file diff --git a/src/api/field.operations/logical.operation/logical.operation.validator.ts b/src/api/field.operations/logical.operation/logical.operation.validator.ts new file mode 100644 index 0000000..0cc70d9 --- /dev/null +++ b/src/api/field.operations/logical.operation/logical.operation.validator.ts @@ -0,0 +1,84 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import BaseValidator from '../../base.validator'; +import { + LogicalOperationCreateModel, + LogicalOperationUpdateModel, + OperationSearchFilters +} from '../../../domain.types/forms/operation.domain.types'; +import { LogicalOperatorType, OperationType } from '../../../domain.types/forms/operation.enums'; + + +export class LogicalOperationValidator extends BaseValidator { + + // Logical Operation validation + public validateLogicalOperationCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Operator: joi.string().valid(...Object.values(LogicalOperatorType)).required(), + Operands: joi.string().required(), + }); + await schema.validateAsync(request.body); + return { + Type: OperationType.Logical, + Name: request.body.Name, + Description: request.body.Description, + Operator: request.body.Operator, + Operands: request.body.Operands + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateLogicalOperationUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Operator: joi.string().valid(...Object.values(LogicalOperatorType)).optional(), + Operands: joi.string().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Operator: request.body.Operator, + Operands: request.body.Operands + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateOperationSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + name: joi.string().optional(), + description: joi.string().optional(), + operator: joi.string().valid(...Object.values(LogicalOperatorType)).optional(), + PageIndex: joi.number().integer().min(0).optional(), + ItemsPerPage: joi.number().integer().min(1).max(100).optional(), + OrderBy: joi.string().optional(), + Order: joi.string().valid('ASC', 'DESC').optional(), + }); + await schema.validateAsync(request.query); + return { + id: request.query.id as string, + name: request.query.name as string, + description: request.query.description as string, + operator: request.query.operator as LogicalOperatorType, + PageIndex: request.query.PageIndex ? parseInt(request.query.PageIndex as string) : 0, + ItemsPerPage: request.query.ItemsPerPage ? parseInt(request.query.ItemsPerPage as string) : 10, + OrderBy: request.query.OrderBy as string, + Order: request.query.Order as 'ASC' | 'DESC', + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; +} \ No newline at end of file diff --git a/src/api/field.operations/mathematical.operation/mathematical.operation.controller.ts b/src/api/field.operations/mathematical.operation/mathematical.operation.controller.ts new file mode 100644 index 0000000..ad23d84 --- /dev/null +++ b/src/api/field.operations/mathematical.operation/mathematical.operation.controller.ts @@ -0,0 +1,83 @@ +import express from 'express'; +import { ResponseHandler } from '../../../common/handlers/response.handler'; +import { BaseController } from '../../base.controller'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import { uuid } from '../../../domain.types/miscellaneous/system.types'; +import { MathematicalOperationService } from '../../../services/field.operations/mathematical.operation.service'; +import { MathematicalOperationCreateModel, MathematicalOperationUpdateModel, OperationSearchFilters } from '../../../domain.types/forms/operation.domain.types'; +import { ApiError } from '../../../common/api.error'; +import { Injector } from '../../../startup/injector'; +import { MathematicalOperationValidator } from './mathematical.operation.validator'; + +export class MathematicalOperationController extends BaseController { + _service: MathematicalOperationService = Injector.Container.resolve(MathematicalOperationService); + _validator: MathematicalOperationValidator = new MathematicalOperationValidator(); + + constructor() { + super(); + } + + // Mathematical Operation operations + createMathematicalOperation = async (request: express.Request, response: express.Response) => { + try { + const model: MathematicalOperationCreateModel = await this._validator.validateMathematicalOperationCreateRequest(request); + const record = await this._service.createMathematicalOperation(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to create Mathematical Operation!', new Error()); + } + const message = 'Mathematical Operation created successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getMathematicalOperationById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getMathematicalOperationById(id); + if (!record) { + throw new ApiError('Mathematical Operation not found!', 404); + } + const message = 'Mathematical Operation retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + updateMathematicalOperation = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: MathematicalOperationUpdateModel = await this._validator.validateMathematicalOperationUpdateRequest(request); + const updatedRecord = await this._service.updateMathematicalOperation(id, model); + const message = 'Mathematical Operation updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + deleteMathematicalOperation = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.deleteMathematicalOperation(id); + const message = 'Mathematical Operation deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + searchMathematicalOperation = async (request: express.Request, response: express.Response) => { + try { + const filters: OperationSearchFilters = await this._validator.validateOperationSearchRequest(request); + const searchResults = await this._service.searchMathematicalOperation(filters); + const message = 'Mathematical Operation search completed successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + +} \ No newline at end of file diff --git a/src/api/field.operations/mathematical.operation/mathematical.operation.router.ts b/src/api/field.operations/mathematical.operation/mathematical.operation.router.ts new file mode 100644 index 0000000..c7d01e5 --- /dev/null +++ b/src/api/field.operations/mathematical.operation/mathematical.operation.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import { MathematicalOperationController } from './mathematical.operation.controller'; + +/////////////////////////////////////////////////////////////////////////////////// + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new MathematicalOperationController(); + + // Mathematical Operation routes + router.get('/search', controller.searchMathematicalOperation); + router.post('/', controller.createMathematicalOperation); + router.put('/:id', controller.updateMathematicalOperation); + router.get('/:id', controller.getMathematicalOperationById); + router.delete('/:id', controller.deleteMathematicalOperation); + + app.use('/api/v1/field-mathematical-operations', router); +}; \ No newline at end of file diff --git a/src/api/field.operations/mathematical.operation/mathematical.operation.validator.ts b/src/api/field.operations/mathematical.operation/mathematical.operation.validator.ts new file mode 100644 index 0000000..a6325b3 --- /dev/null +++ b/src/api/field.operations/mathematical.operation/mathematical.operation.validator.ts @@ -0,0 +1,88 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import BaseValidator from '../../base.validator'; +import { + MathematicalOperationCreateModel, + MathematicalOperationUpdateModel, + OperationSearchFilters +} from '../../../domain.types/forms/operation.domain.types'; +import { MathematicalOperatorType, OperationType } from '../../../domain.types/forms/operation.enums'; + + +export class MathematicalOperationValidator extends BaseValidator { + + // Mathematical Operation validation + public validateMathematicalOperationCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Operator: joi.string().valid(...Object.values(MathematicalOperatorType)).required(), + Operands: joi.string().required(), + ResultDataType: joi.string().required(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Type: OperationType.Mathematical, // Always set to Mathematical for this operation type + Operator: request.body.Operator, + Operands: request.body.Operands, + ResultDataType: request.body.ResultDataType + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateMathematicalOperationUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Operator: joi.string().valid(...Object.values(MathematicalOperatorType)).optional(), + Operands: joi.string().optional(), + ResultDataType: joi.string().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Operator: request.body.Operator, + Operands: request.body.Operands, + ResultDataType: request.body.ResultDataType + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateOperationSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + name: joi.string().optional(), + description: joi.string().optional(), + operator: joi.string().valid(...Object.values(MathematicalOperatorType)).optional(), + PageIndex: joi.number().integer().min(0).optional(), + ItemsPerPage: joi.number().integer().min(1).max(100).optional(), + OrderBy: joi.string().optional(), + Order: joi.string().valid('ASC', 'DESC').optional(), + }); + await schema.validateAsync(request.query); + return { + id: request.query.id as string, + name: request.query.name as string, + description: request.query.description as string, + operator: request.query.operator as MathematicalOperatorType, + PageIndex: request.query.PageIndex ? parseInt(request.query.PageIndex as string) : 0, + ItemsPerPage: request.query.ItemsPerPage ? parseInt(request.query.ItemsPerPage as string) : 10, + OrderBy: request.query.OrderBy as string, + Order: request.query.Order as 'ASC' | 'DESC', + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; +} \ No newline at end of file diff --git a/src/api/field.rules/calculation.rule/calculation.rule.controller.ts b/src/api/field.rules/calculation.rule/calculation.rule.controller.ts new file mode 100644 index 0000000..e8db2dd --- /dev/null +++ b/src/api/field.rules/calculation.rule/calculation.rule.controller.ts @@ -0,0 +1,83 @@ +import express from 'express'; +import { ResponseHandler } from '../../../common/handlers/response.handler'; +import { BaseController } from '../../base.controller'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import { uuid } from '../../../domain.types/miscellaneous/system.types'; +import { CalculationRuleService } from '../../../services/field.rules/calculation.rule.service'; +import { CalculationRuleValidator } from './calculation.rule.validator'; +import { CalculationRuleCreateModel, CalculationRuleUpdateModel, RuleSearchFilters } from '../../../domain.types/forms/rule.domain.types'; +import { ApiError } from '../../../common/api.error'; +import { Injector } from '../../../startup/injector'; + +export class CalculationRuleController extends BaseController { + _service: CalculationRuleService = Injector.Container.resolve(CalculationRuleService); + _validator: CalculationRuleValidator = new CalculationRuleValidator(); + + constructor() { + super(); + } + + // Calculation Rule operations + createCalculationRule = async (request: express.Request, response: express.Response) => { + try { + const model: CalculationRuleCreateModel = await this._validator.validateCalculationRuleCreateRequest(request); + const record = await this._service.createCalculationRule(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to create Calculation Rule!', new Error()); + } + const message = 'Calculation Rule created successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getCalculationRuleById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getCalculationRuleById(id); + if (!record) { + throw new ApiError('Calculation Rule not found!', 404); + } + const message = 'Calculation Rule retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + updateCalculationRule = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: CalculationRuleUpdateModel = await this._validator.validateCalculationRuleUpdateRequest(request); + const updatedRecord = await this._service.updateCalculationRule(id, model); + const message = 'Calculation Rule updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + deleteCalculationRule = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.deleteCalculationRule(id); + const message = 'Calculation Rule deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + searchCalculationRule = async (request: express.Request, response: express.Response) => { + try { + const filters: RuleSearchFilters = await this._validator.validateRuleSearchRequest(request); + const searchResults = await this._service.searchCalculationRule(filters); + const message = 'Calculation Rule search completed successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + +} \ No newline at end of file diff --git a/src/api/field.rules/calculation.rule/calculation.rule.router.ts b/src/api/field.rules/calculation.rule/calculation.rule.router.ts new file mode 100644 index 0000000..60b92f1 --- /dev/null +++ b/src/api/field.rules/calculation.rule/calculation.rule.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import { CalculationRuleController } from './calculation.rule.controller'; + +/////////////////////////////////////////////////////////////////////////////////// + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new CalculationRuleController(); + + // Calculation Rule routes + router.get('/search', controller.searchCalculationRule); + router.post('/', controller.createCalculationRule); + router.put('/:id', controller.updateCalculationRule); + router.get('/:id', controller.getCalculationRuleById); + router.delete('/:id', controller.deleteCalculationRule); + + app.use('/api/v1/field-calculation-rules', router); +}; \ No newline at end of file diff --git a/src/api/field.rules/calculation.rule/calculation.rule.validator.ts b/src/api/field.rules/calculation.rule/calculation.rule.validator.ts new file mode 100644 index 0000000..aa938f1 --- /dev/null +++ b/src/api/field.rules/calculation.rule/calculation.rule.validator.ts @@ -0,0 +1,100 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import BaseValidator from '../../base.validator'; +import { + CalculationRuleCreateModel, + CalculationRuleUpdateModel, + RuleSearchFilters +} from '../../../domain.types/forms/rule.domain.types'; + + +export class CalculationRuleValidator extends BaseValidator { + + // Calculation Rule validation + public validateCalculationRuleCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Priority: joi.number().integer().min(0).optional(), + IsActive: joi.boolean().optional(), + ConditionForOperationId: joi.string().uuid().optional(), + OperationId: joi.string().uuid().required(), + LogicId: joi.string().uuid().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Priority: request.body.Priority ?? 0, + IsActive: request.body.IsActive ?? true, + ConditionForOperationId: request.body.ConditionForOperationId, + OperationId: request.body.OperationId, + LogicId: request.body.LogicId + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateCalculationRuleUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Priority: joi.number().integer().min(0).optional(), + IsActive: joi.boolean().optional(), + ConditionForOperationId: joi.string().uuid().optional(), + OperationId: joi.string().uuid().optional(), + LogicId: joi.string().uuid().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Priority: request.body.Priority, + IsActive: request.body.IsActive, + ConditionForOperationId: request.body.ConditionForOperationId, + OperationId: request.body.OperationId, + LogicId: request.body.LogicId + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateRuleSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + name: joi.string().optional(), + description: joi.string().optional(), + priority: joi.number().integer().min(0).optional(), + isActive: joi.boolean().optional(), + operationId: joi.string().uuid().optional(), + logicId: joi.string().uuid().optional(), + PageIndex: joi.number().integer().min(0).optional(), + ItemsPerPage: joi.number().integer().min(1).max(100).optional(), + OrderBy: joi.string().optional(), + Order: joi.string().valid('ASC', 'DESC').optional(), + }); + await schema.validateAsync(request.query); + return { + id: request.query.id as string, + name: request.query.name as string, + description: request.query.description as string, + priority: request.query.priority ? parseInt(request.query.priority as string) : undefined, + isActive: request.query.isActive === 'true' ? true : request.query.isActive === 'false' ? false : undefined, + operationId: request.query.operationId as string, + logicId: request.query.logicId as string, + PageIndex: request.query.PageIndex ? parseInt(request.query.PageIndex as string) : 0, + ItemsPerPage: request.query.ItemsPerPage ? parseInt(request.query.ItemsPerPage as string) : 10, + OrderBy: request.query.OrderBy as string, + Order: request.query.Order as 'ASC' | 'DESC', + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; +} \ No newline at end of file diff --git a/src/api/field.rules/skip.rule/skip.rule.controller.ts b/src/api/field.rules/skip.rule/skip.rule.controller.ts new file mode 100644 index 0000000..3b45cbe --- /dev/null +++ b/src/api/field.rules/skip.rule/skip.rule.controller.ts @@ -0,0 +1,83 @@ +import express from 'express'; +import { ResponseHandler } from '../../../common/handlers/response.handler'; +import { BaseController } from '../../base.controller'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import { uuid } from '../../../domain.types/miscellaneous/system.types'; +import { SkipRuleService } from '../../../services/field.rules/skip.rule.service'; +import { SkipRuleValidator } from './skip.rule.validator'; +import { SkipRuleCreateModel, SkipRuleUpdateModel, RuleSearchFilters } from '../../../domain.types/forms/rule.domain.types'; +import { ApiError } from '../../../common/api.error'; +import { Injector } from '../../../startup/injector'; + +export class SkipRuleController extends BaseController { + _service: SkipRuleService = Injector.Container.resolve(SkipRuleService); + _validator: SkipRuleValidator = new SkipRuleValidator(); + + constructor() { + super(); + } + + // Skip Rule operations + createSkipRule = async (request: express.Request, response: express.Response) => { + try { + const model: SkipRuleCreateModel = await this._validator.validateSkipRuleCreateRequest(request); + const record = await this._service.createSkipRule(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to create Skip Rule!', new Error()); + } + const message = 'Skip Rule created successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getSkipRuleById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getSkipRuleById(id); + if (!record) { + throw new ApiError('Skip Rule not found!', 404); + } + const message = 'Skip Rule retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + updateSkipRule = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: SkipRuleUpdateModel = await this._validator.validateSkipRuleUpdateRequest(request); + const updatedRecord = await this._service.updateSkipRule(id, model); + const message = 'Skip Rule updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + deleteSkipRule = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.deleteSkipRule(id); + const message = 'Skip Rule deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + searchSkipRule = async (request: express.Request, response: express.Response) => { + try { + const filters: RuleSearchFilters = await this._validator.validateRuleSearchRequest(request); + const searchResults = await this._service.searchSkipRule(filters); + const message = 'Skip Rule search completed successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + +} \ No newline at end of file diff --git a/src/api/field.rules/skip.rule/skip.rule.router.ts b/src/api/field.rules/skip.rule/skip.rule.router.ts new file mode 100644 index 0000000..92b9dea --- /dev/null +++ b/src/api/field.rules/skip.rule/skip.rule.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import { SkipRuleController } from './skip.rule.controller'; + +/////////////////////////////////////////////////////////////////////////////////// + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new SkipRuleController(); + + // Skip Rule routes + router.get('/search', controller.searchSkipRule); + router.post('/', controller.createSkipRule); + router.put('/:id', controller.updateSkipRule); + router.get('/:id', controller.getSkipRuleById); + router.delete('/:id', controller.deleteSkipRule); + + app.use('/api/v1/field-skip-rules', router); +}; \ No newline at end of file diff --git a/src/api/field.rules/skip.rule/skip.rule.validator.ts b/src/api/field.rules/skip.rule/skip.rule.validator.ts new file mode 100644 index 0000000..e9a3c2a --- /dev/null +++ b/src/api/field.rules/skip.rule/skip.rule.validator.ts @@ -0,0 +1,100 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import BaseValidator from '../../base.validator'; +import { + SkipRuleCreateModel, + SkipRuleUpdateModel, + RuleSearchFilters +} from '../../../domain.types/forms/rule.domain.types'; + + +export class SkipRuleValidator extends BaseValidator { + + // Skip Rule validation + public validateSkipRuleCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Priority: joi.number().integer().min(0).optional(), + IsActive: joi.boolean().optional(), + OperationId: joi.string().uuid().required(), + SkipWhenTrue: joi.boolean().required(), + LogicId: joi.string().uuid().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Priority: request.body.Priority ?? 0, + IsActive: request.body.IsActive ?? true, + OperationId: request.body.OperationId, + SkipWhenTrue: request.body.SkipWhenTrue, + LogicId: request.body.LogicId + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateSkipRuleUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Priority: joi.number().integer().min(0).optional(), + IsActive: joi.boolean().optional(), + OperationId: joi.string().uuid().optional(), + SkipWhenTrue: joi.boolean().optional(), + LogicId: joi.string().uuid().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Priority: request.body.Priority, + IsActive: request.body.IsActive, + OperationId: request.body.OperationId, + SkipWhenTrue: request.body.SkipWhenTrue, + LogicId: request.body.LogicId + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateRuleSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + name: joi.string().optional(), + description: joi.string().optional(), + priority: joi.number().integer().min(0).optional(), + isActive: joi.boolean().optional(), + operationId: joi.string().uuid().optional(), + logicId: joi.string().uuid().optional(), + PageIndex: joi.number().integer().min(0).optional(), + ItemsPerPage: joi.number().integer().min(1).max(100).optional(), + OrderBy: joi.string().optional(), + Order: joi.string().valid('ASC', 'DESC').optional(), + }); + await schema.validateAsync(request.query); + return { + id: request.query.id as string, + name: request.query.name as string, + description: request.query.description as string, + priority: request.query.priority ? parseInt(request.query.priority as string) : undefined, + isActive: request.query.isActive === 'true' ? true : request.query.isActive === 'false' ? false : undefined, + operationId: request.query.operationId as string, + logicId: request.query.logicId as string, + PageIndex: request.query.PageIndex ? parseInt(request.query.PageIndex as string) : 0, + ItemsPerPage: request.query.ItemsPerPage ? parseInt(request.query.ItemsPerPage as string) : 10, + OrderBy: request.query.OrderBy as string, + Order: request.query.Order as 'ASC' | 'DESC', + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; +} \ No newline at end of file diff --git a/src/api/field.rules/validation.rule/validation.rule.controller.ts b/src/api/field.rules/validation.rule/validation.rule.controller.ts new file mode 100644 index 0000000..d790904 --- /dev/null +++ b/src/api/field.rules/validation.rule/validation.rule.controller.ts @@ -0,0 +1,83 @@ +import express from 'express'; +import { ResponseHandler } from '../../../common/handlers/response.handler'; +import { BaseController } from '../../base.controller'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import { uuid } from '../../../domain.types/miscellaneous/system.types'; +import { ValidationRuleService } from '../../../services/field.rules/validation.rule.service'; +import { ValidationRuleValidator } from './validation.rule.validator'; +import { ValidationRuleCreateModel, ValidationRuleUpdateModel, RuleSearchFilters } from '../../../domain.types/forms/rule.domain.types'; +import { ApiError } from '../../../common/api.error'; +import { Injector } from '../../../startup/injector'; + +export class ValidationRuleController extends BaseController { + _service: ValidationRuleService = Injector.Container.resolve(ValidationRuleService); + _validator: ValidationRuleValidator = new ValidationRuleValidator(); + + constructor() { + super(); + } + + // Validation Rule operations + createValidationRule = async (request: express.Request, response: express.Response) => { + try { + const model: ValidationRuleCreateModel = await this._validator.validateValidationRuleCreateRequest(request); + const record = await this._service.createValidationRule(model); + if (record === null) { + ErrorHandler.throwInternalServerError('Unable to create Validation Rule!', new Error()); + } + const message = 'Validation Rule created successfully!'; + return ResponseHandler.success(request, response, message, 201, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + getValidationRuleById = async (request: express.Request, response: express.Response) => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const record = await this._service.getValidationRuleById(id); + if (!record) { + throw new ApiError('Validation Rule not found!', 404); + } + const message = 'Validation Rule retrieved successfully!'; + return ResponseHandler.success(request, response, message, 200, record); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + updateValidationRule = async (request: express.Request, response: express.Response) => { + try { + const id = await this._validator.validateParamAsUUID(request, 'id'); + const model: ValidationRuleUpdateModel = await this._validator.validateValidationRuleUpdateRequest(request); + const updatedRecord = await this._service.updateValidationRule(id, model); + const message = 'Validation Rule updated successfully!'; + ResponseHandler.success(request, response, message, 200, updatedRecord); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + deleteValidationRule = async (request: express.Request, response: express.Response): Promise => { + try { + const id: uuid = await this._validator.validateParamAsUUID(request, 'id'); + const result = await this._service.deleteValidationRule(id); + const message = 'Validation Rule deleted successfully!'; + ResponseHandler.success(request, response, message, 200, result); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + + searchValidationRule = async (request: express.Request, response: express.Response) => { + try { + const filters: RuleSearchFilters = await this._validator.validateRuleSearchRequest(request); + const searchResults = await this._service.searchValidationRule(filters); + const message = 'Validation Rule search completed successfully!'; + ResponseHandler.success(request, response, message, 200, searchResults); + } catch (error) { + ResponseHandler.handleError(request, response, error); + } + }; + +} \ No newline at end of file diff --git a/src/api/field.rules/validation.rule/validation.rule.router.ts b/src/api/field.rules/validation.rule/validation.rule.router.ts new file mode 100644 index 0000000..5665236 --- /dev/null +++ b/src/api/field.rules/validation.rule/validation.rule.router.ts @@ -0,0 +1,19 @@ +import express from 'express'; +import { ValidationRuleController } from './validation.rule.controller'; + +/////////////////////////////////////////////////////////////////////////////////// + +export const register = (app: express.Application): void => { + + const router = express.Router(); + const controller = new ValidationRuleController(); + + // Validation Rule routes + router.get('/search', controller.searchValidationRule); + router.post('/', controller.createValidationRule); + router.put('/:id', controller.updateValidationRule); + router.get('/:id', controller.getValidationRuleById); + router.delete('/:id', controller.deleteValidationRule); + + app.use('/api/v1/field-validation-rules', router); +}; \ No newline at end of file diff --git a/src/api/field.rules/validation.rule/validation.rule.validator.ts b/src/api/field.rules/validation.rule/validation.rule.validator.ts new file mode 100644 index 0000000..c8cf4d4 --- /dev/null +++ b/src/api/field.rules/validation.rule/validation.rule.validator.ts @@ -0,0 +1,104 @@ +import joi from 'joi'; +import express from 'express'; +import { ErrorHandler } from '../../../common/handlers/error.handler'; +import BaseValidator from '../../base.validator'; +import { + ValidationRuleCreateModel, + ValidationRuleUpdateModel, + RuleSearchFilters +} from '../../../domain.types/forms/rule.domain.types'; + + +export class ValidationRuleValidator extends BaseValidator { + + // Validation Rule validation + public validateValidationRuleCreateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Priority: joi.number().integer().min(0).optional(), + IsActive: joi.boolean().optional(), + OperationId: joi.string().uuid().required(), + ErrorWhenFalse: joi.boolean().required(), + ErrorMessage: joi.string().required(), + LogicId: joi.string().uuid().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Priority: request.body.Priority ?? 0, + IsActive: request.body.IsActive ?? true, + OperationId: request.body.OperationId, + ErrorWhenFalse: request.body.ErrorWhenFalse, + ErrorMessage: request.body.ErrorMessage, + LogicId: request.body.LogicId + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateValidationRuleUpdateRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + Name: joi.string().optional(), + Description: joi.string().optional(), + Priority: joi.number().integer().min(0).optional(), + IsActive: joi.boolean().optional(), + OperationId: joi.string().uuid().optional(), + ErrorWhenFalse: joi.boolean().optional(), + ErrorMessage: joi.string().optional(), + LogicId: joi.string().uuid().optional(), + }); + await schema.validateAsync(request.body); + return { + Name: request.body.Name, + Description: request.body.Description, + Priority: request.body.Priority, + IsActive: request.body.IsActive, + OperationId: request.body.OperationId, + ErrorWhenFalse: request.body.ErrorWhenFalse, + ErrorMessage: request.body.ErrorMessage, + LogicId: request.body.LogicId + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; + + public validateRuleSearchRequest = async (request: express.Request): Promise => { + try { + const schema = joi.object({ + id: joi.string().uuid().optional(), + name: joi.string().optional(), + description: joi.string().optional(), + priority: joi.number().integer().min(0).optional(), + isActive: joi.boolean().optional(), + operationId: joi.string().uuid().optional(), + logicId: joi.string().uuid().optional(), + PageIndex: joi.number().integer().min(0).optional(), + ItemsPerPage: joi.number().integer().min(1).max(100).optional(), + OrderBy: joi.string().optional(), + Order: joi.string().valid('ASC', 'DESC').optional(), + }); + await schema.validateAsync(request.query); + return { + id: request.query.id as string, + name: request.query.name as string, + description: request.query.description as string, + priority: request.query.priority ? parseInt(request.query.priority as string) : undefined, + isActive: request.query.isActive === 'true' ? true : request.query.isActive === 'false' ? false : undefined, + operationId: request.query.operationId as string, + logicId: request.query.logicId as string, + PageIndex: request.query.PageIndex ? parseInt(request.query.PageIndex as string) : 0, + ItemsPerPage: request.query.ItemsPerPage ? parseInt(request.query.ItemsPerPage as string) : 10, + OrderBy: request.query.OrderBy as string, + Order: request.query.Order as 'ASC' | 'DESC', + }; + } catch (error) { + ErrorHandler.handleValidationError(error); + } + }; +} \ No newline at end of file diff --git a/src/api/form.field/form.field.auth.ts b/src/api/form.field/form.field.auth.ts new file mode 100644 index 0000000..d7ca003 --- /dev/null +++ b/src/api/form.field/form.field.auth.ts @@ -0,0 +1 @@ +// Form Field Authentication - to be implemented \ No newline at end of file diff --git a/src/api/question/question.controller.ts b/src/api/form.field/form.field.controller.ts similarity index 71% rename from src/api/question/question.controller.ts rename to src/api/form.field/form.field.controller.ts index 49c59b9..83518ec 100644 --- a/src/api/question/question.controller.ts +++ b/src/api/form.field/form.field.controller.ts @@ -4,20 +4,20 @@ import { BaseController } from '../base.controller'; import { ErrorHandler } from '../../common/handlers/error.handler'; import { uuid } from '../../domain.types/miscellaneous/system.types'; import { error } from 'console'; -import { QuestionService } from '../../services/question/question.service'; -import { QuestionValidator } from './question.validator'; -import { QuestionCreateModel, QuestionSearchFilters, QuestionUpdateModel } from '../../domain.types/forms/question.domain.types'; +import { FormFieldService } from '../../services/form.field/form.field.service'; +import { FormFieldCreateModel, FormFieldSearchFilters, FormFieldUpdateModel } from '../../domain.types/forms/form.field.domain.types'; import { Injector } from '../../startup/injector'; +import { FormFieldValidator } from './form.field.validator'; /////////////////////////////////////////////////////////////////////////////////////// -export class QuestionController extends BaseController { +export class FormFieldController extends BaseController { //#region member variables and constructors - _service: QuestionService = Injector.Container.resolve(QuestionService); + _service: FormFieldService = Injector.Container.resolve(FormFieldService); - _validator: QuestionValidator = new QuestionValidator(); + _validator: FormFieldValidator = new FormFieldValidator(); constructor() { super(); @@ -27,11 +27,11 @@ export class QuestionController extends BaseController { // getAll = async (request: express.Request, response: express.Response) => { // try { - // const record = await this._service.allQuestions(); + // const record = await this._service.allFormFields(); // if (record === null) { - // ErrorHandler.throwInternalServerError('Unable to add Question!', error); + // ErrorHandler.throwInternalServerError('Unable to add FormField!', error); // } - // const message = 'All Questions retrived successfully!'; + // const message = 'All FormFields retrived successfully!'; // return ResponseHandler.success(request, response, message, 201, record); // } catch (error) { // ResponseHandler.handleError(request, response, error); @@ -40,22 +40,22 @@ export class QuestionController extends BaseController { create = async (request: express.Request, response: express.Response) => { try { - let model: QuestionCreateModel = await this._validator.validateCreateRequest(request); + let model: FormFieldCreateModel = await this._validator.validateCreateRequest(request); const parentSectionId = request.body.ParentSectionId; - const allQuestions = await this._service.search({ parentSectionId }); + const allFormFields = await this._service.search({ parentSectionId }); - if (allQuestions.Items.length === 0) { + if (allFormFields.Items.length === 0) { model.Sequence = 1; } else { - model.Sequence = allQuestions.Items.length + 1; + model.Sequence = allFormFields.Items.length + 1; } const record = await this._service.create(model); if (record === null) { - ErrorHandler.throwInternalServerError('Unable to add Form!', error); + ErrorHandler.throwInternalServerError('Unable to add FormField!', error); } - const message = 'Question added successfully!'; + const message = 'FormField added successfully!'; return ResponseHandler.success(request, response, message, 201, record); } catch (error) { ResponseHandler.handleError(request, response, error); @@ -67,7 +67,7 @@ export class QuestionController extends BaseController { // await this.authorize('Form.GetById', request, response); var id: uuid = await this._validator.validateParamAsUUID(request, 'id'); const record = await this._service.getById(id); - const message = 'Question retrieved successfully!'; + const message = 'FormField retrieved successfully!'; return ResponseHandler.success(request, response, message, 200, record); } catch (error) { ResponseHandler.handleError(request, response, error); @@ -79,7 +79,7 @@ export class QuestionController extends BaseController { // await this.authorize('Form.GetById', request, response); var id: uuid = await this._validator.validateParamAsUUID(request, 'templateId'); const record = await this._service.getByTemplateId(id); - const message = 'Questions by templateId retrived successfully!'; + const message = 'FormFields by templateId retrived successfully!'; return ResponseHandler.success(request, response, message, 200, record); } catch (error) { ResponseHandler.handleError(request, response, error); @@ -90,9 +90,9 @@ export class QuestionController extends BaseController { try { // await this.authorize('Form.Update', request, response); const id = await this._validator.validateParamAsUUID(request, 'id'); - var model: QuestionUpdateModel = await this._validator.validateUpdateRequest(request); + var model: FormFieldUpdateModel = await this._validator.validateUpdateRequest(request); const updatedRecord = await this._service.update(id, model); - const message = 'Question updated successfully!'; + const message = 'FormField updated successfully!'; ResponseHandler.success(request, response, message, 200, updatedRecord); } catch (error) { ResponseHandler.handleError(request, response, error); @@ -104,7 +104,7 @@ export class QuestionController extends BaseController { // await this.authorize('Form.Delete', request, response); var id: uuid = await this._validator.validateParamAsUUID(request, 'id'); const result = await this._service.delete(id); - const message = 'Question deleted successfully!'; + const message = 'FormField deleted successfully!'; ResponseHandler.success(request, response, message, 200, result); } catch (error) { ResponseHandler.handleError(request, response, error); @@ -113,14 +113,13 @@ export class QuestionController extends BaseController { search = async (request: express.Request, response: express.Response) => { try { - var filters: QuestionSearchFilters = await this._validator.validateSearchRequest(request); + var filters: FormFieldSearchFilters = await this._validator.validateSearchRequest(request); const searchResults = await this._service.search(filters); - const message = 'Question retrieved successfully!'; + const message = 'FormField retrieved successfully!'; ResponseHandler.success(request, response, message, 200, searchResults); } catch (error) { ResponseHandler.handleError(request, response, error); } }; -} - +} \ No newline at end of file diff --git a/src/api/question/question.router.ts b/src/api/form.field/form.field.router.ts similarity index 77% rename from src/api/question/question.router.ts rename to src/api/form.field/form.field.router.ts index 26bbbeb..d9e8b9c 100644 --- a/src/api/question/question.router.ts +++ b/src/api/form.field/form.field.router.ts @@ -1,5 +1,5 @@ import express from 'express'; -import { QuestionController } from './question.controller'; +import { FormFieldController } from './form.field.controller'; /////////////////////////////////////////////////////////////////////////////////// @@ -7,7 +7,7 @@ import { QuestionController } from './question.controller'; export const register = (app: express.Application): void => { const router = express.Router(); - const controller = new QuestionController(); + const controller = new FormFieldController(); router.get('/search', controller.search); // router.get('/all', controller.getAll); @@ -17,5 +17,5 @@ export const register = (app: express.Application): void => { router.get('/by-template-id/:templateId', controller.getByTemplateId); router.delete('/:id', controller.delete); - app.use('/api/v1/questions', router); -}; + app.use('/api/v1/form-fields', router); +}; \ No newline at end of file diff --git a/src/api/question/question.validator.ts b/src/api/form.field/form.field.validator.ts similarity index 91% rename from src/api/question/question.validator.ts rename to src/api/form.field/form.field.validator.ts index 2a694b1..8ff4fb8 100644 --- a/src/api/question/question.validator.ts +++ b/src/api/form.field/form.field.validator.ts @@ -4,15 +4,15 @@ import { ErrorHandler } from '../../common/handlers/error.handler'; import BaseValidator from '../base.validator'; -import { QuestionCreateModel, QuestionSearchFilters, QuestionUpdateModel } from '../../domain.types/forms/question.domain.types'; +import { FormFieldCreateModel, FormFieldSearchFilters, FormFieldUpdateModel } from '../../domain.types/forms/form.field.domain.types'; import { generateDisplayCode } from '../../domain.types/miscellaneous/display.code'; import { ParsedQs } from 'qs'; /////////////////////////////////////////////////////////////////////////////////////////////// -export class QuestionValidator extends BaseValidator { +export class FormFieldValidator extends BaseValidator { - // public validateCreateRequest = async (request: express.Request): Promise => { + // public validateCreateRequest = async (request: express.Request): Promise => { // try { // const schema = joi.object({ // ParentTemplateId: joi.string().uuid().required(), @@ -37,7 +37,7 @@ export class QuestionValidator extends BaseValidator { // ParentSectionId: request.body.ParentSectionId, // Title: request.body.Title, // Description: request.body.Description, - // DisplayCode: request.body.DisplayCode ?? generateDisplayCode(25, 'QUESTION_#'), + // DisplayCode: request.body.DisplayCode ?? generateDisplayCode(25, 'FORMFIELD_#'), // ResponseType: request.body.ResponseType, // Score: request.body.Score, // Sequence: request.body.Sequence, @@ -54,7 +54,7 @@ export class QuestionValidator extends BaseValidator { // } // }; - public validateCreateRequest = async (request: express.Request): Promise => { + public validateCreateRequest = async (request: express.Request): Promise => { try { const optionSchema = joi.object({ Text: joi.string().required(), @@ -77,7 +77,7 @@ export class QuestionValidator extends BaseValidator { Options: joi.array().items(optionSchema).optional(), // Validate Options as an array of objects // FileResourceId: joi.string().uuid(), QuestionImageUrl: joi.string().optional(), - RangeMin: joi.number().optional(), // Should be a number to match the type in QuestionCreateModel + RangeMin: joi.number().optional(), // Should be a number to match the type in FormFieldCreateModel RangeMax: joi.number().optional(), }); @@ -87,14 +87,14 @@ export class QuestionValidator extends BaseValidator { ParentSectionId: request.body.ParentSectionId, Title: request.body.Title, Description: request.body.Description, - DisplayCode: request.body.DisplayCode ?? generateDisplayCode(25, 'QUESTION_#'), + DisplayCode: request.body.DisplayCode ?? generateDisplayCode(25, 'FORMFIELD_#'), ResponseType: request.body.ResponseType, Score: request.body.Score, Sequence: request.body.Sequence, CorrectAnswer: request.body.CorrectAnswer, IsRequired: request.body.IsRequired ?? false, Hint: request.body.Hint, - Options: request.body.Options, // Options should now be an array of QuestionOption + Options: request.body.Options, // Options should now be an array of FormFieldOption // FileResourceId: request.body.FileResourceId, QuestionImageUrl: request.body.QuestionImageUrl, RangeMin: request.body.RangeMin ?? null, @@ -105,7 +105,7 @@ export class QuestionValidator extends BaseValidator { } }; - public validateUpdateRequest = async (request: express.Request): Promise => { + public validateUpdateRequest = async (request: express.Request): Promise => { try { const optionSchema = joi.object({ Text: joi.string().required(), @@ -149,7 +149,7 @@ export class QuestionValidator extends BaseValidator { } }; - public validateSearchRequest = async (request: express.Request): Promise => { + public validateSearchRequest = async (request: express.Request): Promise => { try { const schema = joi.object({ id: joi.string().uuid().optional(), @@ -179,7 +179,7 @@ export class QuestionValidator extends BaseValidator { } }; - private getSearchFilters = (query: ParsedQs): QuestionSearchFilters => { + private getSearchFilters = (query: ParsedQs): FormFieldSearchFilters => { var filters: any = {}; var id = query.id ? query.id : null; @@ -230,6 +230,10 @@ export class QuestionValidator extends BaseValidator { if (options != null) { filters['options'] = options; } + // var FileResourceId = query.FileResourceId ? query.FileResourceId : null; + // if (FileResourceId != null) { + // filters['FileResourceId'] = FileResourceId; + // } var questionImageUrl = query.questionImageUrl ? query.questionImageUrl : null; if (questionImageUrl != null) { filters['questionImageUrl'] = questionImageUrl; @@ -243,19 +247,6 @@ export class QuestionValidator extends BaseValidator { filters['rangeMax'] = rangeMax; } - var itemsPerPage = query.itemsPerPage ? query.itemsPerPage : 25; - if (itemsPerPage != null) { - filters['ItemsPerPage'] = itemsPerPage; - } - var orderBy = query.orderBy ? query.orderBy : 'CreatedAt'; - if (orderBy != null) { - filters['OrderBy'] = orderBy; - } - var order = query.order ? query.order : 'ASC'; - if (order != null) { - filters['Order'] = order; - } return filters; }; - -} +} \ No newline at end of file diff --git a/src/api/form.submission/form.validator.ts b/src/api/form.submission/form.validator.ts index 22f5295..4beef32 100644 --- a/src/api/form.submission/form.validator.ts +++ b/src/api/form.submission/form.validator.ts @@ -5,8 +5,8 @@ import { } from '../../common/handlers/error.handler'; import BaseValidator from '../base.validator'; import { FormSubmissionCreateModel, FormSubmissionDto, FormSubmissionSearchFilters, FormSubmissionUpdateModel } from '../../domain.types/forms/form.submission.domain.types'; -import { FormStatus } from '../../database/sql/typeorm/models/form.submission/form.submission.model'; -import { FormType } from '../../database/sql/typeorm/models/form.template/form.template.model'; +import { FormStatus } from '../../domain.types/forms/form.submission.enums'; +import { FormType } from '../../domain.types/forms/form.template.enums'; import { ParsedQs } from 'qs'; import { TimeHelper } from '../../common/time.helper'; import { DurationType } from '../../miscellaneous/time.types'; diff --git a/src/api/form.template/form.template.auth.ts b/src/api/form.template/form.template.auth.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/api/question.response/question.response.controller.ts b/src/api/question.response/question.response.controller.ts index 6e0f7a7..12deb1c 100644 --- a/src/api/question.response/question.response.controller.ts +++ b/src/api/question.response/question.response.controller.ts @@ -13,7 +13,7 @@ import * as path from 'path'; import * as fs from 'fs'; import { container } from 'tsyringe'; import { FormService } from '../../services/form.submission/form.submission.service'; -import { FormStatus } from '../../database/sql/typeorm/models/form.submission/form.submission.model'; +import { FormStatus } from '../../domain.types/forms/form.submission.enums'; import { Injector } from '../../startup/injector'; /////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/api/question.response/question.response.validator.ts b/src/api/question.response/question.response.validator.ts index b11ce65..be2b00f 100644 --- a/src/api/question.response/question.response.validator.ts +++ b/src/api/question.response/question.response.validator.ts @@ -10,7 +10,7 @@ import { } from "../../domain.types/forms/response.domain.types"; import { ParsedQs } from 'qs'; import { FormSubmissionDto } from "../../domain.types/forms/form.submission.domain.types"; -import { FormStatus } from "../../database/sql/typeorm/models/form.submission/form.submission.model"; +import { FormStatus } from "../../domain.types/forms/form.submission.enums"; import { ApiError } from "../../common/api.error"; /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/api/question/question.auth.ts b/src/api/question/question.auth.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/app.ts b/src/app.ts index c3f6174..ca23040 100644 --- a/src/app.ts +++ b/src/app.ts @@ -57,6 +57,9 @@ export default class Application { //Load configurations ConfigurationManager.loadConfigurations(); + //Register dependency injections + Injector.registerInjections(); + //Load the modules await Loader.init(); diff --git a/src/database/repository.interfaces/field.logic/calculation.logic/calculation.logic.repo.interface.ts b/src/database/repository.interfaces/field.logic/calculation.logic/calculation.logic.repo.interface.ts new file mode 100644 index 0000000..f393d04 --- /dev/null +++ b/src/database/repository.interfaces/field.logic/calculation.logic/calculation.logic.repo.interface.ts @@ -0,0 +1,16 @@ +import { + CalculationLogicResponseDto, + CalculationLogicCreateModel, + CalculationLogicUpdateModel, + LogicSearchFilters +} from "../../../../domain.types/forms/logic.domain.types"; + +export interface ICalculationLogicRepo { + + // Calculation Logic operations + createCalculationLogic(model: CalculationLogicCreateModel): Promise; + updateCalculationLogic(id: string, model: CalculationLogicUpdateModel): Promise; + getCalculationLogicById(id: string): Promise; + deleteCalculationLogic(id: string): Promise; + searchCalculationLogic(filters: LogicSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/field.logic/logic.repo.interface.ts b/src/database/repository.interfaces/field.logic/logic.repo.interface.ts new file mode 100644 index 0000000..15c8e2c --- /dev/null +++ b/src/database/repository.interfaces/field.logic/logic.repo.interface.ts @@ -0,0 +1,45 @@ +import { + BaseLogicResponseDto, + SkipLogicResponseDto, + CalculationLogicResponseDto, + ValidationLogicResponseDto, + SkipLogicCreateModel, + CalculationLogicCreateModel, + ValidationLogicCreateModel, + SkipLogicUpdateModel, + CalculationLogicUpdateModel, + ValidationLogicUpdateModel, + LogicSearchFilters +} from "../../../domain.types/forms/logic.domain.types"; + +export interface ILogicRepo { + // Base Logic operations + createBaseLogic(model: SkipLogicCreateModel | CalculationLogicCreateModel | ValidationLogicCreateModel): Promise; + updateBaseLogic(id: string, model: SkipLogicUpdateModel | CalculationLogicUpdateModel | ValidationLogicUpdateModel): Promise; + getBaseLogicById(id: string): Promise; + deleteBaseLogic(id: string): Promise; + searchBaseLogic(filters: LogicSearchFilters): Promise; + + // Skip Logic operations + createSkipLogic(model: SkipLogicCreateModel): Promise; + updateSkipLogic(id: string, model: SkipLogicUpdateModel): Promise; + getSkipLogicById(id: string): Promise; + deleteSkipLogic(id: string): Promise; + searchSkipLogic(filters: LogicSearchFilters): Promise; + + // Calculation Logic operations + createCalculationLogic(model: CalculationLogicCreateModel): Promise; + updateCalculationLogic(id: string, model: CalculationLogicUpdateModel): Promise; + getCalculationLogicById(id: string): Promise; + deleteCalculationLogic(id: string): Promise; + searchCalculationLogic(filters: LogicSearchFilters): Promise; + + // Validation Logic operations + createValidationLogic(model: ValidationLogicCreateModel): Promise; + updateValidationLogic(id: string, model: ValidationLogicUpdateModel): Promise; + getValidationLogicById(id: string): Promise; + deleteValidationLogic(id: string): Promise; + searchValidationLogic(filters: LogicSearchFilters): Promise; + + +} \ No newline at end of file diff --git a/src/database/repository.interfaces/field.logic/skip.logic/skip.logic.repo.interface.ts b/src/database/repository.interfaces/field.logic/skip.logic/skip.logic.repo.interface.ts new file mode 100644 index 0000000..3a7c625 --- /dev/null +++ b/src/database/repository.interfaces/field.logic/skip.logic/skip.logic.repo.interface.ts @@ -0,0 +1,16 @@ +import { + SkipLogicResponseDto, + SkipLogicCreateModel, + SkipLogicUpdateModel, + LogicSearchFilters +} from "../../../../domain.types/forms/logic.domain.types"; + +export interface ISkipLogicRepo { + + // Skip Logic operations + createSkipLogic(model: SkipLogicCreateModel): Promise; + updateSkipLogic(id: string, model: SkipLogicUpdateModel): Promise; + getSkipLogicById(id: string): Promise; + deleteSkipLogic(id: string): Promise; + searchSkipLogic(filters: LogicSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/field.logic/validation.logic/validation.logic.repo.interface.ts b/src/database/repository.interfaces/field.logic/validation.logic/validation.logic.repo.interface.ts new file mode 100644 index 0000000..a5e231d --- /dev/null +++ b/src/database/repository.interfaces/field.logic/validation.logic/validation.logic.repo.interface.ts @@ -0,0 +1,16 @@ +import { + ValidationLogicResponseDto, + ValidationLogicCreateModel, + ValidationLogicUpdateModel, + LogicSearchFilters +} from "../../../../domain.types/forms/logic.domain.types"; + +export interface IValidationLogicRepo { + + // Validation Logic operations + createValidationLogic(model: ValidationLogicCreateModel): Promise; + updateValidationLogic(id: string, model: ValidationLogicUpdateModel): Promise; + getValidationLogicById(id: string): Promise; + deleteValidationLogic(id: string): Promise; + searchValidationLogic(filters: LogicSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/field.operations/composition.operation/composition.operation.repo.interface.ts b/src/database/repository.interfaces/field.operations/composition.operation/composition.operation.repo.interface.ts new file mode 100644 index 0000000..f44768e --- /dev/null +++ b/src/database/repository.interfaces/field.operations/composition.operation/composition.operation.repo.interface.ts @@ -0,0 +1,18 @@ +import { + CompositionOperationCreateModel, + CompositionOperationUpdateModel, + CompositionOperationResponseDto, + OperationSearchFilters +} from "../../../../domain.types/forms/operation.domain.types"; + +export interface ICompositionOperationRepo { + + + // Composition Operation operations + createCompositionOperation(model: CompositionOperationCreateModel): Promise; + getCompositionOperationById(id: string): Promise; + updateCompositionOperation(id: string, model: CompositionOperationUpdateModel): Promise; + deleteCompositionOperation(id: string): Promise; + searchCompositionOperation(filters: OperationSearchFilters): Promise; + +} \ No newline at end of file diff --git a/src/database/repository.interfaces/field.operations/function.expression.operation/function.expression.operation.repo.interface.ts b/src/database/repository.interfaces/field.operations/function.expression.operation/function.expression.operation.repo.interface.ts new file mode 100644 index 0000000..9c93e61 --- /dev/null +++ b/src/database/repository.interfaces/field.operations/function.expression.operation/function.expression.operation.repo.interface.ts @@ -0,0 +1,16 @@ +import { + FunctionExpressionOperationResponseDto, + FunctionExpressionOperationCreateModel, + FunctionExpressionOperationUpdateModel, + OperationSearchFilters +} from "../../../../domain.types/forms/operation.domain.types"; + +export interface IFunctionExpressionOperationRepo { + + // Function Expression Operation operations + createFunctionExpressionOperation(model: FunctionExpressionOperationCreateModel): Promise; + updateFunctionExpressionOperation(id: string, model: FunctionExpressionOperationUpdateModel): Promise; + getFunctionExpressionOperationById(id: string): Promise; + deleteFunctionExpressionOperation(id: string): Promise; + searchFunctionExpressionOperation(filters: OperationSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/field.operations/iterate.operation/iterate.operation.repo.interface.ts b/src/database/repository.interfaces/field.operations/iterate.operation/iterate.operation.repo.interface.ts new file mode 100644 index 0000000..7de8702 --- /dev/null +++ b/src/database/repository.interfaces/field.operations/iterate.operation/iterate.operation.repo.interface.ts @@ -0,0 +1,16 @@ +import { + IterateOperationResponseDto, + IterateOperationCreateModel, + IterateOperationUpdateModel, + OperationSearchFilters +} from "../../../../domain.types/forms/operation.domain.types"; + +export interface IIterateOperationRepo { + + // Iterate Operation operations + createIterateOperation(model: IterateOperationCreateModel): Promise; + updateIterateOperation(id: string, model: IterateOperationUpdateModel): Promise; + getIterateOperationById(id: string): Promise; + deleteIterateOperation(id: string): Promise; + searchIterateOperation(filters: OperationSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/field.operations/logical.operation/logical.operation.repo.interface.ts b/src/database/repository.interfaces/field.operations/logical.operation/logical.operation.repo.interface.ts new file mode 100644 index 0000000..e9b86b8 --- /dev/null +++ b/src/database/repository.interfaces/field.operations/logical.operation/logical.operation.repo.interface.ts @@ -0,0 +1,16 @@ +import { + LogicalOperationResponseDto, + LogicalOperationCreateModel, + LogicalOperationUpdateModel, + OperationSearchFilters +} from "../../../../domain.types/forms/operation.domain.types"; + +export interface ILogicalOperationRepo { + + // Logical Operation operations + createLogicalOperation(model: LogicalOperationCreateModel): Promise; + updateLogicalOperation(id: string, model: LogicalOperationUpdateModel): Promise; + getLogicalOperationById(id: string): Promise; + deleteLogicalOperation(id: string): Promise; + searchLogicalOperation(filters: OperationSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/field.operations/mathematical.operation/mathematical.operation.repo.interface.ts b/src/database/repository.interfaces/field.operations/mathematical.operation/mathematical.operation.repo.interface.ts new file mode 100644 index 0000000..3c64bfb --- /dev/null +++ b/src/database/repository.interfaces/field.operations/mathematical.operation/mathematical.operation.repo.interface.ts @@ -0,0 +1,16 @@ +import { + MathematicalOperationResponseDto, + MathematicalOperationCreateModel, + MathematicalOperationUpdateModel, + OperationSearchFilters +} from "../../../../domain.types/forms/operation.domain.types"; + +export interface IMathematicalOperationRepo { + + // Mathematical Operation operations + createMathematicalOperation(model: MathematicalOperationCreateModel): Promise; + updateMathematicalOperation(id: string, model: MathematicalOperationUpdateModel): Promise; + getMathematicalOperationById(id: string): Promise; + deleteMathematicalOperation(id: string): Promise; + searchMathematicalOperation(filters: OperationSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/field.rules/calculation.rule/calculation.rule.repo.interface.ts b/src/database/repository.interfaces/field.rules/calculation.rule/calculation.rule.repo.interface.ts new file mode 100644 index 0000000..c416485 --- /dev/null +++ b/src/database/repository.interfaces/field.rules/calculation.rule/calculation.rule.repo.interface.ts @@ -0,0 +1,11 @@ +import { CalculationRuleCreateModel, CalculationRuleUpdateModel, CalculationRuleResponseDto, RuleSearchFilters } from '../../../../domain.types/forms/rule.domain.types'; +import { BaseSearchResults } from '../../../../domain.types/miscellaneous/base.search.types'; +import { uuid } from '../../../../domain.types/miscellaneous/system.types'; + +export interface ICalculationRuleRepo { + create(model: CalculationRuleCreateModel): Promise; + getById(id: uuid): Promise; + update(id: uuid, model: CalculationRuleUpdateModel): Promise; + delete(id: uuid): Promise; + search(filters: RuleSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/field.rules/skip.rule/skip.rule.repo.interface.ts b/src/database/repository.interfaces/field.rules/skip.rule/skip.rule.repo.interface.ts new file mode 100644 index 0000000..c881c4b --- /dev/null +++ b/src/database/repository.interfaces/field.rules/skip.rule/skip.rule.repo.interface.ts @@ -0,0 +1,11 @@ +import { SkipRuleCreateModel, SkipRuleUpdateModel, SkipRuleResponseDto, RuleSearchFilters } from '../../../../domain.types/forms/rule.domain.types'; +import { BaseSearchResults } from '../../../../domain.types/miscellaneous/base.search.types'; +import { uuid } from '../../../../domain.types/miscellaneous/system.types'; + +export interface ISkipRuleRepo { + create(model: SkipRuleCreateModel): Promise; + getById(id: uuid): Promise; + update(id: uuid, model: SkipRuleUpdateModel): Promise; + delete(id: uuid): Promise; + search(filters: RuleSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/field.rules/validation.rule/validation.rule.repo.interface.ts b/src/database/repository.interfaces/field.rules/validation.rule/validation.rule.repo.interface.ts new file mode 100644 index 0000000..ff242e0 --- /dev/null +++ b/src/database/repository.interfaces/field.rules/validation.rule/validation.rule.repo.interface.ts @@ -0,0 +1,11 @@ +import { ValidationRuleCreateModel, ValidationRuleUpdateModel, ValidationRuleResponseDto, RuleSearchFilters } from '../../../../domain.types/forms/rule.domain.types'; +import { BaseSearchResults } from '../../../../domain.types/miscellaneous/base.search.types'; +import { uuid } from '../../../../domain.types/miscellaneous/system.types'; + +export interface IValidationRuleRepo { + create(model: ValidationRuleCreateModel): Promise; + getById(id: uuid): Promise; + update(id: uuid, model: ValidationRuleUpdateModel): Promise; + delete(id: uuid): Promise; + search(filters: RuleSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/form.field/form.field.repo.interface.ts b/src/database/repository.interfaces/form.field/form.field.repo.interface.ts new file mode 100644 index 0000000..73ed0cc --- /dev/null +++ b/src/database/repository.interfaces/form.field/form.field.repo.interface.ts @@ -0,0 +1,18 @@ +import { FormFieldCreateModel, FormFieldResponseDto, FormFieldSearchFilters, FormFieldUpdateModel } from "../../../domain.types/forms/form.field.domain.types"; + + + +export interface IFormFieldRepo { + + create(model: FormFieldCreateModel): Promise; + + update(id: string, model: FormFieldUpdateModel): Promise; + + getById(id: string): Promise; + + getByTemplateId(id: string): Promise; + + delete(id: string): Promise; + + search(filters: FormFieldSearchFilters): Promise; +} \ No newline at end of file diff --git a/src/database/repository.interfaces/question/question.repo.interface.ts b/src/database/repository.interfaces/question/question.repo.interface.ts deleted file mode 100644 index d7979c4..0000000 --- a/src/database/repository.interfaces/question/question.repo.interface.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { QuestionCreateModel, QuestionResponseDto, QuestionSearchFilters, QuestionUpdateModel } from "../../../domain.types/forms/question.domain.types"; - - - -export interface IQuestionRepo{ - - create(model: QuestionCreateModel) : Promise; - - update(id: string, model: QuestionUpdateModel) : Promise; - - getById(id: string) : Promise; - - getByTemplateId(id: string) : Promise; - - delete(id: string) : Promise; - - search(filters: QuestionSearchFilters) : Promise; -} \ No newline at end of file diff --git a/src/database/sql/typeorm/database.connector.typeorm.ts b/src/database/sql/typeorm/database.connector.typeorm.ts index 459c2f6..52d88b2 100644 --- a/src/database/sql/typeorm/database.connector.typeorm.ts +++ b/src/database/sql/typeorm/database.connector.typeorm.ts @@ -1,322 +1,3 @@ -// import "reflect-metadata"; -// import * as fs from 'fs'; -// import * as path from 'path'; -// import { execSync } from 'child_process'; -// // import { Dialect } from 'sequelize'; -// // import { Sequelize } from 'sequelize-typescript'; -// // import { Logger } from '../../../common/logger'; -// import { IPrimaryDatabaseConnector } from '../../database.connector.interface'; -// import { DatabaseSchemaType, databaseConfig } from '../../../common/database.utils/database.config'; -// import { DataSource, DataSourceOptions } from 'typeorm'; -// import { FavoriteTemplate } from './models/favorite.template/favorite.template.model'; -// import { FormSection } from './models/form.section/form.section.model'; -// import { FormSubmission } from './models/form.submission/form.submission.model'; -// import { FormTemplate } from './models/form.template/form.template.model'; -// import { FormTemplateApproval } from './models/form.template.approval/form.template.approval.model'; -// import { InputUnitList } from './models/input.unit.list/input.unit.list.model'; -// import { Question } from './models/question/question.model'; -// import { QuestionResponse } from './models/question.response/question.response.model'; -// import { TemplateFolder } from './models/template.folder/template.folder.model'; -// import { User } from './models/user/user.model'; -// import { Logger } from '../../../startup/logger'; -// import { Injector } from "../../../startup/injector"; -// import { DatabaseClient } from "../../../common/database.utils/dialect.clients/database.client"; - -// const Config = databaseConfig(DatabaseSchemaType.Primary); - -// ////////////////////////////////////////////////////////////// - -// export class DatabaseConnector_TypeOrm implements IPrimaryDatabaseConnector { -// // private _sequelize: Sequelize = null; - -// // public static db: Sequelize = null; - -// public static _source = new DataSource({ -// name: Config.Dialect, -// type: Config.Dialect, -// host: Config.Host, -// port: Config.Port, -// username: Config.Username, -// password: Config.Password, -// database: Config.DatabaseName, -// synchronize: true, -// entities: [ -// // FavoriteTemplate, -// // FormSection, -// // FormSubmission, -// // FormTemplate, -// // FormTemplateApproval, -// // InputUnitList, -// // Question, -// // QuestionResponse, -// // TemplateFolder, -// User, -// ], -// migrations: [], -// subscribers: [], -// // logger: new DBLogger(), -// logger: Logger.instance(), -// logging: true, -// // poolSize: Config.Pool.Max, -// cache: true, -// }); - -// // public connect = async (): Promise => { -// // try { - -// // const config = databaseConfig(DatabaseSchemaType.Primary); -// // const modelsFolder = path.join(__dirname, '/models'); -// // const modelsPath = getFoldersRecursively(modelsFolder); -// // const options = { -// // host : config.Host, -// // dialect : config.Dialect, -// // models : modelsPath, -// // pool : { -// // max : config.Pool.Max, -// // min : config.Pool.Min, -// // acquire : config.Pool.Acquire, -// // idle : config.Pool.Idle, -// // }, -// // logging : false, //TODO: Please provide a function here to handle logging... -// // }; - -// // const sequelize = new Sequelize(config.DatabaseName, config.Username, config.Password, options); -// // this._sequelize = sequelize; - -// // Logger.instance().log(`Connecting to database '${config.DatabaseName}' ...`); - -// // const databaseClient = Injector.Container.resolve(DatabaseClient); -// // await databaseClient.createDb(DatabaseSchemaType.Primary); - -// // await this._sequelize.authenticate(); -// // await this._sequelize.sync({ force: false, alter: true }); - -// // Logger.instance().log(`Connected to database '${config.DatabaseName}'.`); - -// // DatabaseConnector_Sequelize.db = this._sequelize; - -// // return true; - -// // } catch (error) { -// // Logger.instance().log(error.message); -// // return false; -// // } -// // }; - -// public connect = async (): Promise => { -// try { -// const config = databaseConfig(DatabaseSchemaType.Primary); -// // const entitiesFolder = path.join(__dirname, "/models"); -// // const entitiesPath = getFoldersRecursively(entitiesFolder); - -// Logger.instance().log( -// `Connecting to database '${config.DatabaseName}' ...` -// ); - -// // const options : DataSourceOptions= { -// // name: Config.Dialect, -// // type: config.Dialect, -// // host: config.Host, -// // port: config.Port || 3306, -// // username: config.Username, -// // password: config.Password, -// // database: config.DatabaseName, -// // entities: [ -// // // FormTemplate, -// // // FavoriteTemplate, -// // // FormSection, -// // // FormSubmission, -// // // FormTemplateApproval, -// // // InputUnitList, -// // // Question, -// // // QuestionResponse, -// // // TemplateFolder, -// // User, -// // ], -// // synchronize: true, -// // logging: true, -// // migrations: [], -// // subscribers: [], -// // // logger: new DBLogger(), -// // logger: Logger.instance(), -// // cache: true -// // // poolSize: config.Pool?.Max || 10, -// // // extra: { -// // // connectionLimit: config.Pool?.Max || 10, -// // // idleTimeout: config.Pool?.Idle || 10000, -// // // // acquireTimeout: config.Pool?.Acquire || 30000, -// // // }, -// // }; - -// // this._source = new DataSource(options); - -// const databaseClient = Injector.Container.resolve(DatabaseClient); -// await databaseClient.createDb(DatabaseSchemaType.Primary); - -// await this.initialize(); - -// // await this._source.initialize(); - -// Logger.instance().log( -// `${FormTemplate}` -// ); - -// // Logger.instance().log(`Connected to database '${config.DatabaseName}'.`); - -// return true; -// } catch (error) { -// Logger.instance().log( -// error instanceof Error -// ? error.message -// : "Unknown database connection error" -// ); -// return false; -// } -// }; - -// private initialize = (): Promise => { -// return new Promise((resolve, reject) => { -// this._source -// .initialize() -// .then(() => { -// Logger.instance().log('Database connection has been established successfully.'); -// resolve(true); -// }) -// .catch((error) => { -// Logger.instance().log('Unable to connect to the database:' + error.message); -// reject(false); -// }); -// }); -// }; - -// // public db = (): Sequelize => { -// // return this._sequelize; -// // }; - -// // public sync = async () => { -// // try { -// // await this._sequelize.sync({ alter: true }); -// // return true; -// // } catch (error) { -// // Logger.instance().log(error.message); -// // } -// // return false; -// // }; - -// public sync = async () => { -// try { -// if (!this._source.isInitialized) { -// await this._source.initialize(); -// } - -// await this._source.synchronize(); - -// return true; -// } catch (error) { -// Logger.instance().log( -// error instanceof Error ? error.message : "Unknown synchronization error" -// ); -// return false; -// } -// }; - -// // public migrate = async () => { -// // try { -// // const output = execSync('npx sequelize-cli db:migrate'); - -// // const str = output.toString(); -// // Logger.instance().log('Database migrated successfully!'); -// // Logger.instance().log(str); - -// // return true; -// // } catch (error) { -// // Logger.instance().log(error.message); -// // } -// // return false; -// // }; -// public migrate = async (): Promise => { -// try { -// if (!this._source.isInitialized) { -// await this._source.initialize(); -// } - -// // Run pending migrations -// const migrations = await this._source.runMigrations(); - -// if (migrations.length > 0) { -// Logger.instance().log("Database migrated successfully!"); -// migrations.forEach((migration) => { -// Logger.instance().log(`Executed migration: ${migration.name}`); -// }); -// } else { -// Logger.instance().log("No pending migrations found."); -// } - -// return true; -// } catch (error) { -// Logger.instance().log( -// error instanceof Error ? error.message : "Migration error" -// ); -// return false; -// } -// }; - -// // public executeQuery = async (query: string): Promise => { -// // try { -// // const result = await this._sequelize.query(query); -// // return result; -// // } catch (error) { -// // Logger.instance().log(error.message); -// // Logger.instance().log(`Error trace: ${error.stack}`); -// // } -// // return null; -// // }; - -// public executeQuery = async (query: string): Promise => { -// try { -// if (!this._source.isInitialized) { -// await this._source.initialize(); -// } - -// // For SELECT queries that return data -// const result = await this._source.query(query); -// return result; - -// } catch (error) { -// Logger.instance().log(error instanceof Error ? error.message : 'Query execution error'); -// Logger.instance().log(`Error trace: ${error instanceof Error ? error.stack : 'No stack trace'}`); -// throw error; // Consider throwing instead of returning null -// } -// }; -// } - -// /////////////////////////////////////////////////////////////////////////////////////////// - -// function getFoldersRecursively(location: string) { -// const items = fs.readdirSync(location, { withFileTypes: true }); -// let paths = []; -// for (const item of items) { -// if (item.isDirectory()) { -// const fullPath = path.join(location, item.name); -// const childrenPaths = getFoldersRecursively(fullPath); -// paths = [ -// ...paths, -// fullPath, -// ...childrenPaths, -// ]; -// } -// } -// return paths; -// } - -// // const dbTypeOrm= new DatabaseConnector_TypeOrm(); - -// // export const Source = dbTypeOrm._source; - -// const Source = DatabaseConnector_TypeOrm._source; - -// export { DatabaseConnector as DBConnector, Source }; - /* eslint-disable @typescript-eslint/no-unused-vars */ import "reflect-metadata"; import { Config } from "../../../common/database.utils/database.config"; @@ -332,93 +13,118 @@ import { FormSection } from "./models/form.section/form.section.model"; import { FormSubmission } from "./models/form.submission/form.submission.model"; import { FormTemplateApproval } from "./models/form.template.approval/form.template.approval.model"; import { InputUnitList } from "./models/input.unit.list/input.unit.list.model"; -import { Question } from "./models/question/question.model"; import { QuestionResponse } from "./models/question.response/question.response.model"; +import { FormFieldEntity } from "./models/form.field/form.field.model"; import { TemplateFolder } from "./models/template.folder/template.folder.model"; +import { ValidationLogicEntity } from "./models/logic/validation.logic.model"; +import { CalculationLogicEntity } from "./models/logic/calculation.logic.model"; +import { SkipLogicEntity } from "./models/logic/skip.logic.model"; +import { CalculationRuleEntity } from "./models/rule/calculation.rule.model"; +import { SkipRuleEntity } from "./models/rule/skip.rule.model"; +import { ValidationRuleEntity } from "./models/rule/validation.rule.model"; + +import { CompositionOperationEntity } from "./models/operation/composition.operation.model"; +import { FunctionExpressionOperationEntity } from "./models/operation/function.expression.operation.model"; +import { IterateOperationEntity } from "./models/operation/iterate.operation.model"; +import { LogicalOperationEntity } from "./models/operation/logical.operation.model"; +import { MathematicalOperationEntity } from "./models/operation/mathematical.operation.model"; /////////////////////////////////////////////////////////////////////////////////// - Logger.instance().log(`environment : ${process.env.NODE_ENV}`); - Logger.instance().log(`db name : ${Config.database}`); - Logger.instance().log(`db username : ${Config.username}`); - Logger.instance().log(`db host : ${Config.host}`); +Logger.instance().log(`environment : ${process.env.NODE_ENV}`); +Logger.instance().log(`db name : ${Config.database}`); +Logger.instance().log(`db username : ${Config.username}`); +Logger.instance().log(`db host : ${Config.host}`); /////////////////////////////////////////////////////////////////////////////////// class DatabaseConnector implements IPrimaryDatabaseConnector { - static _source = new DataSource({ - name : Config.dialect, - type : Config.dialect, - host : Config.host, - port : Config.port, - username : Config.username, - password : Config.password, - database : Config.database, - synchronize : true, - entities : [ - FormTemplate, - FavoriteTemplate, - FormSection, - FormSubmission, - FormTemplateApproval, - InputUnitList, - Question, - QuestionResponse, - TemplateFolder, - User - ], - migrations : [], - subscribers : [], - logger : Logger.instance(), - logging : true, - poolSize : Config.pool.max, - cache : true, - }); - - static getFoldersRecursively(location: string) { - const items = fs.readdirSync(location, { withFileTypes: true }); - let paths = []; - for (const item of items) { - if (item.isDirectory()) { - const fullPath = path.join(location, item.name); - const childrenPaths = this.getFoldersRecursively(fullPath); - paths = [ - ...paths, - fullPath, - ...childrenPaths, - ]; - } - } - return paths; - } - - public connect=async (): Promise => { - - DatabaseConnector.initialize(); - - return true; + static _source = new DataSource({ + name: Config.dialect, + type: Config.dialect, + host: Config.host, + port: Config.port, + username: Config.username, + password: Config.password, + database: Config.database, + synchronize: true, + entities: [ + FormTemplate, + FavoriteTemplate, + FormSection, + FormSubmission, + FormTemplateApproval, + InputUnitList, + QuestionResponse, + FormFieldEntity, + TemplateFolder, + User, + + SkipLogicEntity, + CalculationLogicEntity, + ValidationLogicEntity, + ValidationRuleEntity, + CalculationRuleEntity, + SkipRuleEntity, + + CompositionOperationEntity, + FunctionExpressionOperationEntity, + IterateOperationEntity, + LogicalOperationEntity, + MathematicalOperationEntity + ], + migrations: [], + subscribers: [], + logger: Logger.instance(), + logging: true, + poolSize: Config.pool.max, + cache: true, + }); + + static getFoldersRecursively(location: string) { + const items = fs.readdirSync(location, { withFileTypes: true }); + let paths = []; + for (const item of items) { + if (item.isDirectory()) { + const fullPath = path.join(location, item.name); + const childrenPaths = this.getFoldersRecursively(fullPath); + paths = [ + ...paths, + fullPath, + ...childrenPaths, + ]; + } } - - static initialize = (): Promise => { - return new Promise((resolve, reject) => { - this._source - .initialize() - .then(() => { - Logger.instance().log('Database connection has been established successfully.'); - resolve(true); - }) - .catch(error => { - Logger.instance().log('Unable to connect to the database:' + error.message); - reject(false); - }); + return paths; + } + + public connect = async (): Promise => { + + DatabaseConnector.initialize(); + + return true; + } + + static initialize = (): Promise => { + return new Promise((resolve, reject) => { + this._source + .initialize() + .then(() => { + Logger.instance().log('Database connection has been established successfully.'); + resolve(true); + }) + .catch(error => { + Logger.instance().log('Unable to connect to the database:' + error.message); + reject(false); }); + }); - }; + }; - public sync = async () => { + public sync = async () => { try { if (!DatabaseConnector._source.isInitialized) { await DatabaseConnector._source.initialize(); @@ -440,7 +146,7 @@ class DatabaseConnector implements IPrimaryDatabaseConnector { // const output = execSync('npx sequelize-cli db:migrate'); // const str = output.toString(); - // Logger.instance().log('Database migrated successfully!'); + // Logger.instance().log('Migration completed successfully!'); // Logger.instance().log(str); // return true; diff --git a/src/database/sql/typeorm/mappers/calculation.logic.mapper.ts b/src/database/sql/typeorm/mappers/calculation.logic.mapper.ts new file mode 100644 index 0000000..27edb47 --- /dev/null +++ b/src/database/sql/typeorm/mappers/calculation.logic.mapper.ts @@ -0,0 +1,47 @@ +import { + CalculationLogicResponseDto, +} from "../../../../domain.types/forms/logic.domain.types"; +import { LogicType } from "../../../../domain.types/forms/logic.enums"; + +export class CalculationLogicMapper { + static toDto = (record: any): CalculationLogicResponseDto => { + if (record === null) { + return null; + } + + const dto: CalculationLogicResponseDto = { + id: record.id, + Type: LogicType.Calculation, + FieldId: record.FieldId, + Enabled: record.Enabled, + FallbackValue: record.FallbackValue, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toCalculationLogicDto = (record: any): CalculationLogicResponseDto => { + if (record === null) { + return null; + } + + const dto: CalculationLogicResponseDto = { + id: record.id, + Type: LogicType.Calculation, + FieldId: record.FieldId, + Enabled: record.Enabled, + FallbackValue: record.FallbackValue, + Rules: record.Rules ? record.Rules.map((rule: any) => ({ + id: rule.id, + OperationId: rule.OperationId, + LogicId: rule.LogicId, + CreatedAt: rule.CreatedAt, + UpdatedAt: rule.UpdatedAt + })) : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/calculation.rule.mapper.ts b/src/database/sql/typeorm/mappers/calculation.rule.mapper.ts new file mode 100644 index 0000000..a4ad8cb --- /dev/null +++ b/src/database/sql/typeorm/mappers/calculation.rule.mapper.ts @@ -0,0 +1,61 @@ +import { + CalculationRuleResponseDto, +} from "../../../../domain.types/forms/rule.domain.types"; + +export class CalculationRuleMapper { + static toDto = (record: any): CalculationRuleResponseDto => { + if (record === null) { + return null; + } + + const dto: CalculationRuleResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Priority: record.Priority, + IsActive: record.IsActive, + ConditionForOperationId: record.ConditionForOperationId, + OperationId: record.OperationId, + LogicId: record.LogicId, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toCalculationRuleDto = (record: any): CalculationRuleResponseDto => { + if (record === null) { + return null; + } + + const dto: CalculationRuleResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Priority: record.Priority, + IsActive: record.IsActive, + ConditionForOperationId: record.ConditionForOperationId, + OperationId: record.OperationId, + LogicId: record.LogicId, + ConditionForOperation: record.ConditionForOperation ? { + id: record.ConditionForOperation.id, + Name: record.ConditionForOperation.Name, + Description: record.ConditionForOperation.Description + } : undefined, + Operation: record.Operation ? { + id: record.Operation.id, + Name: record.Operation.Name, + Description: record.Operation.Description + } : undefined, + Logic: record.Logic ? { + id: record.Logic.id, + Type: record.Logic.Type, + DefaultSkip: record.Logic.DefaultSkip, + FallbackValue: record.Logic.FallbackValue + } : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/composition.operation.mapper.ts b/src/database/sql/typeorm/mappers/composition.operation.mapper.ts new file mode 100644 index 0000000..87d7762 --- /dev/null +++ b/src/database/sql/typeorm/mappers/composition.operation.mapper.ts @@ -0,0 +1,41 @@ +import { + CompositionOperationResponseDto, +} from "../../../../domain.types/forms/operation.domain.types"; + +export class CompositionOperationMapper { + static toDto = (record: any): CompositionOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: CompositionOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Type: record.Type, + Operator: record.Operator, + Operands: record.Operands, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toCompositionOperationDto = (record: any): CompositionOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: CompositionOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Type: record.Type, + Operator: record.Operator, + Operands: record.Operands, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/form.field.mapper.ts b/src/database/sql/typeorm/mappers/form.field.mapper.ts index 6170cb8..118cf6b 100644 --- a/src/database/sql/typeorm/mappers/form.field.mapper.ts +++ b/src/database/sql/typeorm/mappers/form.field.mapper.ts @@ -1,6 +1,4 @@ -import { - FormFieldResponseDto -} from "../../../../domain.types/forms/form.field.domain.types"; +import { FormFieldOption, FormFieldResponseDto } from "../../../../domain.types/forms/form.field.domain.types"; export class FormFieldMapper { static toDto = (record: any): FormFieldResponseDto => { @@ -8,74 +6,58 @@ export class FormFieldMapper { return null; } + // Parse the Options JSON if it's present + let options: FormFieldOption[] = []; + if (record.Options !== null && record.Options !== undefined) { + try { + options = typeof record.Options === 'string' ? JSON.parse(record.Options) : record.Options; + } catch (error) { + options = []; + } + } + + // Map the record to FormFieldResponseDto const dto: FormFieldResponseDto = { id: record.id, - Name: record.Name, - Label: record.Label, Title: record.Title, Description: record.Description, - DisplayCode: record.DisplayCode, + DisplayCode: record.DisplayCode ?? null, ResponseType: record.ResponseType, - QueryResponseType: record.QueryResponseType, - Required: record.Required, - Value: record.Value, Score: record.Score, Sequence: record.Sequence, - ExpectedAnswer: record.ExpectedAnswer, + CorrectAnswer: record.ExpectedAnswer, + IsRequired: record.IsRequired, Hint: record.Hint, - Options: record.Options, - ImageResourceId: record.ImageResourceId, + Options: options, + QuestionImageUrl: record.ImageResourceId, RangeMin: record.RangeMin, RangeMax: record.RangeMax, - DefaultExpectedUnit: record.DefaultExpectedUnit, - PageBreakAfter: record.PageBreakAfter, - SkipLogicId: record.SkipLogicId, - CalculateLogicId: record.CalculateLogicId, - ValidateLogicId: record.ValidateLogicId, - TemplateId: record.TemplateId, - ParentSectionId: record.ParentSectionId, - FormId: record.FormId, - SkipLogic: record.SkipLogic ? { - id: record.SkipLogic.id, - Type: record.SkipLogic.Type, - DefaultSkip: record.SkipLogic.DefaultSkip - } : undefined, - CalculateLogic: record.CalculateLogic ? { - id: record.CalculateLogic.id, - Type: record.CalculateLogic.Type, - DefaultSkip: record.CalculateLogic.DefaultSkip - } : undefined, - ValidateLogic: record.ValidateLogic ? { - id: record.ValidateLogic.id, - Type: record.ValidateLogic.Type, - DefaultSkip: record.ValidateLogic.DefaultSkip - } : undefined, - FormTemplate: record.FormTemplate ? { - id: record.FormTemplate.id, - Title: record.FormTemplate.Title, - Description: record.FormTemplate.Description, - DisplayCode: record.FormTemplate.DisplayCode - } : undefined, ParentFormSection: record.ParentFormSection ? { id: record.ParentFormSection.id, SectionIdentifier: record.ParentFormSection.SectionIdentifier, Title: record.ParentFormSection.Title, Description: record.ParentFormSection.Description, - DisplayCode: record.ParentFormSection.DisplayCode - } : undefined, - ExpectedInputUnitList: record.ExpectedInputUnitList ? { - id: record.ExpectedInputUnitList.id, - Name: record.ExpectedInputUnitList.Name, - Description: record.ExpectedInputUnitList.Description - } : undefined, - Responses: record.Responses ? record.Responses.map((response: any) => ({ - id: response.id, - ResponseValue: response.ResponseValue, - CreatedAt: response.CreatedAt - })) : undefined, + DisplayCode: record.ParentFormSection.DisplayCode, + Sequence: record.ParentFormSection.Sequence, + ParentSectionId: record.ParentFormSection.ParentSectionId, + CreatedAt: record.ParentFormSection.CreatedAt, + } : null, + ParentFormTemplate: record.FormTemplate ? { + id: record.FormTemplate.id, + Title: record.FormTemplate.Title, + Description: record.FormTemplate.Description, + CurrentVersion: record.FormTemplate.Version, + Type: record.FormTemplate.Type, + DisplayCode: record.FormTemplate.DisplayCode, + OwnerUserId: record.FormTemplate.OwnerUserId, + RootSectionId: record.FormTemplate.RootSectionId, + DefaultSectionNumbering: record.FormTemplate.DefaultSectionNumbering, + CreatedAt: record.FormTemplate.CreatedAt, + } : null, CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt + UpdatedAt: record.UpdatedAt, }; + return dto; }; diff --git a/src/database/sql/typeorm/mappers/function.expression.operation.mapper.ts b/src/database/sql/typeorm/mappers/function.expression.operation.mapper.ts new file mode 100644 index 0000000..36a4485 --- /dev/null +++ b/src/database/sql/typeorm/mappers/function.expression.operation.mapper.ts @@ -0,0 +1,43 @@ +import { + FunctionExpressionOperationResponseDto, +} from "../../../../domain.types/forms/operation.domain.types"; + +export class FunctionExpressionOperationMapper { + static toDto = (record: any): FunctionExpressionOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: FunctionExpressionOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Type: record.Type, + Expression: record.Expression, + Variables: record.Variables, + ResultDataType: record.ResultDataType, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toFunctionExpressionOperationDto = (record: any): FunctionExpressionOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: FunctionExpressionOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Type: record.Type, + Expression: record.Expression, + Variables: record.Variables, + ResultDataType: record.ResultDataType, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/iterate.operation.mapper.ts b/src/database/sql/typeorm/mappers/iterate.operation.mapper.ts new file mode 100644 index 0000000..0360dba --- /dev/null +++ b/src/database/sql/typeorm/mappers/iterate.operation.mapper.ts @@ -0,0 +1,50 @@ +import { + IterateOperationResponseDto, +} from "../../../../domain.types/forms/operation.domain.types"; + +export class IterateOperationMapper { + static toDto = (record: any): IterateOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: IterateOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Type: record.Type, + CollectionField: record.CollectionField, + ResultField: record.ResultField, + OperationId: record.OperationId, + FilterExpression: record.FilterExpression, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toIterateOperationDto = (record: any): IterateOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: IterateOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Type: record.Type, + CollectionField: record.CollectionField, + ResultField: record.ResultField, + OperationId: record.OperationId, + FilterExpression: record.FilterExpression, + Operation: record.Operation ? { + id: record.Operation.id, + Name: record.Operation.Name, + Description: record.Operation.Description + } : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/logic.mapper.ts b/src/database/sql/typeorm/mappers/logic.mapper.ts deleted file mode 100644 index f6e2910..0000000 --- a/src/database/sql/typeorm/mappers/logic.mapper.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { - BaseLogicResponseDto, - SkipLogicResponseDto, - CalculationLogicResponseDto, - ValidationLogicResponseDto, - LegacyLogicResponseDto -} from "../../../../domain.types/forms/logic.domain.types"; - -export class LogicMapper { - static toBaseDto = (record: any): BaseLogicResponseDto => { - if (record === null) { - return null; - } - - const dto: BaseLogicResponseDto = { - id: record.id, - Type: record.Type, - DefaultSkip: record.DefaultSkip, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toSkipLogicDto = (record: any): SkipLogicResponseDto => { - if (record === null) { - return null; - } - - const dto: SkipLogicResponseDto = { - id: record.id, - Type: record.Type, - DefaultSkip: record.DefaultSkip, - Rules: record.Rules ? record.Rules.map((rule: any) => ({ - id: rule.id, - OperationId: rule.OperationId, - SkipWhenTrue: rule.SkipWhenTrue, - LogicId: rule.LogicId, - CreatedAt: rule.CreatedAt, - UpdatedAt: rule.UpdatedAt - })) : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toCalculationLogicDto = (record: any): CalculationLogicResponseDto => { - if (record === null) { - return null; - } - - const dto: CalculationLogicResponseDto = { - id: record.id, - Type: record.Type, - DefaultSkip: record.DefaultSkip, - FallbackValue: record.FallbackValue, - Rules: record.Rules ? record.Rules.map((rule: any) => ({ - id: rule.id, - ConditionForOperationId: rule.ConditionForOperationId, - OperationId: rule.OperationId, - LogicId: rule.LogicId, - CreatedAt: rule.CreatedAt, - UpdatedAt: rule.UpdatedAt - })) : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toValidationLogicDto = (record: any): ValidationLogicResponseDto => { - if (record === null) { - return null; - } - - const dto: ValidationLogicResponseDto = { - id: record.id, - Type: record.Type, - DefaultSkip: record.DefaultSkip, - Rules: record.Rules ? record.Rules.map((rule: any) => ({ - id: rule.id, - OperationId: rule.OperationId, - ErrorWhenFalse: rule.ErrorWhenFalse, - ErrorMessage: rule.ErrorMessage, - LogicId: rule.LogicId, - CreatedAt: rule.CreatedAt, - UpdatedAt: rule.UpdatedAt - })) : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toLegacyLogicDto = (record: any): LegacyLogicResponseDto => { - if (record === null) { - return null; - } - - const dto: LegacyLogicResponseDto = { - id: record.id, - Type: record.Type, - DefaultSkip: record.DefaultSkip, - Rules: record.Rules ? record.Rules.map((rule: any) => ({ - id: rule.id, - OperationId: rule.OperationId, - LogicId: rule.LogicId, - CreatedAt: rule.CreatedAt, - UpdatedAt: rule.UpdatedAt - })) : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toArrayBaseDto(records: any[]): BaseLogicResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => LogicMapper.toBaseDto(record)); - } - - static toArraySkipLogicDto(records: any[]): SkipLogicResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => LogicMapper.toSkipLogicDto(record)); - } - - static toArrayCalculationLogicDto(records: any[]): CalculationLogicResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => LogicMapper.toCalculationLogicDto(record)); - } - - static toArrayValidationLogicDto(records: any[]): ValidationLogicResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => LogicMapper.toValidationLogicDto(record)); - } - - static toArrayLegacyLogicDto(records: any[]): LegacyLogicResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => LogicMapper.toLegacyLogicDto(record)); - } -} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/logical.operation.mapper.ts b/src/database/sql/typeorm/mappers/logical.operation.mapper.ts new file mode 100644 index 0000000..ce49f1d --- /dev/null +++ b/src/database/sql/typeorm/mappers/logical.operation.mapper.ts @@ -0,0 +1,41 @@ +import { + LogicalOperationResponseDto, +} from "../../../../domain.types/forms/operation.domain.types"; + +export class LogicalOperationMapper { + static toDto = (record: any): LogicalOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: LogicalOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Type: record.Type, + Operator: record.Operator, + Operands: record.Operands, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toLogicalOperationDto = (record: any): LogicalOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: LogicalOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Type: record.Type, + Operator: record.Operator, + Operands: record.Operands, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/mathematical.operation.mapper.ts b/src/database/sql/typeorm/mappers/mathematical.operation.mapper.ts new file mode 100644 index 0000000..1e2ea04 --- /dev/null +++ b/src/database/sql/typeorm/mappers/mathematical.operation.mapper.ts @@ -0,0 +1,43 @@ +import { + MathematicalOperationResponseDto, +} from "../../../../domain.types/forms/operation.domain.types"; + +export class MathematicalOperationMapper { + static toDto = (record: any): MathematicalOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: MathematicalOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Type: record.Type, + Operator: record.Operator, + Operands: record.Operands, + ResultDataType: record.ResultDataType, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toMathematicalOperationDto = (record: any): MathematicalOperationResponseDto => { + if (record === null) { + return null; + } + + const dto: MathematicalOperationResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Type: record.Type, + Operator: record.Operator, + Operands: record.Operands, + ResultDataType: record.ResultDataType, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/operation.mapper.ts b/src/database/sql/typeorm/mappers/operation.mapper.ts deleted file mode 100644 index 1669da6..0000000 --- a/src/database/sql/typeorm/mappers/operation.mapper.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { - BaseOperationResponseDto, - LogicalOperationResponseDto, - MathematicalOperationResponseDto, - CompositionOperationResponseDto, - IterateOperationResponseDto, - FunctionExpressionOperationResponseDto -} from "../../../../domain.types/forms/operation.domain.types"; - -export class OperationMapper { - static toBaseDto = (record: any): BaseOperationResponseDto => { - if (record === null) { - return null; - } - - const dto: BaseOperationResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toLogicalOperationDto = (record: any): LogicalOperationResponseDto => { - if (record === null) { - return null; - } - - const dto: LogicalOperationResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - OperatorType: record.OperatorType, - LeftOperand: record.LeftOperand, - RightOperand: record.RightOperand, - Parameters: record.Parameters, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toMathematicalOperationDto = (record: any): MathematicalOperationResponseDto => { - if (record === null) { - return null; - } - - const dto: MathematicalOperationResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - OperatorType: record.OperatorType, - LeftOperand: record.LeftOperand, - RightOperand: record.RightOperand, - Parameters: record.Parameters, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toCompositionOperationDto = (record: any): CompositionOperationResponseDto => { - if (record === null) { - return null; - } - - const dto: CompositionOperationResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - OperatorType: record.OperatorType, - LeftOperand: record.LeftOperand, - RightOperand: record.RightOperand, - Parameters: record.Parameters, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toIterateOperationDto = (record: any): IterateOperationResponseDto => { - if (record === null) { - return null; - } - - const dto: IterateOperationResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - ArrayOperand: record.ArrayOperand, - ItemAlias: record.ItemAlias, - OperationId: record.OperationId, - Operation: record.Operation ? { - id: record.Operation.id, - Name: record.Operation.Name, - Description: record.Operation.Description - } : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toFunctionExpressionOperationDto = (record: any): FunctionExpressionOperationResponseDto => { - if (record === null) { - return null; - } - - const dto: FunctionExpressionOperationResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - FunctionName: record.FunctionName, - Parameters: record.Parameters, - Expression: record.Expression, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toArrayBaseDto(records: any[]): BaseOperationResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => OperationMapper.toBaseDto(record)); - } - - static toArrayLogicalOperationDto(records: any[]): LogicalOperationResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => OperationMapper.toLogicalOperationDto(record)); - } - - static toArrayMathematicalOperationDto(records: any[]): MathematicalOperationResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => OperationMapper.toMathematicalOperationDto(record)); - } - - static toArrayCompositionOperationDto(records: any[]): CompositionOperationResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => OperationMapper.toCompositionOperationDto(record)); - } - - static toArrayIterateOperationDto(records: any[]): IterateOperationResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => OperationMapper.toIterateOperationDto(record)); - } - - static toArrayFunctionExpressionOperationDto(records: any[]): FunctionExpressionOperationResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => OperationMapper.toFunctionExpressionOperationDto(record)); - } -} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/question.mapper.ts b/src/database/sql/typeorm/mappers/question.mapper.ts deleted file mode 100644 index 3aa7243..0000000 --- a/src/database/sql/typeorm/mappers/question.mapper.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { QuestionOption, QuestionResponseDto } from "../../../../domain.types/forms/question.domain.types"; - -// export class QuestionMapper { -// static toDto = (record: any): QuestionResponseDto => { -// if (record === null) { -// return null; -// } - -// var options = []; -// if (record.Options !== null && record.Options !== undefined) { -// options = JSON.parse(record.Options); -// } -// const dto: QuestionResponseDto = { -// id : record.id, -// Title : record.Title, -// Description : record.Description, -// DisplayCode : record.DisplayCode ?? null, -// ResponseType : record.ResponseType, -// Score : record.Score, -// Sequence : record.Sequence, -// CorrectAnswer : record.CorrectAnswer, -// Hint : record.Hint, -// Options : options, -// QuestionImageUrl: record.QuestionImageUrl, -// RangeMin : record.RangeMin, -// RangeMax : record.RangeMax, -// ParentFormSection: { -// id : record.ParentFormSection.id, -// SectionIdentifier: record.ParentFormSection.SectionIdentifier, -// Title : record.ParentFormSection.Title, -// Description : record.ParentFormSection.Description, -// DisplayCode : record.ParentFormSection.DisplayCode, -// Sequence : record.ParentFormSection.Sequence, -// ParentSectionId : record.ParentFormSection.ParentSectionId, -// CreatedAt : record.ParentFormSection.CreatedAt -// }, -// ParentFormTemplate: { -// id : record.ParentFormTemplate.id, -// Title : record.ParentFormTemplate.Title, -// Description : record.ParentFormTemplate.Description, -// CurrentVersion : record.ParentFormTemplate.CorrectAnswer, -// Type : record.ParentFormTemplate.Type, -// DisplayCode : record.ParentFormTemplate.DisplayCode, -// OwnerUserId : record.ParentFormTemplate.OwnerUserId, -// RootSectionId : record.ParentFormTemplate.RootSectionId, -// DefaultSectionNumbering: record.ParentFormTemplate.DefaultSectionNumbering, -// CreatedAt : record.ParentFormTemplate.CreatedAt -// }, -// CreatedAt: record.CreatedAt, -// UpdatedAt: record.UpdatedAt -// }; -// return dto; -// }; - -// static toArrayDto(records: any[]): QuestionResponseDto[] { -// if (records === null) { -// return []; -// } -// return records.map(record => QuestionMapper.toDto(record)); -// } -// } -export class QuestionMapper { - static toDto = (record: any): QuestionResponseDto => { - if (record === null) { - return null; - } - - // Parse the Options JSON if it's present - let options: QuestionOption[] = []; - if (record.Options !== null && record.Options !== undefined) { - options = record.Options as QuestionOption[]; - } - - // Map the record to QuestionResponseDto - const dto: QuestionResponseDto = { - id: record.id, - Title: record.Title, - Description: record.Description, - DisplayCode: record.DisplayCode ?? null, - ResponseType: record.ResponseType, - Score: record.Score, - Sequence: record.Sequence, - CorrectAnswer: record.CorrectAnswer, - IsRequired:record.IsRequired, - Hint: record.Hint, - Options: options, - QuestionImageUrl: record.QuestionImageUrl, - RangeMin: record.RangeMin, - RangeMax: record.RangeMax, - ParentFormSection: record.ParentFormSection ? { - id: record.ParentFormSection.id, - SectionIdentifier: record.ParentFormSection.SectionIdentifier, - Title: record.ParentFormSection.Title, - Description: record.ParentFormSection.Description, - DisplayCode: record.ParentFormSection.DisplayCode, - Sequence: record.ParentFormSection.Sequence, - ParentSectionId: record.ParentFormSection.ParentSectionId, - CreatedAt: record.ParentFormSection.CreatedAt, - } : null, - ParentFormTemplate: record.ParentFormTemplate ? { - id: record.ParentFormTemplate.id, - Title: record.ParentFormTemplate.Title, - Description: record.ParentFormTemplate.Description, - CurrentVersion: record.ParentFormTemplate.CorrectAnswer, - Type: record.ParentFormTemplate.Type, - DisplayCode: record.ParentFormTemplate.DisplayCode, - OwnerUserId: record.ParentFormTemplate.OwnerUserId, - RootSectionId: record.ParentFormTemplate.RootSectionId, - DefaultSectionNumbering: record.ParentFormTemplate.DefaultSectionNumbering, - CreatedAt: record.ParentFormTemplate.CreatedAt, - } : null, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt, - }; - - return dto; - }; - - static toArrayDto(records: any[]): QuestionResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => QuestionMapper.toDto(record)); - } -} diff --git a/src/database/sql/typeorm/mappers/rule.mapper.ts b/src/database/sql/typeorm/mappers/rule.mapper.ts deleted file mode 100644 index c3e8586..0000000 --- a/src/database/sql/typeorm/mappers/rule.mapper.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { - BaseRuleResponseDto, - SkipRuleResponseDto, - CalculationRuleResponseDto, - ValidationRuleResponseDto, - LegacyRuleResponseDto -} from "../../../../domain.types/forms/rule.domain.types"; - -export class RuleMapper { - static toBaseDto = (record: any): BaseRuleResponseDto => { - if (record === null) { - return null; - } - - const dto: BaseRuleResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Priority: record.Priority, - IsActive: record.IsActive, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toSkipRuleDto = (record: any): SkipRuleResponseDto => { - if (record === null) { - return null; - } - - const dto: SkipRuleResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Priority: record.Priority, - IsActive: record.IsActive, - OperationId: record.OperationId, - SkipWhenTrue: record.SkipWhenTrue, - LogicId: record.LogicId, - Operation: record.Operation ? { - id: record.Operation.id, - Name: record.Operation.Name, - Description: record.Operation.Description - } : undefined, - Logic: record.Logic ? { - id: record.Logic.id, - Type: record.Logic.Type, - DefaultSkip: record.Logic.DefaultSkip - } : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toCalculationRuleDto = (record: any): CalculationRuleResponseDto => { - if (record === null) { - return null; - } - - const dto: CalculationRuleResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Priority: record.Priority, - IsActive: record.IsActive, - ConditionForOperationId: record.ConditionForOperationId, - OperationId: record.OperationId, - LogicId: record.LogicId, - ConditionForOperation: record.ConditionForOperation ? { - id: record.ConditionForOperation.id, - Name: record.ConditionForOperation.Name, - Description: record.ConditionForOperation.Description - } : undefined, - Operation: record.Operation ? { - id: record.Operation.id, - Name: record.Operation.Name, - Description: record.Operation.Description - } : undefined, - Logic: record.Logic ? { - id: record.Logic.id, - Type: record.Logic.Type, - DefaultSkip: record.Logic.DefaultSkip, - FallbackValue: record.Logic.FallbackValue - } : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toValidationRuleDto = (record: any): ValidationRuleResponseDto => { - if (record === null) { - return null; - } - - const dto: ValidationRuleResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Priority: record.Priority, - IsActive: record.IsActive, - OperationId: record.OperationId, - ErrorWhenFalse: record.ErrorWhenFalse, - ErrorMessage: record.ErrorMessage, - LogicId: record.LogicId, - Operation: record.Operation ? { - id: record.Operation.id, - Name: record.Operation.Name, - Description: record.Operation.Description - } : undefined, - Logic: record.Logic ? { - id: record.Logic.id, - Type: record.Logic.Type, - DefaultSkip: record.Logic.DefaultSkip - } : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toLegacyRuleDto = (record: any): LegacyRuleResponseDto => { - if (record === null) { - return null; - } - - const dto: LegacyRuleResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Priority: record.Priority, - IsActive: record.IsActive, - OperationId: record.OperationId, - LogicId: record.LogicId, - Operation: record.Operation ? { - id: record.Operation.id, - Name: record.Operation.Name, - Description: record.Operation.Description - } : undefined, - Logic: record.Logic ? { - id: record.Logic.id, - Type: record.Logic.Type, - DefaultSkip: record.Logic.DefaultSkip - } : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; - - static toArrayBaseDto(records: any[]): BaseRuleResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => RuleMapper.toBaseDto(record)); - } - - static toArraySkipRuleDto(records: any[]): SkipRuleResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => RuleMapper.toSkipRuleDto(record)); - } - - static toArrayCalculationRuleDto(records: any[]): CalculationRuleResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => RuleMapper.toCalculationRuleDto(record)); - } - - static toArrayValidationRuleDto(records: any[]): ValidationRuleResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => RuleMapper.toValidationRuleDto(record)); - } - - static toArrayLegacyRuleDto(records: any[]): LegacyRuleResponseDto[] { - if (records === null) { - return []; - } - return records.map(record => RuleMapper.toLegacyRuleDto(record)); - } -} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/skip.logic.mapper.ts b/src/database/sql/typeorm/mappers/skip.logic.mapper.ts new file mode 100644 index 0000000..0b227ea --- /dev/null +++ b/src/database/sql/typeorm/mappers/skip.logic.mapper.ts @@ -0,0 +1,48 @@ +import { + SkipLogicResponseDto, +} from "../../../../domain.types/forms/logic.domain.types"; +import { LogicType } from "../../../../domain.types/forms/logic.enums"; + +export class SkipLogicMapper { + static toDto = (record: any): SkipLogicResponseDto => { + if (record === null) { + return null; + } + + const dto: SkipLogicResponseDto = { + id: record.id, + Type: LogicType.Skip, + FieldId: record.FieldId, + Enabled: record.Enabled, + DefaultSkip: record.DefaultSkip, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toSkipLogicDto = (record: any): SkipLogicResponseDto => { + if (record === null) { + return null; + } + + const dto: SkipLogicResponseDto = { + id: record.id, + Type: LogicType.Skip, + FieldId: record.FieldId, + Enabled: record.Enabled, + DefaultSkip: record.DefaultSkip, + Rules: record.Rules ? record.Rules.map((rule: any) => ({ + id: rule.id, + OperationId: rule.OperationId, + SkipWhenTrue: rule.SkipWhenTrue, + LogicId: rule.LogicId, + CreatedAt: rule.CreatedAt, + UpdatedAt: rule.UpdatedAt + })) : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/skip.rule.mapper.ts b/src/database/sql/typeorm/mappers/skip.rule.mapper.ts new file mode 100644 index 0000000..2d588f6 --- /dev/null +++ b/src/database/sql/typeorm/mappers/skip.rule.mapper.ts @@ -0,0 +1,55 @@ +import { + SkipRuleResponseDto, +} from "../../../../domain.types/forms/rule.domain.types"; + +export class SkipRuleMapper { + static toDto = (record: any): SkipRuleResponseDto => { + if (record === null) { + return null; + } + + const dto: SkipRuleResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Priority: record.Priority, + IsActive: record.IsActive, + OperationId: record.OperationId, + SkipWhenTrue: record.SkipWhenTrue, + LogicId: record.LogicId, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toSkipRuleDto = (record: any): SkipRuleResponseDto => { + if (record === null) { + return null; + } + + const dto: SkipRuleResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Priority: record.Priority, + IsActive: record.IsActive, + OperationId: record.OperationId, + SkipWhenTrue: record.SkipWhenTrue, + LogicId: record.LogicId, + Operation: record.Operation ? { + id: record.Operation.id, + Name: record.Operation.Name, + Description: record.Operation.Description + } : undefined, + Logic: record.Logic ? { + id: record.Logic.id, + Type: record.Logic.Type, + DefaultSkip: record.Logic.DefaultSkip + } : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/validation.logic.mapper.ts b/src/database/sql/typeorm/mappers/validation.logic.mapper.ts new file mode 100644 index 0000000..6d4b4bf --- /dev/null +++ b/src/database/sql/typeorm/mappers/validation.logic.mapper.ts @@ -0,0 +1,45 @@ +import { + ValidationLogicResponseDto, +} from "../../../../domain.types/forms/logic.domain.types"; +import { LogicType } from "../../../../domain.types/forms/logic.enums"; + +export class ValidationLogicMapper { + static toDto = (record: any): ValidationLogicResponseDto => { + if (record === null) { + return null; + } + + const dto: ValidationLogicResponseDto = { + id: record.id, + Type: LogicType.Validation, + FieldId: record.FieldId, + Enabled: record.Enabled, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toValidationLogicDto = (record: any): ValidationLogicResponseDto => { + if (record === null) { + return null; + } + + const dto: ValidationLogicResponseDto = { + id: record.id, + Type: LogicType.Validation, + FieldId: record.FieldId, + Enabled: record.Enabled, + Rules: record.Rules ? record.Rules.map((rule: any) => ({ + id: rule.id, + OperationId: rule.OperationId, + LogicId: rule.LogicId, + CreatedAt: rule.CreatedAt, + UpdatedAt: rule.UpdatedAt + })) : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/validation.rule.mapper.ts b/src/database/sql/typeorm/mappers/validation.rule.mapper.ts new file mode 100644 index 0000000..5c29bed --- /dev/null +++ b/src/database/sql/typeorm/mappers/validation.rule.mapper.ts @@ -0,0 +1,57 @@ +import { + ValidationRuleResponseDto, +} from "../../../../domain.types/forms/rule.domain.types"; + +export class ValidationRuleMapper { + static toDto = (record: any): ValidationRuleResponseDto => { + if (record === null) { + return null; + } + + const dto: ValidationRuleResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Priority: record.Priority, + IsActive: record.IsActive, + OperationId: record.OperationId, + ErrorWhenFalse: record.ErrorWhenFalse, + ErrorMessage: record.ErrorMessage, + LogicId: record.LogicId, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; + + static toValidationRuleDto = (record: any): ValidationRuleResponseDto => { + if (record === null) { + return null; + } + + const dto: ValidationRuleResponseDto = { + id: record.id, + Name: record.Name, + Description: record.Description, + Priority: record.Priority, + IsActive: record.IsActive, + OperationId: record.OperationId, + ErrorWhenFalse: record.ErrorWhenFalse, + ErrorMessage: record.ErrorMessage, + LogicId: record.LogicId, + Operation: record.Operation ? { + id: record.Operation.id, + Name: record.Operation.Name, + Description: record.Operation.Description + } : undefined, + Logic: record.Logic ? { + id: record.Logic.id, + Type: record.Logic.Type, + DefaultSkip: record.Logic.DefaultSkip + } : undefined, + CreatedAt: record.CreatedAt, + UpdatedAt: record.UpdatedAt + }; + return dto; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/form.field/field.types.ts b/src/database/sql/typeorm/models/form.field/field.types.ts deleted file mode 100644 index 4ba1c73..0000000 --- a/src/database/sql/typeorm/models/form.field/field.types.ts +++ /dev/null @@ -1,55 +0,0 @@ -// Field Response Types -export enum FieldResponseType { - Text = 'Text', - Number = 'Number', - Boolean = 'Boolean', - Date = 'Date', - DateTime = 'DateTime', - Time = 'Time', - Email = 'Email', - Phone = 'Phone', - URL = 'URL', - Password = 'Password', - TextArea = 'TextArea', - Select = 'Select', - MultiSelect = 'MultiSelect', - Radio = 'Radio', - Checkbox = 'Checkbox', - File = 'File', - Image = 'Image', - Signature = 'Signature', - Location = 'Location', - Rating = 'Rating', - Slider = 'Slider', - Color = 'Color', - RichText = 'RichText', - JSON = 'JSON' -} - -// Field Response Types -// export enum FieldResponseType { -// Text = 'Text', -// Number = 'Number', -// Boolean = 'Boolean', -// Date = 'Date', -// DateTime = 'DateTime', -// Time = 'Time', -// Email = 'Email', -// Phone = 'Phone', -// URL = 'URL', -// Password = 'Password', -// TextArea = 'TextArea', -// Select = 'Select', -// MultiSelect = 'MultiSelect', -// Radio = 'Radio', -// Checkbox = 'Checkbox', -// File = 'File', -// Image = 'Image', -// Signature = 'Signature', -// Location = 'Location', -// Rating = 'Rating', -// Slider = 'Slider', -// Color = 'Color', -// RichText = 'RichText', -// JSON = 'JSON' -// } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/form.field/form.field.model.ts b/src/database/sql/typeorm/models/form.field/form.field.model.ts index fd072fb..a392244 100644 --- a/src/database/sql/typeorm/models/form.field/form.field.model.ts +++ b/src/database/sql/typeorm/models/form.field/form.field.model.ts @@ -1,246 +1,76 @@ -import { Entity, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn, OneToMany } from 'typeorm'; -import { FieldResponseType } from './field.types'; -import { SkipLogicEntity } from '../logic/skip.logic.model'; -import { CalculationLogicEntity } from '../logic/calculation.logic.model'; -import { ValidationLogicEntity } from '../logic/validation.logic.model'; -import { BaseEntity } from '../base.entity'; -import { QueryResponseType } from '../../../../../domain.types/forms/query.response.types'; +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, CreateDateColumn, UpdateDateColumn, JoinColumn } from 'typeorm'; import { FormTemplate } from '../form.template/form.template.model'; import { FormSection } from '../form.section/form.section.model'; +import { BaseEntity } from '../base.entity'; import { QuestionResponse } from '../question.response/question.response.model'; import { InputUnitList } from '../input.unit.list/input.unit.list.model'; +import { QueryResponseType } from '../../../../../domain.types/forms/query.response.types'; -// Form Field Interface -// export interface FormField { -// FieldId: string; -// Name: string; -// Label: string; -// Title?: string; -// Description?: string; -// DisplayCode?: string; -// ResponseType: FieldResponseType; -// QueryResponseType?: QueryResponseType; -// Required: boolean; -// Value?: any; -// Score?: number; -// Sequence?: number; -// ExpectedAnswer?: string; -// Hint?: string; -// Options?: string; -// ImageResourceId?: string; -// RangeMin?: number; -// RangeMax?: number; -// DefaultExpectedUnit?: string; -// PageBreakAfter?: boolean; -// SkipLogic?: SkipLogicEntity | null; -// CalculateLogic?: CalculationLogicEntity | null; -// ValidateLogic?: ValidationLogicEntity | null; -// } - -// Comprehensive Form Field Entity combining Question and FormField models @Entity({ name: 'form_fields' }) export class FormFieldEntity extends BaseEntity { - // Basic Form Field Properties - @Column({ - type: 'varchar', - length: 255, - nullable: false - }) - Name: string; - - @Column({ - type: 'varchar', - length: 255, - nullable: false - }) - Label: string; - - // Question Model Properties - @Column({ - type: 'varchar', - length: 128, - nullable: true - }) - Title?: string; - - @Column({ - type: 'varchar', - length: 512, - nullable: true - }) - Description?: string; - - @Column({ - type: 'varchar', - length: 128, - nullable: true - }) - DisplayCode?: string; - - // Response Types - @Column({ - type: 'varchar', - length: 50, - nullable: false - }) - ResponseType: FieldResponseType; - - @Column({ - type: 'enum', - enum: QueryResponseType, - nullable: true - }) - QueryResponseType?: QueryResponseType; - - // Form Field Properties - @Column({ - type: 'boolean', - nullable: false, - default: false - }) - Required: boolean; - - @Column({ - type: 'text', - nullable: true - }) - Value?: string; // JSON serialized any - - // Question Model Additional Properties - @Column({ - type: 'int', - nullable: true - }) - Score?: number; - - @Column({ - type: 'int', - nullable: true - }) - Sequence?: number; - - @Column({ - type: 'varchar', - nullable: true - }) - ExpectedAnswer?: string; - - @Column({ - type: 'varchar', - length: 512, - nullable: true - }) - Hint?: string; - - @Column({ - type: 'json', - nullable: true - }) - Options?: string; - - @Column({ - type: 'uuid', - nullable: true - }) + + @Column({ type: 'uuid', nullable: true }) + TemplateId: string; + + @Column({ type: 'uuid', nullable: false }) + ParentSectionId: string; + + @Column({ type: 'varchar', length: 128, nullable: true }) + Title: string; + + @Column({ type: 'varchar', length: 512, nullable: true }) + Description: string; + + @Column({ type: 'varchar', length: 128, nullable: false }) + DisplayCode: string; + + @Column({ type: 'enum', enum: QueryResponseType }) + ResponseType: QueryResponseType; + + @Column({ type: 'int', nullable: true }) + Score: number; + + @Column({ type: 'int', nullable: true }) + Sequence: number; + + @Column({ type: 'varchar', nullable: true }) + ExpectedAnswer: string; + + @Column({ type: 'boolean', nullable: true }) + IsRequired: boolean; + + @Column({ type: 'varchar', length: 512, nullable: true }) + Hint: string; + + @Column({ type: 'json', nullable: true }) + Options: string; + + @Column({ type: 'uuid', nullable: true }) ImageResourceId?: string; - @Column({ - type: 'int', - nullable: true - }) - RangeMin?: number; - - @Column({ - type: 'int', - nullable: true - }) - RangeMax?: number; - - @Column({ - type: 'varchar', - nullable: true - }) - DefaultExpectedUnit?: string; - - @Column({ - type: 'boolean', - nullable: false, - default: false - }) + @Column({ type: 'int', nullable: true }) + RangeMin: number; + + @Column({ type: 'int', nullable: true }) + RangeMax: number; + + @Column({ type: 'varchar', nullable: true }) + DefaultExpectedUnit: string; + + @Column({ type: 'boolean', nullable: false, default: false }) PageBreakAfter: boolean; - // Logic References - @Column({ - type: 'uuid', - nullable: true - }) - SkipLogicId?: string; - - @ManyToOne(() => SkipLogicEntity, { nullable: true }) - @JoinColumn({ name: 'SkipLogicId' }) - SkipLogic?: SkipLogicEntity; - - @Column({ - type: 'uuid', - nullable: true - }) - CalculateLogicId?: string; - - @ManyToOne(() => CalculationLogicEntity, { nullable: true }) - @JoinColumn({ name: 'CalculateLogicId' }) - CalculateLogic?: CalculationLogicEntity; - - @Column({ - type: 'uuid', - nullable: true - }) - ValidateLogicId?: string; - - @ManyToOne(() => ValidationLogicEntity, { nullable: true }) - @JoinColumn({ name: 'ValidateLogicId' }) - ValidateLogic?: ValidationLogicEntity; - - // Template and Section References - @Column({ - type: 'uuid', - nullable: true - }) - TemplateId?: string; - - @Column({ - type: 'uuid', - nullable: true - }) - ParentSectionId?: string; - - // Relationships - @OneToMany(() => QuestionResponse, (response) => response.Question) - Responses?: QuestionResponse[]; - - @ManyToOne(() => FormTemplate, (template) => template.Questions, { nullable: true }) - FormTemplate?: FormTemplate; - - @ManyToOne(() => FormSection, (section) => section.Questions, { nullable: true }) - ParentFormSection?: FormSection; - - @ManyToOne(() => InputUnitList, { nullable: true }) - ExpectedInputUnitList?: InputUnitList; - - // Form Reference - @Column({ - type: 'uuid', - nullable: true - }) - FormId?: string; - - // TODO: Add proper Form relationship when Form entity is created - // @ManyToOne(() => Form, form => form.Fields, { nullable: true }) - // @JoinColumn({ name: 'FormId' }) - // Form?: Form; - - @CreateDateColumn() - CreatedAt: Date; - - @UpdateDateColumn() - UpdatedAt: Date; + @OneToMany(() => QuestionResponse, (response) => response.FormField) + Responses: QuestionResponse[]; + + @ManyToOne(() => FormTemplate, (template) => template.FormFields) + @JoinColumn({ name: 'TemplateId' }) + FormTemplate: FormTemplate; + + @ManyToOne(() => FormSection, (section) => section.FormFields) + @JoinColumn({ name: 'ParentSectionId' }) + ParentFormSection: FormSection; + + @ManyToOne(() => InputUnitList) + ExpectedInputUnitList: InputUnitList } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/form.section/form.section.model.ts b/src/database/sql/typeorm/models/form.section/form.section.model.ts index cc8f4b2..1b29098 100644 --- a/src/database/sql/typeorm/models/form.section/form.section.model.ts +++ b/src/database/sql/typeorm/models/form.section/form.section.model.ts @@ -1,7 +1,8 @@ import { Entity, Column, ManyToOne, OneToMany, JoinColumn } from 'typeorm'; import { BaseEntity } from '../base.entity'; import { FormTemplate } from '../form.template/form.template.model'; -import { Question } from '../question/question.model'; + +import { FormFieldEntity } from '../form.field/form.field.model'; // enum SectionNodeType { // RootSection = 'RootSection', @@ -49,6 +50,9 @@ export class FormSection extends BaseEntity { @OneToMany(() => FormSection, (section) => section.ParentSection) ChildSections: FormSection[]; - @OneToMany(() => Question, (question) => question.ParentFormSection) - Questions: Question[]; + // @OneToMany(() => Question, (question) => question.ParentFormSection) + // Questions: Question[]; + + @OneToMany(() => FormFieldEntity, (formField) => formField.ParentFormSection) + FormFields: FormFieldEntity[]; } diff --git a/src/database/sql/typeorm/models/form.submission/form.submission.model.ts b/src/database/sql/typeorm/models/form.submission/form.submission.model.ts index b913762..6f88335 100644 --- a/src/database/sql/typeorm/models/form.submission/form.submission.model.ts +++ b/src/database/sql/typeorm/models/form.submission/form.submission.model.ts @@ -2,15 +2,7 @@ import { Entity, Column, ManyToOne, OneToMany, JoinColumn } from 'typeorm'; import { BaseEntity } from '../base.entity'; import { FormTemplate } from '../form.template/form.template.model'; import { QuestionResponse } from '../question.response/question.response.model'; - - -export enum FormStatus { - LinkShared = 'LinkShared', - Saved = 'Saved', - InProgress='InProgress', - LinkExpired = 'LinkExpired', - Submitted = 'Submitted' -} +import { FormStatus } from '../../../../../domain.types/forms/form.submission.enums'; @Entity('form_submissions') export class FormSubmission extends BaseEntity { diff --git a/src/database/sql/typeorm/models/form.template.approval/form.template.approval.model.ts b/src/database/sql/typeorm/models/form.template.approval/form.template.approval.model.ts index 4290dab..d4d9b7f 100644 --- a/src/database/sql/typeorm/models/form.template.approval/form.template.approval.model.ts +++ b/src/database/sql/typeorm/models/form.template.approval/form.template.approval.model.ts @@ -9,7 +9,7 @@ export class FormTemplateApproval extends BaseEntity { @Column({ type: 'uuid', nullable: false, unique: true }) TemplateId: string - + @Column({ type: 'boolean', nullable: false, default: false }) Approved: boolean diff --git a/src/database/sql/typeorm/models/form.template/form.template.model.ts b/src/database/sql/typeorm/models/form.template/form.template.model.ts index 0364be3..37d012b 100644 --- a/src/database/sql/typeorm/models/form.template/form.template.model.ts +++ b/src/database/sql/typeorm/models/form.template/form.template.model.ts @@ -2,21 +2,9 @@ import { Entity, Column, OneToMany, ManyToOne, JoinColumn } from 'typeorm'; import { BaseEntity } from '../base.entity'; import { FormSubmission } from '../form.submission/form.submission.model'; import { FormSection } from '../form.section/form.section.model'; -import { Question } from '../question/question.model'; - -export enum FormType { - Survey = 'Survey', - Questionnaire = 'Questionnaire', - TestPaper = 'TestPaper', - DataCollection = 'DataCollection' -} - -export enum NavigationStrategy { - OneQuestion = 'OneQuestion', - OneSection = 'OneSection', - AllAtOnce = 'AllAtOnce', - Paginated = 'Paginated' -} +// import { Question } from '../question/question.model'; +import { FormFieldEntity } from '../form.field/form.field.model'; +import { FormType, NavigationStrategy } from '../../../../../domain.types/forms/form.template.enums'; @Entity({name: 'form_templates'}) export class FormTemplate extends BaseEntity { @@ -68,6 +56,9 @@ export class FormTemplate extends BaseEntity { @OneToMany(() => FormSection, (section) => section.FormTemplate) FormSections: FormSection[]; - @OneToMany(() => Question, (question) => question.FormTemplate) - Questions: Question[]; + // @OneToMany(() => Question, (question) => question.FormTemplate) + // Questions: Question[]; + + @OneToMany(() => FormFieldEntity, (formField) => formField.FormTemplate) + FormFields: FormFieldEntity[]; } diff --git a/src/database/sql/typeorm/models/logic/base.logic.model.ts b/src/database/sql/typeorm/models/logic/base.logic.model.ts index 70c7956..99f3017 100644 --- a/src/database/sql/typeorm/models/logic/base.logic.model.ts +++ b/src/database/sql/typeorm/models/logic/base.logic.model.ts @@ -1,10 +1,10 @@ import { Entity, Column } from 'typeorm'; import { BaseEntity } from '../base.entity'; -import { LogicType } from './logic.types'; +import { LogicType } from '../../../../../domain.types/forms/logic.enums'; // Base Logic Entity (Abstract - no table) export abstract class BaseLogicEntity extends BaseEntity { - @Column({ type: 'varchar', length: 255, nullable: false }) + @Column({ type: 'uuid', nullable: false }) FieldId: string; @Column({ type: 'boolean', nullable: false, default: true }) diff --git a/src/database/sql/typeorm/models/logic/calculation.logic.model.ts b/src/database/sql/typeorm/models/logic/calculation.logic.model.ts index f9b25d7..1035fe3 100644 --- a/src/database/sql/typeorm/models/logic/calculation.logic.model.ts +++ b/src/database/sql/typeorm/models/logic/calculation.logic.model.ts @@ -1,18 +1,19 @@ import { Entity, Column, OneToMany } from 'typeorm'; -import { LogicType } from './logic.types'; +import { LogicType } from '../../../../../domain.types/forms/logic.enums'; import { CalculationRuleEntity } from '../rule/calculation.rule.model'; import { BaseLogicEntity } from './base.logic.model'; // Calculation Logic Entity -@Entity({ name: 'calculation_logics' }) +@Entity({ name: 'eval_calculation_logics' }) export class CalculationLogicEntity extends BaseLogicEntity { @Column({ type: 'varchar', length: 50, nullable: false, default: LogicType.Calculation }) Type: LogicType.Calculation; + // TODO: Add proper CalculationRule relationship when CalculationRule entity is created @OneToMany(() => CalculationRuleEntity, rule => rule.LogicId) - Rules!: CalculationRuleEntity[]; + Rules: CalculationRuleEntity[]; - @Column({ type: 'text', nullable: true }) - FallbackValue?: string; // JSON serialized value + @Column({ type: 'varchar', length: 255, nullable: true }) + FallbackValue?: string; } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/logic/legacy.logic.model.ts b/src/database/sql/typeorm/models/logic/legacy.logic.model.ts deleted file mode 100644 index 6255ac9..0000000 --- a/src/database/sql/typeorm/models/logic/legacy.logic.model.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Entity, Column } from 'typeorm'; -import { BaseLogicEntity } from './base.logic.model'; -import { LogicType } from './logic.types'; - -// Legacy Logic for backward compatibility - Separate Table -@Entity({ name: 'legacy_logics' }) -export class LegacyLogicEntity extends BaseLogicEntity { - @Column({ - type: 'varchar', - length: 50, - nullable: false - }) - Type: LogicType; - - @Column({ - type: 'text', - nullable: true - }) - Rules: string; // JSON serialized Rule[] - - @Column({ - type: 'uuid', - nullable: true - }) - FallbackRuleId?: string; - - @Column({ - type: 'text', - nullable: true - }) - FallbackValue?: string; // JSON serialized value - - @Column({ - type: 'boolean', - nullable: true - }) - DefaultSkip?: boolean; -} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/logic/skip.logic.model.ts b/src/database/sql/typeorm/models/logic/skip.logic.model.ts index 92846c8..aa9db04 100644 --- a/src/database/sql/typeorm/models/logic/skip.logic.model.ts +++ b/src/database/sql/typeorm/models/logic/skip.logic.model.ts @@ -1,11 +1,11 @@ import { Entity, Column, OneToMany } from 'typeorm'; -import { BaseEntity } from '../base.entity'; -import { LogicType } from './logic.types'; +import { LogicType } from '../../../../../domain.types/forms/logic.enums'; import { SkipRuleEntity } from '../rule/skip.rule.model'; +import { BaseLogicEntity } from './base.logic.model'; // Skip Logic Entity -@Entity({ name: 'skip_logics' }) -export class SkipLogicEntity extends BaseEntity { +@Entity({ name: 'eval_skip_logics' }) +export class SkipLogicEntity extends BaseLogicEntity { @Column({ type: 'varchar', length: 50, nullable: false, default: LogicType.Skip }) Type: LogicType.Skip; diff --git a/src/database/sql/typeorm/models/logic/validation.logic.model.ts b/src/database/sql/typeorm/models/logic/validation.logic.model.ts index bed02c2..6b852f3 100644 --- a/src/database/sql/typeorm/models/logic/validation.logic.model.ts +++ b/src/database/sql/typeorm/models/logic/validation.logic.model.ts @@ -1,16 +1,16 @@ import { Entity, Column, OneToMany } from 'typeorm'; -import { BaseEntity } from '../base.entity'; -import { LogicType } from './logic.types'; +import { LogicType } from '../../../../../domain.types/forms/logic.enums'; import { ValidationRuleEntity } from '../rule/validation.rule.model'; - +import { BaseLogicEntity } from './base.logic.model'; // Validation Logic Entity -@Entity({ name: 'validation_logics' }) -export class ValidationLogicEntity extends BaseEntity { +@Entity({ name: 'eval_validation_logics' }) +export class ValidationLogicEntity extends BaseLogicEntity { @Column({ type: 'varchar', length: 50, nullable: false, default: LogicType.Validation }) Type: LogicType.Validation; + // TODO: Add proper ValidationRule relationship when ValidationRule entity is created @OneToMany(() => ValidationRuleEntity, rule => rule.LogicId) Rules: ValidationRuleEntity[]; } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/base.operation.model.ts b/src/database/sql/typeorm/models/operation/base.operation.model.ts index f7d8af9..f8da943 100644 --- a/src/database/sql/typeorm/models/operation/base.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/base.operation.model.ts @@ -1,6 +1,6 @@ import { Entity, Column } from 'typeorm'; import { BaseEntity } from '../base.entity'; -import { OperationType } from './operation.types'; +import { OperationType } from '../../../../../domain.types/forms/operation.enums'; // Base Operation Entity (Abstract - no table) export abstract class BaseOperationEntity extends BaseEntity { @@ -9,4 +9,7 @@ export abstract class BaseOperationEntity extends BaseEntity { @Column({ type: 'text', nullable: true }) Description?: string; + + @Column({ type: 'varchar', length: 50, nullable: false }) + Type: OperationType; } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/composition.operation.model.ts b/src/database/sql/typeorm/models/operation/composition.operation.model.ts index 4e80d5f..a547817 100644 --- a/src/database/sql/typeorm/models/operation/composition.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/composition.operation.model.ts @@ -1,16 +1,13 @@ import { Entity, Column } from 'typeorm'; import { BaseOperationEntity } from './base.operation.model'; -import { OperationType, CompositionOperatorType } from './operation.types'; +import { CompositionOperatorType } from '../../../../../domain.types/forms/operation.enums'; // Composition Operation Entity -@Entity({ name: 'composition_operations' }) +@Entity({ name: 'eval_composition_operations' }) export class CompositionOperationEntity extends BaseOperationEntity { - @Column({ type: 'varchar', length: 50, nullable: false, default: OperationType.Composition }) - Type: OperationType.Composition; - @Column({ type: 'varchar', length: 50, nullable: false }) Operator: CompositionOperatorType; @Column({ type: 'text', nullable: false }) - Children: string; // JSON serialized Operation[] (stored as IDs) + Operands: string; // JSON serialized Operand[] } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/function.expression.operation.model.ts b/src/database/sql/typeorm/models/operation/function.expression.operation.model.ts index 32b70ab..c210030 100644 --- a/src/database/sql/typeorm/models/operation/function.expression.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/function.expression.operation.model.ts @@ -1,17 +1,16 @@ import { Entity, Column } from 'typeorm'; import { BaseOperationEntity } from './base.operation.model'; -import { OperationType } from './operation.types'; - // Function Expression Operation Entity -@Entity({ name: 'function_expression_operations' }) +@Entity({ name: 'eval_function_expression_operations' }) export class FunctionExpressionOperationEntity extends BaseOperationEntity { - @Column({ type: 'varchar', length: 50, nullable: false, default: OperationType.FunctionExpression }) - Type: OperationType.FunctionExpression; - @Column({ type: 'text', nullable: false }) Expression: string; @Column({ type: 'text', nullable: false }) Variables: string; // JSON serialized Record + + @Column({ type: 'text', nullable: false }) + ResultDataType: string; + } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/iterate.operation.model.ts b/src/database/sql/typeorm/models/operation/iterate.operation.model.ts index e2aab2d..0026a23 100644 --- a/src/database/sql/typeorm/models/operation/iterate.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/iterate.operation.model.ts @@ -1,24 +1,22 @@ import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseOperationEntity } from './base.operation.model'; -import { OperationType } from './operation.types'; - // Iterate Operation Entity -@Entity({ name: 'iterate_operations' }) +@Entity({ name: 'eval_iterate_operations' }) export class IterateOperationEntity extends BaseOperationEntity { - @Column({ type: 'varchar', length: 50, nullable: false, default: OperationType.Iterate }) - Type: OperationType.Iterate; - - @Column({ type: 'text', nullable: false }) - ArrayOperand: string; // JSON serialized Operand + @Column({ type: 'varchar', length: 255, nullable: false }) + CollectionField: string; // Field name to iterate over - @Column({ type: 'varchar', length: 100, nullable: false }) - ItemAlias: string; + @Column({ type: 'varchar', length: 255, nullable: false }) + ResultField: string; // Field name to store result @Column({ type: 'uuid', nullable: false }) - OperationId: string; // Reference to child operation + OperationId: string; + + // @ManyToOne(() => BaseOperationEntity, { nullable: false }) + // @JoinColumn({ name: 'OperationId' }) + // Operation: BaseOperationEntity; - @ManyToOne(() => BaseOperationEntity, { nullable: false }) - @JoinColumn({ name: 'OperationId' }) - Operation: BaseOperationEntity; + @Column({ type: 'text', nullable: true }) + FilterExpression?: string; // Optional filter expression } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/logical.operation.model.ts b/src/database/sql/typeorm/models/operation/logical.operation.model.ts index b5b41d7..cd332b9 100644 --- a/src/database/sql/typeorm/models/operation/logical.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/logical.operation.model.ts @@ -1,14 +1,10 @@ import { Entity, Column } from 'typeorm'; import { BaseOperationEntity } from './base.operation.model'; -import { OperationType, LogicalOperatorType } from './operation.types'; - +import { LogicalOperatorType } from '../../../../../domain.types/forms/operation.enums'; // Logical Operation Entity -@Entity({ name: 'logical_operations' }) +@Entity({ name: 'eval_logical_operations' }) export class LogicalOperationEntity extends BaseOperationEntity { - @Column({ type: 'varchar', length: 50, nullable: false, default: OperationType.Logical }) - Type: OperationType.Logical; - @Column({ type: 'varchar', length: 50, nullable: false }) Operator: LogicalOperatorType; diff --git a/src/database/sql/typeorm/models/operation/mathematical.operation.model.ts b/src/database/sql/typeorm/models/operation/mathematical.operation.model.ts index d355772..eb395ab 100644 --- a/src/database/sql/typeorm/models/operation/mathematical.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/mathematical.operation.model.ts @@ -1,18 +1,16 @@ import { Entity, Column } from 'typeorm'; import { BaseOperationEntity } from './base.operation.model'; -import { OperationType, MathematicalOperatorType } from './operation.types'; - - +import { MathematicalOperatorType } from '../../../../../domain.types/forms/operation.enums'; // Mathematical Operation Entity -@Entity({ name: 'mathematical_operations' }) +@Entity({ name: 'eval_mathematical_operations' }) export class MathematicalOperationEntity extends BaseOperationEntity { - @Column({ type: 'varchar', length: 50, nullable: false, default: OperationType.Mathematical }) - Type: OperationType.Mathematical; - @Column({ type: 'varchar', length: 50, nullable: false }) Operator: MathematicalOperatorType; @Column({ type: 'text', nullable: false }) Operands: string; // JSON serialized Operand[] + + @Column({ type: 'text', nullable: false }) + ResultDataType: string; } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/question.response/question.response.model.ts b/src/database/sql/typeorm/models/question.response/question.response.model.ts index fc61606..a9ab3d4 100644 --- a/src/database/sql/typeorm/models/question.response/question.response.model.ts +++ b/src/database/sql/typeorm/models/question.response/question.response.model.ts @@ -2,7 +2,8 @@ import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseEntity } from '../base.entity'; import { FormSubmission } from '../form.submission/form.submission.model'; import { QueryResponseType } from '../../../../../domain.types/forms/query.response.types'; -import { Question } from '../question/question.model'; +// import { Question } from '../question/question.model'; +import { FormFieldEntity } from '../form.field/form.field.model'; @Entity('question_responses') @@ -13,6 +14,9 @@ export class QuestionResponse extends BaseEntity { @Column({ type: 'uuid', nullable: false }) QuestionId: string; + @Column({ type: 'uuid', nullable: true }) + FormFieldId: string; + @Column({ type: 'enum', enum: QueryResponseType, nullable: false, default: QueryResponseType.SingleChoiceSelection }) ResponseType: QueryResponseType; @@ -57,7 +61,11 @@ export class QuestionResponse extends BaseEntity { @JoinColumn({ name: 'FormSubmissionId' }) FormSubmission: FormSubmission; - @ManyToOne(() => Question, (question) => question.Responses) - @JoinColumn({ name: 'QuestionId' }) - Question: Question; + // @ManyToOne(() => Question, (question) => question.Responses) + // @JoinColumn({ name: 'QuestionId' }) + // Question: Question; + + @ManyToOne(() => FormFieldEntity, (formField) => formField.Responses) + @JoinColumn({ name: 'FormFieldId' }) + FormField: FormFieldEntity; } diff --git a/src/database/sql/typeorm/models/question/question.model.ts b/src/database/sql/typeorm/models/question/question.model.ts deleted file mode 100644 index d0a94e9..0000000 --- a/src/database/sql/typeorm/models/question/question.model.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm'; -import { FormTemplate } from '../form.template/form.template.model'; -import { FormSection } from '../form.section/form.section.model'; -import { BaseEntity } from '../base.entity'; -import { QuestionResponse } from '../question.response/question.response.model'; -import { InputUnitList } from '../input.unit.list/input.unit.list.model'; -import { QueryResponseType } from '../../../../../domain.types/forms/query.response.types'; - -// export enum QueryResponseType { -// Text = "Text", -// Float = "Float", -// Integer = "Integer", -// Boolean = "Boolean", -// Object = "Object", -// TextArray = "TextArray", -// SingleChoiceSelection = "SingleChoiceSelection", -// MultiChoiceSelection = "MultiChoiceSelection", -// File = "File", -// Date = "Date", -// DateTime = "DateTime", -// Rating = "Rating", -// Location = "Location", -// Url = "Url", -// Range = "Range" -// } - -@Entity({ name: 'questions' }) -export class Question extends BaseEntity { - - @Column({ type: 'uuid', nullable: true }) - TemplateId: string; - - @Column({ type: 'uuid', nullable: false }) - ParentSectionId: string; - - @Column({ type: 'varchar', length: 128, nullable: true }) - Title: string; - - @Column({ type: 'varchar', length: 512, nullable: true }) - Description: string; - - @Column({ type: 'varchar', length: 128, nullable: false }) - DisplayCode: string; - - @Column({ type: 'enum', enum: QueryResponseType }) - ResponseType: QueryResponseType; - - @Column({ type: 'int', nullable: true }) - Score: number; - - @Column({ type: 'int', nullable: true }) - Sequence: number; - - @Column({ type: 'varchar', nullable: true }) - ExpectedAnswer: string; - - @Column({ type: 'boolean', nullable: true }) - IsRequired: boolean; - - @Column({ type: 'varchar', length: 512, nullable: true }) - Hint: string; - - @Column({ type: 'json', nullable: true }) - Options: string; - - @Column({ type: 'uuid', nullable: true }) - ImageResourceId?: string; - - @Column({ type: 'int', nullable: true }) - RangeMin: number; - - @Column({ type: 'int', nullable: true }) - RangeMax: number; - - @Column({ type: 'varchar', nullable: true }) - DefaultExpectedUnit: string; - - @Column({ type: 'boolean', nullable: false, default: false }) - PageBreakAfter: boolean; - - @OneToMany(() => QuestionResponse, (response) => response.Question) - Responses: QuestionResponse[]; - - @ManyToOne(() => FormTemplate, (template) => template.Questions) - FormTemplate: FormTemplate; - - @ManyToOne(() => FormSection, (section) => section.Questions) - ParentFormSection: FormSection; - - @ManyToOne(() => InputUnitList) - ExpectedInputUnitList: InputUnitList -} diff --git a/src/database/sql/typeorm/models/rule/base.rule.model.ts b/src/database/sql/typeorm/models/rule/base.rule.model.ts index cebe1b1..d279226 100644 --- a/src/database/sql/typeorm/models/rule/base.rule.model.ts +++ b/src/database/sql/typeorm/models/rule/base.rule.model.ts @@ -10,4 +10,10 @@ export abstract class BaseRuleEntity extends BaseEntity { @Column({ type: 'text', nullable: true }) Description?: string; + + @Column({ type: 'int', nullable: false, default: 0 }) + Priority: number; + + @Column({ type: 'boolean', nullable: false, default: true }) + IsActive: boolean; } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/rule/calculation.rule.model.ts b/src/database/sql/typeorm/models/rule/calculation.rule.model.ts index 9c0c733..5b9a88b 100644 --- a/src/database/sql/typeorm/models/rule/calculation.rule.model.ts +++ b/src/database/sql/typeorm/models/rule/calculation.rule.model.ts @@ -1,24 +1,23 @@ import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseRuleEntity } from './base.rule.model'; -import { BaseOperationEntity } from '../operation/base.operation.model'; import { CalculationLogicEntity } from '../logic/calculation.logic.model'; // Calculation Rule Entity -@Entity({ name: 'calculation_rules' }) +@Entity({ name: 'eval_calculation_rules' }) export class CalculationRuleEntity extends BaseRuleEntity { @Column({ type: 'uuid', nullable: true }) ConditionForOperationId?: string; - @ManyToOne(() => BaseOperationEntity, { nullable: true }) - @JoinColumn({ name: 'ConditionForOperationId' }) - ConditionForOperation?: BaseOperationEntity; + // @ManyToOne(() => BaseOperationEntity, { nullable: true }) + // @JoinColumn({ name: 'ConditionForOperationId' }) + // ConditionForOperation?: BaseOperationEntity; @Column({ type: 'uuid', nullable: false }) OperationId: string; - @ManyToOne(() => BaseOperationEntity, { nullable: false }) - @JoinColumn({ name: 'OperationId' }) - Operation: BaseOperationEntity; + // @ManyToOne(() => BaseOperationEntity, { nullable: false }) + // @JoinColumn({ name: 'OperationId' }) + // Operation: BaseOperationEntity; @Column({ type: 'uuid', nullable: true }) LogicId?: string; diff --git a/src/database/sql/typeorm/models/rule/legacy.rule.model.ts b/src/database/sql/typeorm/models/rule/legacy.rule.model.ts deleted file mode 100644 index 56f4301..0000000 --- a/src/database/sql/typeorm/models/rule/legacy.rule.model.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; -import { BaseRuleEntity } from './base.rule.model'; -import { BaseOperationEntity } from '../operation/base.operation.model'; - -// Legacy Rule for backward compatibility - Separate Table -@Entity({ name: 'legacy_rules' }) -export class LegacyRuleEntity extends BaseRuleEntity { - @Column({ - type: 'uuid', - nullable: false - }) - OperationId: string; - - @ManyToOne(() => BaseOperationEntity, { nullable: false }) - @JoinColumn({ name: 'OperationId' }) - Operation: BaseOperationEntity; - - @Column({ - type: 'varchar', - length: 500, - nullable: true - }) - ErrorMessage?: string; - - @Column({ - type: 'uuid', - nullable: true - }) - LogicId?: string; -} \ No newline at end of file diff --git a/src/database/sql/typeorm/models/rule/skip.rule.model.ts b/src/database/sql/typeorm/models/rule/skip.rule.model.ts index 9e69afa..e681ae1 100644 --- a/src/database/sql/typeorm/models/rule/skip.rule.model.ts +++ b/src/database/sql/typeorm/models/rule/skip.rule.model.ts @@ -1,19 +1,14 @@ import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseRuleEntity } from './base.rule.model'; -import { BaseOperationEntity } from '../operation/base.operation.model'; import { SkipLogicEntity } from '../logic/skip.logic.model'; // Skip Rule Entity -@Entity({ name: 'skip_rules' }) +@Entity({ name: 'eval_skip_rules' }) export class SkipRuleEntity extends BaseRuleEntity { @Column({ type: 'uuid', nullable: false }) OperationId: string; - @ManyToOne(() => BaseOperationEntity, { nullable: false }) - @JoinColumn({ name: 'OperationId' }) - Operation: BaseOperationEntity; - @Column({ type: 'boolean', nullable: false, default: true }) SkipWhenTrue: boolean; diff --git a/src/database/sql/typeorm/models/rule/validation.rule.model.ts b/src/database/sql/typeorm/models/rule/validation.rule.model.ts index ede9068..bee0c42 100644 --- a/src/database/sql/typeorm/models/rule/validation.rule.model.ts +++ b/src/database/sql/typeorm/models/rule/validation.rule.model.ts @@ -1,18 +1,17 @@ import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseRuleEntity } from './base.rule.model'; -import { BaseOperationEntity } from '../operation/base.operation.model'; import { ValidationLogicEntity } from '../logic/validation.logic.model'; // Validation Rule Entity -@Entity({ name: 'validation_rules' }) +@Entity({ name: 'eval_validation_rules' }) export class ValidationRuleEntity extends BaseRuleEntity { @Column({ type: 'uuid', nullable: false }) OperationId: string; - @ManyToOne(() => BaseOperationEntity, { nullable: false }) - @JoinColumn({ name: 'OperationId' }) - Operation: BaseOperationEntity; + // @ManyToOne(() => BaseOperationEntity, { nullable: false }) + // @JoinColumn({ name: 'OperationId' }) + // Operation: BaseOperationEntity; @Column({ type: 'boolean', nullable: false, default: false }) ErrorWhenFalse: boolean; diff --git a/src/database/sql/typeorm/repositories/field.logic/field.logic.calculation.repo.ts b/src/database/sql/typeorm/repositories/field.logic/field.logic.calculation.repo.ts new file mode 100644 index 0000000..5572bb9 --- /dev/null +++ b/src/database/sql/typeorm/repositories/field.logic/field.logic.calculation.repo.ts @@ -0,0 +1,158 @@ +import { ICalculationLogicRepo } from "../../../../repository.interfaces/field.logic/calculation.logic/calculation.logic.repo.interface"; +import { + CalculationLogicResponseDto, + CalculationLogicCreateModel, + CalculationLogicUpdateModel, + LogicSearchFilters +} from "../../../../../domain.types/forms/logic.domain.types"; +import { CalculationLogicMapper } from "../../mappers/calculation.logic.mapper"; +import { CalculationLogicEntity } from "../../models/logic/calculation.logic.model"; +import { LogicType } from "../../../../../domain.types/forms/logic.enums"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class CalculationLogicRepo extends BaseRepo implements ICalculationLogicRepo { + + _calculationLogicRepo: Repository = Source.getRepository(CalculationLogicEntity); + + // Calculation Logic operations + createCalculationLogic = async (model: CalculationLogicCreateModel): Promise => { + try { + const data = this._calculationLogicRepo.create({ + FieldId: model.FieldId, + Type: model.Type, + Enabled: model.Enabled, + FallbackValue: model.FallbackValue + }); + const record = await this._calculationLogicRepo.save(data); + return CalculationLogicMapper.toCalculationLogicDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + updateCalculationLogic = async (id: string, model: CalculationLogicUpdateModel): Promise => { + try { + const updateData = await this._calculationLogicRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError("Calculation Logic Data not found!"); + } + + if (model.Type) { + updateData.Type = model.Type as LogicType.Calculation; + } + if (model.FieldId) { + updateData.FieldId = model.FieldId; + } + if (model.Enabled !== undefined) { + updateData.Enabled = model.Enabled; + } + if (model.FallbackValue !== undefined) { + updateData.FallbackValue = model.FallbackValue; + } + + updateData.UpdatedAt = new Date(); + + const record = await this._calculationLogicRepo.save(updateData); + return CalculationLogicMapper.toCalculationLogicDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getCalculationLogicById = async (id: string): Promise => { + try { + const record = await this._calculationLogicRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return CalculationLogicMapper.toCalculationLogicDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + deleteCalculationLogic = async (id: string): Promise => { + try { + const record = await this._calculationLogicRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._calculationLogicRepo.save(record); + + return true; // Soft delete successful + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + searchCalculationLogic = async (filters: LogicSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._calculationLogicRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => CalculationLogicMapper.toCalculationLogicDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: LogicSearchFilters) => { + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where["id"] = filters.id; + } + + if (filters.type) { + search.where["Type"] = filters.type; + } + + if (filters.fieldId) { + search.where["FieldId"] = filters.fieldId; + } + + if (filters.enabled !== undefined) { + search.where["Enabled"] = filters.enabled; + } + + return search; + }; + +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/field.logic/field.logic.skip.repo.ts b/src/database/sql/typeorm/repositories/field.logic/field.logic.skip.repo.ts new file mode 100644 index 0000000..1a8715b --- /dev/null +++ b/src/database/sql/typeorm/repositories/field.logic/field.logic.skip.repo.ts @@ -0,0 +1,162 @@ + +import { ISkipLogicRepo } from "../../../../repository.interfaces/field.logic/skip.logic/skip.logic.repo.interface"; +import { + SkipLogicResponseDto, + SkipLogicCreateModel, + SkipLogicUpdateModel, + LogicSearchFilters +} from "../../../../../domain.types/forms/logic.domain.types"; +import { SkipLogicMapper } from "../../mappers/skip.logic.mapper"; +import { SkipLogicEntity } from "../../models/logic/skip.logic.model"; +import { LogicType } from "../../../../../domain.types/forms/logic.enums"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class SkipLogicRepo extends BaseRepo implements ISkipLogicRepo { + + _skipLogicRepo: Repository = Source.getRepository(SkipLogicEntity); + // Skip Logic operations + createSkipLogic = async (model: SkipLogicCreateModel): Promise => { + try { + const data = this._skipLogicRepo.create({ + FieldId: model.FieldId, + Type: model.Type, + Enabled: model.Enabled, + DefaultSkip: model.DefaultSkip + }); + const record = await this._skipLogicRepo.save(data); + return SkipLogicMapper.toSkipLogicDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + updateSkipLogic = async (id: string, model: SkipLogicUpdateModel): Promise => { + try { + const updateData = await this._skipLogicRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError("Skip Logic Data not found!"); + } + + if (model.Type) { + updateData.Type = model.Type as LogicType.Skip; + } + if (model.FieldId) { + updateData.FieldId = model.FieldId; + } + if (model.Enabled !== undefined) { + updateData.Enabled = model.Enabled; + } + if (model.DefaultSkip !== undefined) { + updateData.DefaultSkip = model.DefaultSkip; + } + + updateData.UpdatedAt = new Date(); + + const record = await this._skipLogicRepo.save(updateData); + return SkipLogicMapper.toSkipLogicDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getSkipLogicById = async (id: string): Promise => { + try { + const record = await this._skipLogicRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return SkipLogicMapper.toSkipLogicDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + deleteSkipLogic = async (id: string): Promise => { + try { + const record = await this._skipLogicRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._skipLogicRepo.save(record); + + return true; // Soft delete successful + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + searchSkipLogic = async (filters: LogicSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._skipLogicRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => SkipLogicMapper.toSkipLogicDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: LogicSearchFilters) => { + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where["id"] = filters.id; + } + + if (filters.type) { + search.where["Type"] = filters.type; + } + + if (filters.fieldId) { + search.where["FieldId"] = filters.fieldId; + } + + if (filters.enabled !== undefined) { + search.where["Enabled"] = filters.enabled; + } + + if (filters.defaultSkip !== undefined) { + search.where["DefaultSkip"] = filters.defaultSkip; + } + + return search; + }; + +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/field.logic/field.logic.validation.repo.ts b/src/database/sql/typeorm/repositories/field.logic/field.logic.validation.repo.ts new file mode 100644 index 0000000..2163761 --- /dev/null +++ b/src/database/sql/typeorm/repositories/field.logic/field.logic.validation.repo.ts @@ -0,0 +1,155 @@ +import { IValidationLogicRepo } from "../../../../repository.interfaces/field.logic/validation.logic/validation.logic.repo.interface"; +import { + ValidationLogicResponseDto, + ValidationLogicCreateModel, + ValidationLogicUpdateModel, + LogicSearchFilters +} from "../../../../../domain.types/forms/logic.domain.types"; +import { ValidationLogicMapper } from "../../mappers/validation.logic.mapper"; +import { ValidationLogicEntity } from "../../models/logic/validation.logic.model"; +import { LogicType } from "../../../../../domain.types/forms/logic.enums"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class ValidationLogicRepo extends BaseRepo implements IValidationLogicRepo { + + _validationLogicRepo: Repository = Source.getRepository(ValidationLogicEntity); + + // Validation Logic operations + createValidationLogic = async (model: ValidationLogicCreateModel): Promise => { + try { + const data = this._validationLogicRepo.create({ + FieldId: model.FieldId, + Type: model.Type, + Enabled: model.Enabled, + // DefaultSkip: model.DefaultSkip + }); + const record = await this._validationLogicRepo.save(data); + return ValidationLogicMapper.toValidationLogicDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + updateValidationLogic = async (id: string, model: ValidationLogicUpdateModel): Promise => { + try { + const updateData = await this._validationLogicRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError("Validation Logic Data not found!"); + } + + if (model.Type) { + updateData.Type = model.Type as LogicType.Validation; + } + if (model.FieldId) { + updateData.FieldId = model.FieldId; + } + if (model.Enabled !== undefined) { + updateData.Enabled = model.Enabled; + } + + updateData.UpdatedAt = new Date(); + + const record = await this._validationLogicRepo.save(updateData); + return ValidationLogicMapper.toValidationLogicDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getValidationLogicById = async (id: string): Promise => { + try { + const record = await this._validationLogicRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return ValidationLogicMapper.toValidationLogicDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + deleteValidationLogic = async (id: string): Promise => { + try { + const record = await this._validationLogicRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._validationLogicRepo.save(record); + + return true; // Soft delete successful + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + searchValidationLogic = async (filters: LogicSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._validationLogicRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => ValidationLogicMapper.toValidationLogicDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: LogicSearchFilters) => { + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where["id"] = filters.id; + } + + if (filters.type) { + search.where["Type"] = filters.type; + } + + if (filters.fieldId) { + search.where["FieldId"] = filters.fieldId; + } + + if (filters.enabled !== undefined) { + search.where["Enabled"] = filters.enabled; + } + + return search; + }; + +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/field.operations/field.operations.composition.repo.ts b/src/database/sql/typeorm/repositories/field.operations/field.operations.composition.repo.ts new file mode 100644 index 0000000..0e97d4e --- /dev/null +++ b/src/database/sql/typeorm/repositories/field.operations/field.operations.composition.repo.ts @@ -0,0 +1,162 @@ +import { ICompositionOperationRepo } from "../../../../repository.interfaces/field.operations/composition.operation/composition.operation.repo.interface"; +import { + CompositionOperationResponseDto, + CompositionOperationCreateModel, + CompositionOperationUpdateModel, + OperationSearchFilters +} from "../../../../../domain.types/forms/operation.domain.types"; +import { CompositionOperationMapper } from "../../mappers/composition.operation.mapper"; +import { CompositionOperationEntity } from "../../models/operation/composition.operation.model"; +import { CompositionOperatorType } from "../../../../../domain.types/forms/operation.enums"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class CompositionOperationRepo extends BaseRepo implements ICompositionOperationRepo { + + _compositionOperationRepo: Repository = Source.getRepository(CompositionOperationEntity); + + // Composition Operation operations + createCompositionOperation = async (model: CompositionOperationCreateModel): Promise => { + try { + const data = this._compositionOperationRepo.create({ + Name: model.Name, + Description: model.Description, + Type: model.Type, + Operator: model.Operator, + Operands: model.Operands + }); + const record = await this._compositionOperationRepo.save(data); + return CompositionOperationMapper.toCompositionOperationDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + updateCompositionOperation = async (id: string, model: CompositionOperationUpdateModel): Promise => { + try { + const updateData = await this._compositionOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError("Composition Operation Data not found!"); + } + + if (model.Name) { + updateData.Name = model.Name; + } + if (model.Description) { + updateData.Description = model.Description; + } + if (model.Type) { + updateData.Type = model.Type as any; + } + if (model.Operator) { + updateData.Operator = model.Operator as CompositionOperatorType; + } + if (model.Operands) { + updateData.Operands = model.Operands; + } + + updateData.UpdatedAt = new Date(); + + const record = await this._compositionOperationRepo.save(updateData); + return CompositionOperationMapper.toCompositionOperationDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getCompositionOperationById = async (id: string): Promise => { + try { + const record = await this._compositionOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return CompositionOperationMapper.toCompositionOperationDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + deleteCompositionOperation = async (id: string): Promise => { + try { + const record = await this._compositionOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._compositionOperationRepo.save(record); + + return true; // Soft delete successful + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + searchCompositionOperation = async (filters: OperationSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._compositionOperationRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => CompositionOperationMapper.toCompositionOperationDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: OperationSearchFilters) => { + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where["id"] = filters.id; + } + + if (filters.name) { + search.where["Name"] = filters.name; + } + + if (filters.description) { + search.where["Description"] = filters.description; + } + + if (filters.operator) { + search.where["Operator"] = filters.operator; + } + + return search; + }; + +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/field.operations/field.operations.function.expression.repo.ts b/src/database/sql/typeorm/repositories/field.operations/field.operations.function.expression.repo.ts new file mode 100644 index 0000000..4a4f062 --- /dev/null +++ b/src/database/sql/typeorm/repositories/field.operations/field.operations.function.expression.repo.ts @@ -0,0 +1,162 @@ +import { IFunctionExpressionOperationRepo } from "../../../../repository.interfaces/field.operations/function.expression.operation/function.expression.operation.repo.interface"; +import { + FunctionExpressionOperationResponseDto, + FunctionExpressionOperationCreateModel, + FunctionExpressionOperationUpdateModel, + OperationSearchFilters +} from "../../../../../domain.types/forms/operation.domain.types"; +import { FunctionExpressionOperationMapper } from "../../mappers/function.expression.operation.mapper"; +import { FunctionExpressionOperationEntity } from "../../models/operation/function.expression.operation.model"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class FunctionExpressionOperationRepo extends BaseRepo implements IFunctionExpressionOperationRepo { + + _functionExpressionOperationRepo: Repository = Source.getRepository(FunctionExpressionOperationEntity); + + // Function Expression Operation operations + createFunctionExpressionOperation = async (model: FunctionExpressionOperationCreateModel): Promise => { + try { + const data = this._functionExpressionOperationRepo.create({ + Name: model.Name, + Description: model.Description, + Type: model.Type, + Expression: model.Expression, + Variables: model.Variables, + ResultDataType: model.ResultDataType + }); + const record = await this._functionExpressionOperationRepo.save(data); + return FunctionExpressionOperationMapper.toFunctionExpressionOperationDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + updateFunctionExpressionOperation = async (id: string, model: FunctionExpressionOperationUpdateModel): Promise => { + try { + const updateData = await this._functionExpressionOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError("Function Expression Operation Data not found!"); + } + + if (model.Name) { + updateData.Name = model.Name; + } + if (model.Description) { + updateData.Description = model.Description; + } + if (model.Type) { + updateData.Type = model.Type as any; + } + if (model.Expression) { + updateData.Expression = model.Expression; + } + if (model.Variables) { + updateData.Variables = model.Variables; + } + if (model.ResultDataType) { + updateData.ResultDataType = model.ResultDataType; + } + + + updateData.UpdatedAt = new Date(); + + const record = await this._functionExpressionOperationRepo.save(updateData); + return FunctionExpressionOperationMapper.toFunctionExpressionOperationDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getFunctionExpressionOperationById = async (id: string): Promise => { + try { + const record = await this._functionExpressionOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return FunctionExpressionOperationMapper.toFunctionExpressionOperationDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + deleteFunctionExpressionOperation = async (id: string): Promise => { + try { + const record = await this._functionExpressionOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._functionExpressionOperationRepo.save(record); + + return true; // Soft delete successful + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + searchFunctionExpressionOperation = async (filters: OperationSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._functionExpressionOperationRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => FunctionExpressionOperationMapper.toFunctionExpressionOperationDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: OperationSearchFilters) => { + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where["id"] = filters.id; + } + + if (filters.name) { + search.where["Name"] = filters.name; + } + + if (filters.description) { + search.where["Description"] = filters.description; + } + + return search; + }; + +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/field.operations/field.operations.iterate.repo.ts b/src/database/sql/typeorm/repositories/field.operations/field.operations.iterate.repo.ts new file mode 100644 index 0000000..6575b95 --- /dev/null +++ b/src/database/sql/typeorm/repositories/field.operations/field.operations.iterate.repo.ts @@ -0,0 +1,165 @@ +import { IIterateOperationRepo } from "../../../../repository.interfaces/field.operations/iterate.operation/iterate.operation.repo.interface"; +import { + IterateOperationResponseDto, + IterateOperationCreateModel, + IterateOperationUpdateModel, + OperationSearchFilters +} from "../../../../../domain.types/forms/operation.domain.types"; +import { IterateOperationMapper } from "../../mappers/iterate.operation.mapper"; +import { IterateOperationEntity } from "../../models/operation/iterate.operation.model"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class IterateOperationRepo extends BaseRepo implements IIterateOperationRepo { + + _iterateOperationRepo: Repository = Source.getRepository(IterateOperationEntity); + + // Iterate Operation operations + createIterateOperation = async (model: IterateOperationCreateModel): Promise => { + try { + const data = this._iterateOperationRepo.create({ + Name: model.Name, + Description: model.Description, + Type: model.Type, + CollectionField: model.CollectionField, + ResultField: model.ResultField, + OperationId: model.OperationId, + FilterExpression: model.FilterExpression + }); + const record = await this._iterateOperationRepo.save(data); + return IterateOperationMapper.toIterateOperationDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + updateIterateOperation = async (id: string, model: IterateOperationUpdateModel): Promise => { + try { + const updateData = await this._iterateOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError("Iterate Operation Data not found!"); + } + + if (model.Name) { + updateData.Name = model.Name; + } + if (model.Description) { + updateData.Description = model.Description; + } + if (model.Type) { + updateData.Type = model.Type as any; + } + if (model.CollectionField) { + updateData.CollectionField = model.CollectionField; + } + if (model.ResultField) { + updateData.ResultField = model.ResultField; + } + if (model.OperationId) { + updateData.OperationId = model.OperationId; + } + if (model.FilterExpression) { + updateData.FilterExpression = model.FilterExpression; + } + + updateData.UpdatedAt = new Date(); + + const record = await this._iterateOperationRepo.save(updateData); + return IterateOperationMapper.toIterateOperationDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getIterateOperationById = async (id: string): Promise => { + try { + const record = await this._iterateOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return IterateOperationMapper.toIterateOperationDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + deleteIterateOperation = async (id: string): Promise => { + try { + const record = await this._iterateOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._iterateOperationRepo.save(record); + + return true; // Soft delete successful + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + searchIterateOperation = async (filters: OperationSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._iterateOperationRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => IterateOperationMapper.toIterateOperationDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: OperationSearchFilters) => { + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where["id"] = filters.id; + } + + if (filters.name) { + search.where["Name"] = filters.name; + } + + if (filters.description) { + search.where["Description"] = filters.description; + } + + return search; + }; + +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/field.operations/field.operations.logical.repo.ts b/src/database/sql/typeorm/repositories/field.operations/field.operations.logical.repo.ts new file mode 100644 index 0000000..2877023 --- /dev/null +++ b/src/database/sql/typeorm/repositories/field.operations/field.operations.logical.repo.ts @@ -0,0 +1,162 @@ +import { ILogicalOperationRepo } from "../../../../repository.interfaces/field.operations/logical.operation/logical.operation.repo.interface"; +import { + LogicalOperationResponseDto, + LogicalOperationCreateModel, + LogicalOperationUpdateModel, + OperationSearchFilters +} from "../../../../../domain.types/forms/operation.domain.types"; +import { LogicalOperationMapper } from "../../mappers/logical.operation.mapper"; +import { LogicalOperationEntity } from "../../models/operation/logical.operation.model"; +import { LogicalOperatorType } from "../../../../../domain.types/forms/operation.enums"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class LogicalOperationRepo extends BaseRepo implements ILogicalOperationRepo { + + _logicalOperationRepo: Repository = Source.getRepository(LogicalOperationEntity); + + // Logical Operation operations + createLogicalOperation = async (model: LogicalOperationCreateModel): Promise => { + try { + const data = this._logicalOperationRepo.create({ + Name: model.Name, + Description: model.Description, + Type: model.Type, + Operator: model.Operator, + Operands: model.Operands + }); + const record = await this._logicalOperationRepo.save(data); + return LogicalOperationMapper.toLogicalOperationDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + updateLogicalOperation = async (id: string, model: LogicalOperationUpdateModel): Promise => { + try { + const updateData = await this._logicalOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError("Logical Operation Data not found!"); + } + + if (model.Name) { + updateData.Name = model.Name; + } + if (model.Description) { + updateData.Description = model.Description; + } + if (model.Type) { + updateData.Type = model.Type as any; + } + if (model.Operator) { + updateData.Operator = model.Operator as LogicalOperatorType; + } + if (model.Operands) { + updateData.Operands = model.Operands; + } + + updateData.UpdatedAt = new Date(); + + const record = await this._logicalOperationRepo.save(updateData); + return LogicalOperationMapper.toLogicalOperationDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getLogicalOperationById = async (id: string): Promise => { + try { + const record = await this._logicalOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return LogicalOperationMapper.toLogicalOperationDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + deleteLogicalOperation = async (id: string): Promise => { + try { + const record = await this._logicalOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._logicalOperationRepo.save(record); + + return true; // Soft delete successful + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + searchLogicalOperation = async (filters: OperationSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._logicalOperationRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => LogicalOperationMapper.toLogicalOperationDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: OperationSearchFilters) => { + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where["id"] = filters.id; + } + + if (filters.name) { + search.where["Name"] = filters.name; + } + + if (filters.description) { + search.where["Description"] = filters.description; + } + + if (filters.operator) { + search.where["Operator"] = filters.operator; + } + + return search; + }; + +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/field.operations/field.operations.mathematical.repo.ts b/src/database/sql/typeorm/repositories/field.operations/field.operations.mathematical.repo.ts new file mode 100644 index 0000000..7b264a6 --- /dev/null +++ b/src/database/sql/typeorm/repositories/field.operations/field.operations.mathematical.repo.ts @@ -0,0 +1,166 @@ +import { IMathematicalOperationRepo } from "../../../../repository.interfaces/field.operations/mathematical.operation/mathematical.operation.repo.interface"; +import { + MathematicalOperationResponseDto, + MathematicalOperationCreateModel, + MathematicalOperationUpdateModel, + OperationSearchFilters +} from "../../../../../domain.types/forms/operation.domain.types"; +import { MathematicalOperationMapper } from "../../mappers/mathematical.operation.mapper"; +import { MathematicalOperationEntity } from "../../models/operation/mathematical.operation.model"; +import { MathematicalOperatorType } from "../../../../../domain.types/forms/operation.enums"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { BaseRepo } from "../base.repo"; +import { FindManyOptions, Repository } from "typeorm"; + +export class MathematicalOperationRepo extends BaseRepo implements IMathematicalOperationRepo { + + _mathematicalOperationRepo: Repository = Source.getRepository(MathematicalOperationEntity); + + // Mathematical Operation operations + createMathematicalOperation = async (model: MathematicalOperationCreateModel): Promise => { + try { + const data = this._mathematicalOperationRepo.create({ + Name: model.Name, + Description: model.Description, + Type: model.Type, + Operator: model.Operator, + Operands: model.Operands, + ResultDataType: model.ResultDataType + }); + const record = await this._mathematicalOperationRepo.save(data); + return MathematicalOperationMapper.toMathematicalOperationDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + updateMathematicalOperation = async (id: string, model: MathematicalOperationUpdateModel): Promise => { + try { + const updateData = await this._mathematicalOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError("Mathematical Operation Data not found!"); + } + + if (model.Name) { + updateData.Name = model.Name; + } + if (model.Description) { + updateData.Description = model.Description; + } + if (model.Type) { + updateData.Type = model.Type as any; + } + if (model.Operator) { + updateData.Operator = model.Operator as MathematicalOperatorType; + } + if (model.Operands) { + updateData.Operands = model.Operands; + } + if (model.ResultDataType) { + updateData.ResultDataType = model.ResultDataType; + } + + updateData.UpdatedAt = new Date(); + + const record = await this._mathematicalOperationRepo.save(updateData); + return MathematicalOperationMapper.toMathematicalOperationDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getMathematicalOperationById = async (id: string): Promise => { + try { + const record = await this._mathematicalOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return MathematicalOperationMapper.toMathematicalOperationDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + deleteMathematicalOperation = async (id: string): Promise => { + try { + const record = await this._mathematicalOperationRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._mathematicalOperationRepo.save(record); + + return true; // Soft delete successful + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + searchMathematicalOperation = async (filters: OperationSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = + this.addSortingAndPagination(search, filters); + const [list, count] = await this._mathematicalOperationRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === "DESC" ? "descending" : "ascending", + OrderedBy: orderByColumn, + Items: list.map((x) => MathematicalOperationMapper.toMathematicalOperationDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError( + "DB Error: Unable to search records!", + error + ); + } + }; + + private getSearchModel = (filters: OperationSearchFilters) => { + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where["id"] = filters.id; + } + + if (filters.name) { + search.where["Name"] = filters.name; + } + + if (filters.description) { + search.where["Description"] = filters.description; + } + + if (filters.operator) { + search.where["Operator"] = filters.operator; + } + + return search; + }; + +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/field.rules/field.rules.calculation.repo.ts b/src/database/sql/typeorm/repositories/field.rules/field.rules.calculation.repo.ts new file mode 100644 index 0000000..0f99a3c --- /dev/null +++ b/src/database/sql/typeorm/repositories/field.rules/field.rules.calculation.repo.ts @@ -0,0 +1,160 @@ +import { CalculationRuleCreateModel, CalculationRuleResponseDto, CalculationRuleSearchFilters, CalculationRuleUpdateModel } from "../../../../../domain.types/forms/calculation.rule.domain.types"; +import { ICalculationRuleRepo } from "../../../../repository.interfaces/field.rules/calculation.rule/calculation.rule.repo.interface"; +import { CalculationRuleEntity } from "../../models/rule/calculation.rule.model"; +import { CalculationRuleMapper } from "../../mappers/calculation.rule.mapper"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { FindManyOptions, Repository } from "typeorm"; +import { BaseRepo } from "../base.repo"; + +export class CalculationRuleRepo extends BaseRepo implements ICalculationRuleRepo { + + _calculationRuleRepo: Repository = Source.getRepository(CalculationRuleEntity); + + create = async (model: CalculationRuleCreateModel): Promise => { + try { + const data = await this._calculationRuleRepo.create({ + Name: model.Name, + Description: model.Description, + Priority: model.Priority, + IsActive: model.IsActive, + ConditionForOperationId: model.ConditionForOperationId, + OperationId: model.OperationId, + LogicId: model.LogicId + }); + const record = await this._calculationRuleRepo.save(data); + return CalculationRuleMapper.toCalculationRuleDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById = async (id: string): Promise => { + try { + var record = await this._calculationRuleRepo.findOne({ + where: { + id: id, + DeletedAt: null + } + }); + return CalculationRuleMapper.toCalculationRuleDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update = async (id: string, model: CalculationRuleUpdateModel): Promise => { + try { + const updateData = await this._calculationRuleRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError('Calculation Rule Data not found!'); + } + if (model.Name) { + updateData.Name = model.Name; + } + if (model.Description) { + updateData.Description = model.Description; + } + if (model.ConditionForOperationId) { + updateData.ConditionForOperationId = model.ConditionForOperationId; + } + if (model.OperationId) { + updateData.OperationId = model.OperationId; + } + if (model.LogicId) { + updateData.LogicId = model.LogicId; + } + var record = await this._calculationRuleRepo.save(updateData); + return CalculationRuleMapper.toCalculationRuleDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete = async (id: string): Promise => { + try { + var record = await this._calculationRuleRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._calculationRuleRepo.save(record); + return true; // Soft delete successful + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search = async (filters: CalculationRuleSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._calculationRuleRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === 'DESC' ? 'descending' : 'ascending', + OrderedBy: orderByColumn, + Items: list.map(x => CalculationRuleMapper.toCalculationRuleDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + } + }; + + private getSearchModel = (filters: CalculationRuleSearchFilters) => { + + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where['id'] = filters.id; + } + + if (filters.name) { + search.where['Name'] = filters.name; + } + + if (filters.description) { + search.where['Description'] = filters.description; + } + + if (filters.priority) { + search.where['Priority'] = filters.priority; + } + + if (filters.isActive !== undefined) { + search.where['IsActive'] = filters.isActive; + } + + if (filters.operationId) { + search.where['OperationId'] = filters.operationId; + } + + if (filters.logicId) { + search.where['LogicId'] = filters.logicId; + } + + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/field.rules/field.rules.skip.repo.ts b/src/database/sql/typeorm/repositories/field.rules/field.rules.skip.repo.ts new file mode 100644 index 0000000..eb87b59 --- /dev/null +++ b/src/database/sql/typeorm/repositories/field.rules/field.rules.skip.repo.ts @@ -0,0 +1,161 @@ +import { SkipRuleCreateModel, SkipRuleResponseDto, SkipRuleSearchFilters, SkipRuleUpdateModel } from "../../../../../domain.types/forms/skip.rule.domain.types"; +import { ISkipRuleRepo } from "../../../../repository.interfaces/field.rules/skip.rule/skip.rule.repo.interface"; +import { SkipRuleEntity } from "../../models/rule/skip.rule.model"; +import { SkipRuleMapper } from "../../mappers/skip.rule.mapper"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { FindManyOptions, Repository } from "typeorm"; +import { BaseRepo } from "../base.repo"; + +export class SkipRuleRepo extends BaseRepo implements ISkipRuleRepo { + + _skipRuleRepo: Repository = Source.getRepository(SkipRuleEntity); + + create = async (model: SkipRuleCreateModel): Promise => { + try { + const data = await this._skipRuleRepo.create({ + Name: model.Name, + Description: model.Description, + Priority: model.Priority, + IsActive: model.IsActive, + OperationId: model.OperationId, + SkipWhenTrue: model.SkipWhenTrue, + LogicId: model.LogicId + }); + const record = await this._skipRuleRepo.save(data); + return SkipRuleMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById = async (id: string): Promise => { + try { + var record = await this._skipRuleRepo.findOne({ + where: { + id: id, + DeletedAt: null + }, + // relations: ['Operation', 'Logic'] + }); + return SkipRuleMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update = async (id: string, model: SkipRuleUpdateModel): Promise => { + try { + const updateData = await this._skipRuleRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError('Skip Rule Data not found!'); + } + if (model.Name) { + updateData.Name = model.Name; + } + if (model.Description) { + updateData.Description = model.Description; + } + if (model.OperationId) { + updateData.OperationId = model.OperationId; + } + if (model.SkipWhenTrue !== undefined) { + updateData.SkipWhenTrue = model.SkipWhenTrue; + } + if (model.LogicId) { + updateData.LogicId = model.LogicId; + } + var record = await this._skipRuleRepo.save(updateData); + return SkipRuleMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete = async (id: string): Promise => { + try { + var record = await this._skipRuleRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._skipRuleRepo.save(record); + return true; // Soft delete successful + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search = async (filters: SkipRuleSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._skipRuleRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === 'DESC' ? 'descending' : 'ascending', + OrderedBy: orderByColumn, + Items: list.map(x => SkipRuleMapper.toDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + } + }; + + private getSearchModel = (filters: SkipRuleSearchFilters) => { + + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where['id'] = filters.id; + } + + if (filters.name) { + search.where['Name'] = filters.name; + } + + if (filters.description) { + search.where['Description'] = filters.description; + } + + if (filters.priority) { + search.where['Priority'] = filters.priority; + } + + if (filters.isActive !== undefined) { + search.where['IsActive'] = filters.isActive; + } + + if (filters.operationId) { + search.where['OperationId'] = filters.operationId; + } + + if (filters.logicId) { + search.where['LogicId'] = filters.logicId; + } + + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/field.rules/field.rules.validation.repo.ts b/src/database/sql/typeorm/repositories/field.rules/field.rules.validation.repo.ts new file mode 100644 index 0000000..c64804c --- /dev/null +++ b/src/database/sql/typeorm/repositories/field.rules/field.rules.validation.repo.ts @@ -0,0 +1,171 @@ +import { ValidationRuleCreateModel, ValidationRuleResponseDto, ValidationRuleSearchFilters, ValidationRuleUpdateModel } from "../../../../../domain.types/forms/validation.rule.domain.types"; +import { IValidationRuleRepo } from "../../../../repository.interfaces/field.rules/validation.rule/validation.rule.repo.interface"; +import { ValidationRuleEntity } from "../../models/rule/validation.rule.model"; +import { ValidationRuleMapper } from "../../mappers/validation.rule.mapper"; +import { Source } from "../../database.connector.typeorm"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { Logger } from "../../../../../common/logger"; +import { FindManyOptions, Repository } from "typeorm"; +import { BaseRepo } from "../base.repo"; + +export class ValidationRuleRepo extends BaseRepo implements IValidationRuleRepo { + + _validationRuleRepo: Repository = Source.getRepository(ValidationRuleEntity); + + create = async (model: ValidationRuleCreateModel): Promise => { + try { + const data = await this._validationRuleRepo.create({ + Name: model.Name, + Description: model.Description, + Priority: model.Priority, + IsActive: model.IsActive, + OperationId: model.OperationId, + ErrorWhenFalse: model.ErrorWhenFalse, + ErrorMessage: model.ErrorMessage, + LogicId: model.LogicId + }); + const record = await this._validationRuleRepo.save(data); + return ValidationRuleMapper.toValidationRuleDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById = async (id: string): Promise => { + try { + var record = await this._validationRuleRepo.findOne({ + where: { + id: id, + DeletedAt: null + }, + // relations: ['Operation', 'Logic'] + }); + return ValidationRuleMapper.toValidationRuleDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update = async (id: string, model: ValidationRuleUpdateModel): Promise => { + try { + const updateData = await this._validationRuleRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError('Validation Rule Data not found!'); + } + if (model.Name) { + updateData.Name = model.Name; + } + if (model.Description) { + updateData.Description = model.Description; + } + if (model.Priority !== undefined) { + updateData.Priority = model.Priority; + } + if (model.IsActive !== undefined) { + updateData.IsActive = model.IsActive; + } + if (model.OperationId) { + updateData.OperationId = model.OperationId; + } + if (model.ErrorWhenFalse !== undefined) { + updateData.ErrorWhenFalse = model.ErrorWhenFalse; + } + if (model.ErrorMessage) { + updateData.ErrorMessage = model.ErrorMessage; + } + if (model.LogicId) { + updateData.LogicId = model.LogicId; + } + var record = await this._validationRuleRepo.save(updateData); + return ValidationRuleMapper.toValidationRuleDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete = async (id: string): Promise => { + try { + var record = await this._validationRuleRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._validationRuleRepo.save(record); + return true; // Soft delete successful + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search = async (filters: ValidationRuleSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._validationRuleRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === 'DESC' ? 'descending' : 'ascending', + OrderedBy: orderByColumn, + Items: list.map(x => ValidationRuleMapper.toValidationRuleDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + } + }; + + private getSearchModel = (filters: ValidationRuleSearchFilters) => { + + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.id) { + search.where['id'] = filters.id; + } + + if (filters.name) { + search.where['Name'] = filters.name; + } + + if (filters.description) { + search.where['Description'] = filters.description; + } + + if (filters.priority) { + search.where['Priority'] = filters.priority; + } + + if (filters.isActive !== undefined) { + search.where['IsActive'] = filters.isActive; + } + + if (filters.operationId) { + search.where['OperationId'] = filters.operationId; + } + + if (filters.logicId) { + search.where['LogicId'] = filters.logicId; + } + + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/form.field/form.field.repo.ts b/src/database/sql/typeorm/repositories/form.field/form.field.repo.ts new file mode 100644 index 0000000..39dd226 --- /dev/null +++ b/src/database/sql/typeorm/repositories/form.field/form.field.repo.ts @@ -0,0 +1,280 @@ +import { FormFieldCreateModel, FormFieldOption } from "../../../../../domain.types/forms/form.field.domain.types"; +import { IFormFieldRepo } from "../../../../repository.interfaces/form.field/form.field.repo.interface"; +import { FormFieldResponseDto } from "../../../../../domain.types/forms/form.field.domain.types"; +import { FormFieldUpdateModel } from "../../../../../domain.types/forms/form.field.domain.types"; +import { FormFieldSearchFilters } from "../../../../../domain.types/forms/form.field.domain.types"; +import { Source } from "../../database.connector.typeorm"; +import { FormFieldEntity } from "../../models/form.field/form.field.model"; +import { FormFieldMapper } from "../../mappers/form.field.mapper"; +import { Logger } from "../../../../../common/logger"; +import { ErrorHandler } from "../../../../../common/handlers/error.handler"; +import { FindManyOptions, Repository } from "typeorm"; +import { BaseRepo } from "../base.repo"; +import { QueryResponseType } from "../../../../../domain.types/forms/query.response.types"; + +export class FormFieldRepo extends BaseRepo implements IFormFieldRepo { + + _formFieldRepo: Repository = Source.getRepository(FormFieldEntity); + + create = async (model: FormFieldCreateModel): Promise => { + try { + + // let jsonData: Prisma.JsonValue | undefined; + + let jsonData: FormFieldOption[] | undefined; + + // Map model.Options to the appropriate structure for JSON storage + if (model.Options && model.Options.length > 0) { + jsonData = model.Options.map((option) => ({ + Text: option.Text, + Sequence: option.Sequence, + ImageUrl: option.ImageUrl, + })); + } + + const data = await this._formFieldRepo.create({ + Title: model.Title, + Description: model.Description, + TemplateId: model.ParentTemplateId, + ParentSectionId: model.ParentSectionId, + DisplayCode: model.DisplayCode, + ResponseType: model.ResponseType as QueryResponseType, + Score: model.Score, + // CorrectAnswer: model.CorrectAnswer, + IsRequired: model.IsRequired, + Hint: model.Hint, + Sequence: model.Sequence, + Options: JSON.stringify(jsonData), // Only assign if jsonData is defined + // QuestionImageUrl: model.QuestionImageUrl, + RangeMax: model.RangeMax, + RangeMin: model.RangeMin, + CreatedAt: new Date(), + // UpdatedAt: new Date(), // Uncomment and modify as needed + // DeletedAt: null, // Uncomment and modify as needed + }); + const record = await this._formFieldRepo.save(data); + return FormFieldMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + update = async (id: string, model: FormFieldUpdateModel): Promise => { + try { + + // let jsonData: Prisma.JsonValue | undefined; + + let jsonData: FormFieldOption[] | undefined; + + // Map model.Options to the appropriate structure for JSON storage + if (model.Options && model.Options.length > 0) { + jsonData = model.Options.map((option) => ({ + Text: option.Text, + Sequence: option.Sequence, + ImageUrl: option.ImageUrl, + })); + } + + + const updateData = await this._formFieldRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError("FormField Data not found!"); + } + // if (model.SectionIdentifier) { + // updateData.SectionIdentifier = model.SectionIdentifier; + // } + if (model.Title) { + updateData.Title = model.Title; + } + if (model.Description) { + updateData.Description = model.Description; + } + if (model.DisplayCode) { + updateData.DisplayCode = model.DisplayCode; + } + + if (model.Score) { + updateData.Score = model.Score; + } + + // if (model.CorrectAnswer) { + // updateData.CorrectAnswer = model.CorrectAnswer; + // } + + if (model.IsRequired) { + updateData.IsRequired = model.IsRequired; + } + + if (model.Hint) { + updateData.Hint = model.Hint; + } + + updateData.Options = JSON.stringify(jsonData); + + // if (model.QuestionImageUrl) { + // updateData.QuestionImageUrl = model.QuestionImageUrl; + // } + + if (model.RangeMax) { + updateData.RangeMax = model.RangeMax; + } + + if (model.RangeMin) { + updateData.RangeMin = model.RangeMin; + } + + updateData.UpdatedAt = new Date(); + + var record = await this._formFieldRepo.save(updateData); + return FormFieldMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById = async (id: string): Promise => { + try { + var record = await this._formFieldRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + relations: ['ParentFormSection', 'FormTemplate'], + }); + return FormFieldMapper.toDto(record); + } + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getByTemplateId = async (id: string): Promise => { + try { + var records = await this._formFieldRepo.find({ + where: { + TemplateId: id, + DeletedAt: null, + }, + relations: ['ParentFormSection', 'FormTemplate'], + }); + + const searchResults = { + TotalCount: records.length, + RetrievedCount: records.length, + PageIndex: 1, + ItemsPerPage: records.length, + Order: 'ASC', + OrderedBy: 'CreatedAt', + Items: records.map((item) => FormFieldMapper.toDto(item)), + }; + + return searchResults; + } + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete = async (id: string): Promise => { + try { + var record = await this._formFieldRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._formFieldRepo.save(record); + return true; // Soft delete successful + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search = async (filters: FormFieldSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._formFieldRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order, + OrderedBy: orderByColumn, + Items: list.map((item) => FormFieldMapper.toDto(item)), + }; + + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + private getSearchModel = (filters: FormFieldSearchFilters) => { + const search: FindManyOptions = { + where: { + DeletedAt: null, + }, + }; + + if (filters.id) { + search.where['id'] = filters.id; + } + if (filters.parentTemplateId) { + search.where['TemplateId'] = filters.parentTemplateId; + } + if (filters.parentSectionId) { + search.where['ParentSectionId'] = filters.parentSectionId; + } + if (filters.title) { + search.where['Title'] = filters.title; + } + if (filters.description) { + search.where['Description'] = filters.description; + } + if (filters.displayCode) { + search.where['DisplayCode'] = filters.displayCode; + } + if (filters.responseType) { + search.where['ResponseType'] = filters.responseType; + } + if (filters.score) { + search.where['Score'] = filters.score; + } + if (filters.correctAnswer) { + search.where['ExpectedAnswer'] = filters.correctAnswer; + } + if (filters.isRequired) { + search.where['IsRequired'] = filters.isRequired; + } + if (filters.hint) { + search.where['Hint'] = filters.hint; + } + if (filters.questionImageUrl) { + search.where['ImageResourceId'] = filters.questionImageUrl; + } + if (filters.rangeMin) { + search.where['RangeMin'] = filters.rangeMin; + } + if (filters.rangeMax) { + search.where['RangeMax'] = filters.rangeMax; + } + + return search; + }; +} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts b/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts index b547111..99843ce 100644 --- a/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts +++ b/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts @@ -10,12 +10,12 @@ import { BaseRepo } from "../base.repo"; -export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ +export class FormSectionRepo extends BaseRepo implements IFormSectionRepo { - _formSectionRepo : Repository = Source.getRepository(FormSection); + _formSectionRepo: Repository = Source.getRepository(FormSection); + + create = async (model: FormSectionCreateModel): Promise => { - create= async(model: FormSectionCreateModel): Promise =>{ - // const entity={ // ParentFormTemplate: { // connect: { id: model.ParentFormTemplateId } @@ -28,7 +28,7 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ // ParentSectionId: model.ParentSectionId, // } - try { + try { const data = await this._formSectionRepo.create({ // FormTemplate: { // connect: { id: model.ParentFormTemplateId } @@ -46,14 +46,14 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } - + }; - - update=async (id: string, model: FormSectionUpdateModel): Promise => { - try { - const updateData = await this._formSectionRepo.findOne({ - where : { - id : id, + + update = async (id: string, model: FormSectionUpdateModel): Promise => { + try { + const updateData = await this._formSectionRepo.findOne({ + where: { + id: id, DeletedAt: null, }, }); @@ -66,7 +66,7 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ if (model.Title) { updateData.Title = model.Title; } - if ( model.Description) { + if (model.Description) { updateData.Description = model.Description; } if (model.DisplayCode) { @@ -85,13 +85,13 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } - }; - - getById=async (id: string): Promise => { + }; + + getById = async (id: string): Promise => { try { var record = await this._formSectionRepo.findOne({ - where : { - id : id, + where: { + id: id, DeletedAt: null }, }); @@ -100,13 +100,13 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); } - }; - - delete=async (id: string): Promise =>{ - try { + }; + + delete = async (id: string): Promise => { + try { var record = await this._formSectionRepo.findOne({ - where : { - id : id, + where: { + id: id, DeletedAt: null, }, }); @@ -120,13 +120,13 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); } - }; - - getByTemplateId=async (id: string): Promise=>{ + }; + + getByTemplateId = async (id: string): Promise => { try { var record = await this._formSectionRepo.findOne({ - where : { - FormTemplateId : id + where: { + FormTemplateId: id }, }); return FormSectionMapper.toDto(record); @@ -134,38 +134,38 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); } - }; - - search=async (filters: FormSectionSearchFilters) : Promise =>{ - try { - var search = this.getSearchModel(filters); + }; + + search = async (filters: FormSectionSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); const [list, count] = await this._formSectionRepo.findAndCount(search); const searchResults = { - TotalCount : count, - RetrievedCount : list.length, - PageIndex : pageIndex, - ItemsPerPage : limit, - Order : order === 'DESC' ? 'descending' : 'ascending', - OrderedBy : orderByColumn, - Items : list.map(x => FormSectionMapper.toDto(x)), + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === 'DESC' ? 'descending' : 'ascending', + OrderedBy: orderByColumn, + Items: list.map(x => FormSectionMapper.toDto(x)), }; return searchResults; } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); } - }; + }; - private getSearchModel = (filters: FormSectionSearchFilters) => { + private getSearchModel = (filters: FormSectionSearchFilters) => { - var search : FindManyOptions = { - relations : {}, - where : {}, + var search: FindManyOptions = { + relations: {}, + where: {}, }; - if (filters.id) { + if (filters.id) { search.where['id'] = filters.id; } @@ -180,15 +180,15 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ // } if (filters.title) { - search.where['Title'] = filters.title; + search.where['Title'] = filters.title; } if (filters.description) { - search.where['Description'] = filters.description; + search.where['Description'] = filters.description; } if (filters.sequence) { - search.where['Sequence'] = filters.sequence; + search.where['Sequence'] = filters.sequence; } if (filters.parentSectionId) { search.where['ParentSectionId'] = filters.parentSectionId; @@ -197,5 +197,5 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo{ return search; }; - + } \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts b/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts index 16d55f2..24bff75 100644 --- a/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts +++ b/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts @@ -1,220 +1,220 @@ import { FormSubmissionCreateModel, FormSubmissionSearchFilters, FormSubmissionUpdateModel } from "../../../../../domain.types/forms/form.submission.domain.types"; import { IFormSubmissionRepo } from "../../../../repository.interfaces/form.submission/form.submission.repo.interface"; import { FormSubmissionDto } from "../../../../../domain.types/forms/form.submission.domain.types"; -import { FormStatus, FormSubmission } from "../../models/form.submission/form.submission.model"; +import { FormSubmission } from "../../models/form.submission/form.submission.model"; +import { FormStatus } from "../../../../../domain.types/forms/form.submission.enums"; import { Source } from "../../database.connector.typeorm"; import { FormMapper } from "../../mappers/form.submission.mapper"; import { ErrorHandler } from "../../../../../common/handlers/error.handler"; import { Logger } from "../../../../../common/logger"; -import { FindManyOptions } from "typeorm"; +import { FindManyOptions, Repository } from "typeorm"; import { uuid } from "../../../../../domain.types/miscellaneous/system.types"; import { BaseRepo } from "../base.repo"; -export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo{ - - _formSubmissionRepo = Source.getRepository(FormSubmission); - - create=async (model: FormSubmissionCreateModel) : Promise => { - try { - const data = await this._formSubmissionRepo.create({ - // FormTemplate: { - // connect: { id: model.ParentFormTemplateId } - // }, - // SectionIdentifier: model.SectionIdentifier, - // FormTemplate: { - // connect: { id: model.FormTemplateId } - // }, - FormTemplateId: model.FormTemplateId, - Title: model.Title, - UserId: model.UserId, - Status: model.Status, - ValidTill: model.ValidTill, - - }); - const record = await this._formSubmissionRepo.save(data); - return FormMapper.toDto(record); - } - - catch (error) { - ErrorHandler.throwInternalServerError(error.message, 500); - } - - }; - - update=async (id: string, model: FormSubmissionUpdateModel) : Promise => { - try { - const updateData = await this._formSubmissionRepo.findOne({ - where : { - id : id, - DeletedAt: null, - }, - }); - if (!updateData) { - ErrorHandler.throwNotFoundError('Form Section Data not found!'); - } - // if (model.SectionIdentifier) { - // updateData.SectionIdentifier = model.SectionIdentifier; - // } - if (model.UserId) { - updateData.UserId = model.UserId; - } - if ( model.Encrypted) { - updateData.Encrypted = model.Encrypted; - } - if (model.Unencrypted) { - updateData.Unencrypted = model.Unencrypted; - } - - if (model.Link) { - updateData.Link = model.Link; - } - - // if (model.QueryParams) { - // updateData.QueryParams = model.QueryParams; - // } - - if (model.LinkQueryParams) { - updateData.LinkQueryParams = model.LinkQueryParams; - } - - if (model.ValidTill) { - updateData.ValidTill = model.ValidTill; - } - - if (model.SubmittedAt) { - updateData.SubmittedAt = model.SubmittedAt; - } - - // if (model.Status) { - // updateData.Status = model.Status; - // } - - // if (model.Category) { - // updateData.Category = model.Category; - // } - - var record = await this._formSubmissionRepo.save(updateData); - return FormMapper.toDto(record); - } - catch (error) - { - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - getById=async (id: string) : Promise => { - try { - var record = await this._formSubmissionRepo.findOne({ - where: { - id: id, - DeletedAt: null, - }, - }); - return FormMapper.toDto(record); - } catch (error) { - Logger.instance().log(error.message); - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - delete=async (id: string) : Promise => { - try { - var record = await this._formSubmissionRepo.findOne({ - where : { - id : id, - DeletedAt: null, - }, - }); - if (!record) { - return false; // Record not found - } - record.DeletedAt = new Date(); // Soft delete - await this._formSubmissionRepo.save(record); - return true; // Soft delete successful - } catch (error) { - Logger.instance().log(error.message); - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - submit=async (id: uuid) : Promise => { - try { - const record = await this._formSubmissionRepo.findOne({ - where: { - id: id, - DeletedAt: null, - }, - }); - if (!record) { - ErrorHandler.throwNotFoundError("Form Section Data not found!"); - } - - record.SubmittedAt= new Date(); - record.Status=FormStatus.Submitted; - record.UpdatedAt=new Date(); - - var submittedData = await this._formSubmissionRepo.save(record); - return FormMapper.toDto(submittedData); - } catch (error) { - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - search=async (filters: FormSubmissionSearchFilters) : Promise => { - try { - var search = this.getSearchModel(filters); - var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); - const [list, count] = await this._formSubmissionRepo.findAndCount(search); - - const searchResults = { - TotalCount : count, - RetrievedCount : list.length, - PageIndex : pageIndex, - ItemsPerPage : limit, - Order : order === 'DESC' ? 'descending' : 'ascending', - OrderedBy : orderByColumn, - Items : list.map(x => FormMapper.toDto(x)), - }; - return searchResults; - } catch (error) { - Logger.instance().log(error.message); - ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); - } +export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo { + + _formSubmissionRepo: Repository = Source.getRepository(FormSubmission); + + create = async (model: FormSubmissionCreateModel): Promise => { + try { + const data = await this._formSubmissionRepo.create({ + // FormTemplate: { + // connect: { id: model.ParentFormTemplateId } + // }, + // SectionIdentifier: model.SectionIdentifier, + // FormTemplate: { + // connect: { id: model.FormTemplateId } + // }, + FormTemplateId: model.FormTemplateId, + Title: model.Title, + UserId: model.UserId, + Status: model.Status, + ValidTill: model.ValidTill, + + }); + const record = await this._formSubmissionRepo.save(data); + return FormMapper.toDto(record); + } + + catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + + }; + + update = async (id: string, model: FormSubmissionUpdateModel): Promise => { + try { + const updateData = await this._formSubmissionRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!updateData) { + ErrorHandler.throwNotFoundError('Form Section Data not found!'); + } + // if (model.SectionIdentifier) { + // updateData.SectionIdentifier = model.SectionIdentifier; + // } + if (model.UserId) { + updateData.UserId = model.UserId; + } + if (model.Encrypted) { + updateData.Encrypted = model.Encrypted; + } + if (model.Unencrypted) { + updateData.Unencrypted = model.Unencrypted; + } + + if (model.Link) { + updateData.Link = model.Link; + } + + // if (model.QueryParams) { + // updateData.QueryParams = model.QueryParams; + // } + + if (model.LinkQueryParams) { + updateData.LinkQueryParams = model.LinkQueryParams; + } + + if (model.ValidTill) { + updateData.ValidTill = model.ValidTill; + } + + if (model.SubmittedAt) { + updateData.SubmittedAt = model.SubmittedAt; + } + + // if (model.Status) { + // updateData.Status = model.Status; + // } + + // if (model.Category) { + // updateData.Category = model.Category; + // } + + var record = await this._formSubmissionRepo.save(updateData); + return FormMapper.toDto(record); + } + catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + getById = async (id: string): Promise => { + try { + var record = await this._formSubmissionRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return FormMapper.toDto(record); + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + delete = async (id: string): Promise => { + try { + var record = await this._formSubmissionRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._formSubmissionRepo.save(record); + return true; // Soft delete successful + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + submit = async (id: uuid): Promise => { + try { + const record = await this._formSubmissionRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + if (!record) { + ErrorHandler.throwNotFoundError("Form Section Data not found!"); + } + + record.SubmittedAt = new Date(); + record.Status = FormStatus.Submitted; + record.UpdatedAt = new Date(); + + var submittedData = await this._formSubmissionRepo.save(record); + return FormMapper.toDto(submittedData); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } + }; + + search = async (filters: FormSubmissionSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._formSubmissionRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === 'DESC' ? 'descending' : 'ascending', + OrderedBy: orderByColumn, + Items: list.map(x => FormMapper.toDto(x)), + }; + return searchResults; + } catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + } + }; + + private getSearchModel = (filters: FormSubmissionSearchFilters) => { + + var search: FindManyOptions = { + relations: {}, + where: {}, }; - private getSearchModel = (filters: FormSubmissionSearchFilters) => { - - var search: FindManyOptions = { - relations: {}, - where: {}, - }; - - if (filters.FormTemplateId) { - search.where["FormTemplateId"] = filters.FormTemplateId; - } - - if (filters.UserId) { - search.where["UserId"] = filters.UserId; - } - - if (filters.Encrypted) { - search.where["Encrypted"] = filters.Encrypted; - } - - if (filters.Status) { - search.where["Status"] = filters.Status; - } - - if (filters.ValidTill) { - search.where["ValidTill"] = filters.ValidTill; - } - - if (filters.SubmittedAt) { - search.where["SubmittedAt"] = filters.SubmittedAt; - } - - - return search; - }; - + if (filters.FormTemplateId) { + search.where["FormTemplateId"] = filters.FormTemplateId; + } + + if (filters.UserId) { + search.where["UserId"] = filters.UserId; + } + + if (filters.Encrypted) { + search.where["Encrypted"] = filters.Encrypted; + } + + if (filters.Status) { + search.where["Status"] = filters.Status; + } + + if (filters.ValidTill) { + search.where["ValidTill"] = filters.ValidTill; + } + + if (filters.SubmittedAt) { + search.where["SubmittedAt"] = filters.SubmittedAt; + } + + + return search; + }; + } \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts b/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts index 14fb577..80bff0f 100644 --- a/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts +++ b/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts @@ -5,50 +5,51 @@ import { FormTemplateUpdateModel } from "../../../../../domain.types/forms/form. import { FormTemplateSearchFilters } from "../../../../../domain.types/forms/form.template.domain.types"; import { FormTemplate } from "../../models/form.template/form.template.model"; import { Source } from "../../database.connector.typeorm"; -import { FormType } from "../../models/form.template/form.template.model"; +import { FormType } from "../../../../../domain.types/forms/form.template.enums"; import { FormTemplateMapper } from "../../mappers/form.template.mapper"; import { ErrorHandler } from "../../../../../common/handlers/error.handler"; import { Logger } from "../../../../../common/logger"; import { BaseRepo } from "../base.repo"; import { FindManyOptions, IsNull, Repository } from "typeorm"; -import { Question } from "../../models/question/question.model"; -import { QuestionMapper } from "../../mappers/question.mapper"; +// import { Question } from "../../models/question/question.model"; +// import { QuestionMapper } from "../../mappers/question.mapper"; import { FormSection } from "../../models/form.section/form.section.model"; +import { FormFieldEntity } from "../../models/form.field/form.field.model"; +import { FormFieldMapper } from "../../mappers/form.field.mapper"; export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { - _formTemplateRepo : Repository = Source.getRepository(FormTemplate); + _formTemplateRepo: Repository = Source.getRepository(FormTemplate); - _question: Repository = Source.getRepository(Question); + _formField: Repository = Source.getRepository(FormFieldEntity); - _formSection : Repository = Source.getRepository(FormSection); + _formSection: Repository = Source.getRepository(FormSection); + + create = async (model: FormTemplateCreateModel): Promise => { - create = async ( model: FormTemplateCreateModel): Promise => { - try { - + const data = this._formTemplateRepo.create({ - Title: model.Title, - Description: model.Description, - // CurrentVersion: model.CurrentVersion, - // TenantCode: model.TenantCode, - Type: model.Type as FormType, - // ItemsPerPage: model.ItemsPerPage, - DisplayCode: model.DisplayCode, - // OwnerUserId: model.OwnerUserId, - RootSectionId: model.RootSectionId, - DefaultSectionNumbering: model.DefaultSectionNumbering, - }); - - const record = await this._formTemplateRepo.save(data); - return FormTemplateMapper.toDto(record); - } - - catch (error) - { - ErrorHandler.throwInternalServerError(error.message, 500); - } + Title: model.Title, + Description: model.Description, + // CurrentVersion: model.CurrentVersion, + // TenantCode: model.TenantCode, + Type: model.Type as FormType, + // ItemsPerPage: model.ItemsPerPage, + DisplayCode: model.DisplayCode, + // OwnerUserId: model.OwnerUserId, + RootSectionId: model.RootSectionId, + DefaultSectionNumbering: model.DefaultSectionNumbering, + }); + + const record = await this._formTemplateRepo.save(data); + return FormTemplateMapper.toDto(record); + } + + catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } }; update = async ( @@ -134,14 +135,14 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { }, relations: { FormSections: { - Questions: true, + FormFields: true, FormTemplate: true, }, }, order: { FormSections: { CreatedAt: "ASC", - Questions: { + FormFields: { CreatedAt: "ASC", }, }, @@ -154,8 +155,8 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { ); record.FormSections.forEach((section) => { - if (section.Questions) { - section.Questions = section.Questions.filter( + if (section.FormFields) { + section.FormFields = section.FormFields.filter( (question) => question.DeletedAt === null ); } @@ -257,15 +258,15 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { }; // Fetch and map questions associated with this section - const sectionQuestions = await this._question.find({ + const sectionQuestions = await this._formField.find({ where: { ParentSectionId: section.id, DeletedAt: null }, }); dtoSection.Questions = sectionQuestions.map((question) => - QuestionMapper.toDto(question) + FormFieldMapper.toDto(question) ); // Fetch and map subsections - const subsections = await this._formSection.find({ + const subsections = await this._formField.find({ where: { ParentSectionId: section.id, DeletedAt: null }, }); @@ -284,11 +285,11 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { }; // Fetch questions for this subsection - const subsectionQuestions = await this._question.find({ + const subsectionQuestions = await this._formField.find({ where: { ParentSectionId: subsection.id, DeletedAt: null }, }); dtoSubsection.Questions = subsectionQuestions.map((question) => - QuestionMapper.toDto(question) + FormFieldMapper.toDto(question) ); // Add the subsection to the parent section @@ -365,11 +366,11 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { }, relations: { FormTemplate: true, - Questions: true, + FormFields: true, }, order: { CreatedAt: "ASC", // Sorting sections in ascending order - Questions: { + FormFields: { CreatedAt: "ASC", // Sorting questions within each section }, }, @@ -406,10 +407,10 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { Sections: [], }; - const questions = await this._question.find({ + const questions = await this._formField.find({ where: { ParentSectionId: section.id, DeletedAt: null }, }); - sectionDto.Questions = questions.map((q) => QuestionMapper.toDto(q)); + sectionDto.Questions = questions.map((q) => FormFieldMapper.toDto(q)); const subSections = await mapSections(section.id); if (subSections.length > 0) { @@ -465,7 +466,7 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { return rootSections; }; - delete = async (id: string) : Promise=> { + delete = async (id: string): Promise => { const record = await this._formTemplateRepo.findOne({ where: { id: id, @@ -473,16 +474,16 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { }, }); - if (!record) { - return false; // Record not found - } - record.DeletedAt = new Date(); // Soft delete - await this._formTemplateRepo.save(record); + if (!record) { + return false; // Record not found + } + record.DeletedAt = new Date(); // Soft delete + await this._formTemplateRepo.save(record); return true; // Soft delete successful }; - submissions = async (id: string) : Promise=> { + submissions = async (id: string): Promise => { const response = await this._formTemplateRepo.find({ where: { id: id, diff --git a/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts b/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts index 61dd9e8..be9a02a 100644 --- a/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts +++ b/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts @@ -10,20 +10,22 @@ import { Logger } from "../../../../../common/logger"; import { ErrorHandler } from "../../../../../common/handlers/error.handler"; import { ResponseMapper } from "../../mappers/question.response.mapper"; import * as fs from 'fs'; -import { FindManyOptions } from "typeorm"; -import { QuestionMapper } from "../../mappers/question.mapper"; -import { Question } from "../../models/question/question.model"; +import { FindManyOptions, Repository } from "typeorm"; +// import { QuestionMapper } from "../../mappers/question.mapper"; +// import { Question } from "../../models/question/question.model"; import PDFDocument from 'pdfkit'; import { BaseRepo } from "../base.repo"; import path from "path"; import { createObjectCsvWriter } from "csv-writer"; import { QuestionResponseDto } from "../../../../../domain.types/forms/question.domain.types"; +import { FormFieldEntity } from "../../models/form.field/form.field.model"; +import { FormFieldMapper } from "../../mappers/form.field.mapper"; export class ResponseRepo extends BaseRepo implements IResponseRepo { - _responseRepo = Source.getRepository(QuestionResponse); + _responseRepo: Repository = Source.getRepository(QuestionResponse); - _questionRepo = Source.getRepository(Question); + _formFieldRepo: Repository = Source.getRepository(FormFieldEntity); create = async (model: QuestionResponseCreateModel): Promise => { try { @@ -159,13 +161,13 @@ export class ResponseRepo extends BaseRepo implements IResponseRepo { getQuestionById = async (id: string): Promise => { try { - var record = await this._questionRepo.findOne({ + var record = await this._formFieldRepo.findOne({ where: { id: id, DeletedAt: null, }, }); - return QuestionMapper.toDto(record); + return FormFieldMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); diff --git a/src/database/sql/typeorm/repositories/question/question.repo.ts b/src/database/sql/typeorm/repositories/question/question.repo.ts deleted file mode 100644 index e60b2a6..0000000 --- a/src/database/sql/typeorm/repositories/question/question.repo.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { QuestionCreateModel, QuestionOption } from "../../../../../domain.types/forms/question.domain.types"; -import { IQuestionRepo } from "../../../../repository.interfaces/question/question.repo.interface"; -import { QuestionResponseDto } from "../../../../../domain.types/forms/question.domain.types"; -import { QuestionUpdateModel } from "../../../../../domain.types/forms/question.domain.types"; -import { QuestionSearchFilters } from "../../../../../domain.types/forms/question.domain.types"; -import { Source } from "../../database.connector.typeorm"; -import { Question } from "../../models/question/question.model"; -import { QuestionMapper } from "../../mappers/question.mapper"; -import { Logger } from "../../../../../common/logger"; -import { ErrorHandler } from "../../../../../common/handlers/error.handler"; -import { FindManyOptions } from "typeorm"; -import { BaseRepo } from "../base.repo"; -import { QueryResponseType } from "../../../../../domain.types/forms/query.response.types"; - -export class QuestionRepo extends BaseRepo implements IQuestionRepo{ - - _questionRepo = Source.getRepository(Question); - - create=async (model: QuestionCreateModel) : Promise => { - try { - - // let jsonData: Prisma.JsonValue | undefined; - - let jsonData: QuestionOption[] | undefined; - - // Map model.Options to the appropriate structure for JSON storage - if (model.Options && model.Options.length > 0) { - jsonData = model.Options.map((option) => ({ - Text: option.Text, - Sequence: option.Sequence, - ImageUrl: option.ImageUrl, - })); - } - - const data = await this._questionRepo.create({ - Title: model.Title, - Description: model.Description, - TemplateId: model.ParentTemplateId, - ParentSectionId: model.ParentSectionId, - DisplayCode: model.DisplayCode, - ResponseType: model.ResponseType as QueryResponseType, - Score: model.Score, - // CorrectAnswer: model.CorrectAnswer, - IsRequired: model.IsRequired, - Hint: model.Hint, - Sequence: model.Sequence, - Options: JSON.stringify(jsonData), // Only assign if jsonData is defined - // QuestionImageUrl: model.QuestionImageUrl, - RangeMax: model.RangeMax, - RangeMin: model.RangeMin, - CreatedAt: new Date(), - // UpdatedAt: new Date(), // Uncomment and modify as needed - // DeletedAt: null, // Uncomment and modify as needed - }); - const record = await this._questionRepo.save(data); - return QuestionMapper.toDto(record); - } catch (error) { - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - update=async (id: string, model: QuestionUpdateModel) : Promise => { - try { - - // let jsonData: Prisma.JsonValue | undefined; - - let jsonData: QuestionOption[] | undefined; - - // Map model.Options to the appropriate structure for JSON storage - if (model.Options && model.Options.length > 0) { - jsonData = model.Options.map((option) => ({ - Text: option.Text, - Sequence: option.Sequence, - ImageUrl: option.ImageUrl, - })); - } - - - const updateData = await this._questionRepo.findOne({ - where: { - id: id, - DeletedAt: null, - }, - }); - if (!updateData) { - ErrorHandler.throwNotFoundError("Question Data not found!"); - } - // if (model.SectionIdentifier) { - // updateData.SectionIdentifier = model.SectionIdentifier; - // } - if (model.Title) { - updateData.Title = model.Title; - } - if (model.Description) { - updateData.Description = model.Description; - } - if (model.DisplayCode) { - updateData.DisplayCode = model.DisplayCode; - } - - if (model.Score) { - updateData.Score = model.Score; - } - - // if (model.CorrectAnswer) { - // updateData.CorrectAnswer = model.CorrectAnswer; - // } - - if (model.IsRequired) { - updateData.IsRequired = model.IsRequired; - } - - if (model.Hint) { - updateData.Hint = model.Hint; - } - - updateData.Options=JSON.stringify(jsonData); - - // if (model.QuestionImageUrl) { - // updateData.QuestionImageUrl = model.QuestionImageUrl; - // } - - if (model.RangeMax) { - updateData.RangeMax = model.RangeMax; - } - - if (model.RangeMin) { - updateData.RangeMin = model.RangeMin; - } - - updateData.UpdatedAt=new Date(); - - var record = await this._questionRepo.save(updateData); - return QuestionMapper.toDto(record); - } catch (error) { - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - getById=async (id: string) : Promise => { - try { - var record = await this._questionRepo.findOne({ - where: { - id: id, - DeletedAt: null, - }, - }); - return QuestionMapper.toDto(record); - } - catch (error) - { - Logger.instance().log(error.message); - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - getByTemplateId=async (id: string) : Promise => { - try { - var record = await this._questionRepo.findOne({ - where : { - TemplateId : id - }, - }); - return QuestionMapper.toDto(record); - } - catch (error) { - Logger.instance().log(error.message); - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - delete=async (id: string) : Promise => { - try { - var record = await this._questionRepo.findOne({ - where : { - id : id, - DeletedAt: null, - }, - }); - if (!record) { - return false; // Record not found - } - record.DeletedAt = new Date(); // Soft delete - await this._questionRepo.save(record); - return true; // Soft delete successful - } catch (error) { - Logger.instance().log(error.message); - ErrorHandler.throwInternalServerError(error.message, 500); - } - }; - - search=async (filters: QuestionSearchFilters) : Promise => { - try { - var search = this.getSearchModel(filters); - var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); - const [list, count] = await this._questionRepo.findAndCount(search); - - const searchResults = { - TotalCount : count, - RetrievedCount : list.length, - PageIndex : pageIndex, - ItemsPerPage : limit, - Order : order === 'DESC' ? 'descending' : 'ascending', - OrderedBy : orderByColumn, - Items : list.map(x => QuestionMapper.toDto(x)), - }; - - return searchResults; - } - - catch (error) { - Logger.instance().log(error.message); - ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); - } - }; - - private getSearchModel = (filters: QuestionSearchFilters) => { - - var search: FindManyOptions = { - relations: {}, - where: {}, - }; - - if (filters.id) { - search.where['id'] = filters.id - } - - if (filters.parentTemplateId) { - search.where['TemplateId'] = filters.parentTemplateId - } - - if (filters.parentSectionId) { - search.where['ParentSectionId'] = filters.parentSectionId - } - - if (filters.title) { - search.where['Title'] = filters.title - } - - if (filters.description) { - search.where['Description'] = filters.description - } - - if (filters.displayCode) { - search.where['DisplayCode'] = filters.displayCode - } - - if (filters.responseType) { - search.where['ResponseType'] = filters.responseType - } - - if (filters.score) { - search.where['Score'] = filters.score - } - if (filters.isRequired) { - search.where['IsRequired'] = filters.isRequired - } - if (filters.hint) { - search.where['Hint'] = filters.hint - } - - if (filters.questionImageUrl) { - search.where['QuestionImageUrl'] = filters.questionImageUrl - } - - if (filters.rangeMin) { - search.where['RangeMin'] = filters.rangeMin - } - - if (filters.rangeMax) { - search.where['RangeMax'] = filters.rangeMax - } - - if (filters.correctAnswer) { - search.where['CorrectAnswer'] = filters.correctAnswer - } - - return search; - }; -} \ No newline at end of file diff --git a/src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts b/src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts index 37595bc..1434354 100644 --- a/src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts +++ b/src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts @@ -9,6 +9,7 @@ import { BaseRepo } from "../base.repo"; import { FindManyOptions, Repository } from "typeorm"; export class TemplateFolderRepo extends BaseRepo implements ITemplateFolderRepo { + _templateFolderRepo: Repository = Source.getRepository(TemplateFolder); create = async (model: TemplateFolderCreateModel): Promise => { diff --git a/src/database/sql/typeorm/typeorm.injector.ts b/src/database/sql/typeorm/typeorm.injector.ts index d6f0b70..3a0faa5 100644 --- a/src/database/sql/typeorm/typeorm.injector.ts +++ b/src/database/sql/typeorm/typeorm.injector.ts @@ -4,13 +4,24 @@ import { DependencyContainer } from 'tsyringe'; import { FormSectionRepo } from './repositories/form.section/form.section.repo'; import { FormSubmissionRepo } from './repositories/form.submission/form.submission.repo'; import { FormTemplateRepo } from './repositories/form.template/form.template.repo'; -import { QuestionRepo } from './repositories/question/question.repo'; +import { FormFieldRepo } from './repositories/form.field/form.field.repo'; import { ResponseRepo } from './repositories/question.response/question.response.repo'; import { UserRepo } from './repositories/user/user.repo'; import { FavoriteTemplateRepo } from './repositories/favorite.template/favorite.template.repo'; import { FormTemplateApprovalRepo } from './repositories/form.template.approval/form.template.approval.repo'; import { InputUnitListRepo } from './repositories/input.unit.list/input.unit.list.repo'; import { TemplateFolderRepo } from './repositories/template.folder/template.folder.repo'; +import { SkipLogicRepo } from './repositories/field.logic/field.logic.skip.repo'; +import { CalculationLogicRepo } from './repositories/field.logic/field.logic.calculation.repo'; +import { ValidationLogicRepo } from './repositories/field.logic/field.logic.validation.repo'; +import { MathematicalOperationRepo } from './repositories/field.operations/field.operations.mathematical.repo'; +import { LogicalOperationRepo } from './repositories/field.operations/field.operations.logical.repo'; +import { CompositionOperationRepo } from './repositories/field.operations/field.operations.composition.repo'; +import { IterateOperationRepo } from './repositories/field.operations/field.operations.iterate.repo'; +import { FunctionExpressionOperationRepo } from './repositories/field.operations/field.operations.function.expression.repo'; +import { SkipRuleRepo } from './repositories/field.rules/field.rules.skip.repo'; +import { CalculationRuleRepo } from './repositories/field.rules/field.rules.calculation.repo'; +import { ValidationRuleRepo } from './repositories/field.rules/field.rules.validation.repo'; @@ -25,7 +36,7 @@ export class TypeOrmInjector { container.register('IFormSectionRepo', FormSectionRepo); container.register('IFormSubmissionRepo', FormSubmissionRepo); container.register('IFormTemplateRepo', FormTemplateRepo); - container.register('IQuestionRepo', QuestionRepo); + container.register('IFormFieldRepo', FormFieldRepo); container.register('IResponseRepo', ResponseRepo); container.register('IUserRepo', UserRepo); container.register('IFavoriteTemplateRepo', FavoriteTemplateRepo); @@ -33,6 +44,18 @@ export class TypeOrmInjector { container.register('IInputUnitListRepo', InputUnitListRepo); container.register('ITemplateFolderRepo', TemplateFolderRepo); + container.register('ISkipLogicRepo', SkipLogicRepo); + container.register('ICalculationLogicRepo', CalculationLogicRepo); + container.register('IValidationLogicRepo', ValidationLogicRepo); + container.register('IMathematicalOperationRepo', MathematicalOperationRepo); + container.register('ILogicalOperationRepo', LogicalOperationRepo); + container.register('ICompositionOperationRepo', CompositionOperationRepo); + container.register('IIterateOperationRepo', IterateOperationRepo); + container.register('IFunctionExpressionOperationRepo', FunctionExpressionOperationRepo); + container.register('ISkipRuleRepo', SkipRuleRepo); + container.register('ICalculationRuleRepo', CalculationRuleRepo); + container.register('IValidationRuleRepo', ValidationRuleRepo); + } } diff --git a/src/domain.types/forms/calculation.logic.domain.types.ts b/src/domain.types/forms/calculation.logic.domain.types.ts new file mode 100644 index 0000000..fd32d73 --- /dev/null +++ b/src/domain.types/forms/calculation.logic.domain.types.ts @@ -0,0 +1,25 @@ +import { BaseLogicCreateModel, BaseLogicResponseDto, BaseLogicUpdateModel, CalculationRuleResponseDto } from "./logic.domain.types"; +import { LogicType } from "./logic.enums"; + +// Calculation Logic DTOs +export interface CalculationLogicCreateModel extends BaseLogicCreateModel { + Type: LogicType.Calculation; + FieldId: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; + DefaultSkip?: boolean; + FallbackValue?: string; +} + +export interface CalculationLogicUpdateModel extends BaseLogicUpdateModel { + Type?: LogicType.Calculation; + FieldId?: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; + DefaultSkip?: boolean; + FallbackValue?: string; +} + +export interface CalculationLogicResponseDto extends BaseLogicResponseDto { + Type: LogicType.Calculation; + FallbackValue?: string; + Rules?: CalculationRuleResponseDto[]; +} \ No newline at end of file diff --git a/src/domain.types/forms/calculation.rule.domain.types.ts b/src/domain.types/forms/calculation.rule.domain.types.ts new file mode 100644 index 0000000..a0a8ed9 --- /dev/null +++ b/src/domain.types/forms/calculation.rule.domain.types.ts @@ -0,0 +1,48 @@ +import { BaseRuleCreateModel, BaseRuleResponseDto, BaseRuleUpdateModel } from "./rule.domain.types"; +import { BaseSearchFilters } from "../miscellaneous/base.search.types"; + +// Calculation Rule DTOs +export interface CalculationRuleCreateModel extends BaseRuleCreateModel { + ConditionForOperationId?: string; + OperationId: string; + LogicId?: string; +} + +export interface CalculationRuleUpdateModel extends BaseRuleUpdateModel { + ConditionForOperationId?: string; + OperationId?: string; + LogicId?: string; +} + +export interface CalculationRuleResponseDto extends BaseRuleResponseDto { + ConditionForOperationId?: string; + OperationId: string; + LogicId?: string; + ConditionForOperation?: { + id: string; + Name?: string; + Description?: string; + }; + Operation?: { + id: string; + Name?: string; + Description?: string; + }; + Logic?: { + id: string; + Type: string; + DefaultSkip?: boolean; + FallbackValue?: string; + }; +} + +// Calculation Rule Search DTOs +export interface CalculationRuleSearchFilters extends BaseSearchFilters { + id?: string; + name?: string; + description?: string; + priority?: number; + isActive?: boolean; + operationId?: string; + logicId?: string; +} \ No newline at end of file diff --git a/src/domain.types/forms/composition.operation.domain.types.ts b/src/domain.types/forms/composition.operation.domain.types.ts new file mode 100644 index 0000000..e56cf88 --- /dev/null +++ b/src/domain.types/forms/composition.operation.domain.types.ts @@ -0,0 +1,24 @@ +import { BaseOperationCreateModel, BaseOperationResponseDto, BaseOperationUpdateModel } from "./operation.domain.types"; +import { CompositionOperatorType } from "./operation.enums"; + +// Composition Operation DTOs +export interface CompositionOperationCreateModel extends BaseOperationCreateModel { + OperatorType: CompositionOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface CompositionOperationUpdateModel extends BaseOperationUpdateModel { + OperatorType?: CompositionOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface CompositionOperationResponseDto extends BaseOperationResponseDto { + OperatorType: CompositionOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} \ No newline at end of file diff --git a/src/domain.types/forms/field.enums.ts b/src/domain.types/forms/field.enums.ts new file mode 100644 index 0000000..399089b --- /dev/null +++ b/src/domain.types/forms/field.enums.ts @@ -0,0 +1,27 @@ +// Field Response Types +export enum FieldResponseType { + Text = 'Text', + Number = 'Number', + Boolean = 'Boolean', + Date = 'Date', + DateTime = 'DateTime', + Time = 'Time', + Email = 'Email', + Phone = 'Phone', + URL = 'URL', + Password = 'Password', + TextArea = 'TextArea', + Select = 'Select', + MultiSelect = 'MultiSelect', + Radio = 'Radio', + Checkbox = 'Checkbox', + File = 'File', + Image = 'Image', + Signature = 'Signature', + Location = 'Location', + Rating = 'Rating', + Slider = 'Slider', + Color = 'Color', + RichText = 'RichText', + JSON = 'JSON' +} \ No newline at end of file diff --git a/src/domain.types/forms/form.field.domain.types.ts b/src/domain.types/forms/form.field.domain.types.ts index f2e5b3d..b1e119e 100644 --- a/src/domain.types/forms/form.field.domain.types.ts +++ b/src/domain.types/forms/form.field.domain.types.ts @@ -1,165 +1,102 @@ -import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; -import { FieldResponseType } from "../../database/sql/typeorm/models/form.field/field.types"; import { QueryResponseType } from "./query.response.types"; +import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; -// Form Field Option Interface export interface FormFieldOption { Text: string; Sequence: string; ImageUrl: string; } -// Base Form Field DTOs export interface FormFieldCreateModel { - Name: string; - Label: string; + ParentTemplateId: string; + ParentSectionId: string; Title?: string; Description?: string; - DisplayCode?: string; - ResponseType: FieldResponseType; - QueryResponseType?: QueryResponseType; - Required: boolean; - Value?: string; + DisplayCode: string; + ResponseType: QueryResponseType; Score?: number; Sequence?: number; - ExpectedAnswer?: string; - Hint?: string; - Options?: string; // JSON serialized - ImageResourceId?: string; - RangeMin?: number; - RangeMax?: number; - DefaultExpectedUnit?: string; - PageBreakAfter?: boolean; - SkipLogicId?: string; - CalculateLogicId?: string; - ValidateLogicId?: string; - TemplateId?: string; - ParentSectionId?: string; - FormId?: string; + CorrectAnswer: string; + IsRequired?: boolean; + Hint: string; + Options?: FormFieldOption[]; + QuestionImageUrl: string; + RangeMin: number; + RangeMax: number | null; } export interface FormFieldUpdateModel { - Name?: string; - Label?: string; Title?: string; Description?: string; DisplayCode?: string; - ResponseType?: FieldResponseType; - QueryResponseType?: QueryResponseType; - Required?: boolean; - Value?: string; + ResponseType?: QueryResponseType; Score?: number; - Sequence?: number; - ExpectedAnswer?: string; + CorrectAnswer?: string; + IsRequired?: boolean; Hint?: string; - Options?: string; - ImageResourceId?: string; + Options?: FormFieldOption[]; + QuestionImageUrl?: string; RangeMin?: number; RangeMax?: number; - DefaultExpectedUnit?: string; - PageBreakAfter?: boolean; - SkipLogicId?: string; - CalculateLogicId?: string; - ValidateLogicId?: string; - TemplateId?: string; - ParentSectionId?: string; - FormId?: string; } export interface FormFieldResponseDto { id: string; - Name: string; - Label: string; - Title?: string; + Title: string; Description?: string; - DisplayCode?: string; - ResponseType: FieldResponseType; - QueryResponseType?: QueryResponseType; - Required: boolean; - Value?: string; - Score?: number; - Sequence?: number; - ExpectedAnswer?: string; - Hint?: string; - Options?: string; - ImageResourceId?: string; - RangeMin?: number; - RangeMax?: number; - DefaultExpectedUnit?: string; - PageBreakAfter: boolean; - SkipLogicId?: string; - CalculateLogicId?: string; - ValidateLogicId?: string; - TemplateId?: string; - ParentSectionId?: string; - FormId?: string; - SkipLogic?: { - id: string; - Type: string; - DefaultSkip?: boolean; - }; - CalculateLogic?: { - id: string; - Type: string; - DefaultSkip?: boolean; - }; - ValidateLogic?: { - id: string; - Type: string; - DefaultSkip?: boolean; - }; - FormTemplate?: { - id: string; - Title: string; - Description?: string; - DisplayCode: string; - }; + DisplayCode: string | null; + ResponseType: QueryResponseType; + Score: number; + Sequence: string; + CorrectAnswer: string; + IsRequired?: boolean; + Hint: string; + Options: FormFieldOption[]; + QuestionImageUrl: string; + RangeMin: number; + RangeMax: number | null; ParentFormSection?: { id: string; SectionIdentifier: string; Title: string; Description?: string; DisplayCode: string; + Sequence: string; + ParentSectionId: string; + CreatedAt: Date; }; - ExpectedInputUnitList?: { + ParentFormTemplate?: { id: string; - Name: string; + Title: string; Description?: string; - }; - Responses?: { - id: string; - ResponseValue: string; + CurrentVersion: string; + Type: string; + DisplayCode: string; + OwnerUserId: string; + RootSectionId: string; + DefaultSectionNumbering: string; CreatedAt: Date; - }[]; + }; CreatedAt: Date; UpdatedAt?: Date; } -// Form Field Search DTOs export interface FormFieldSearchFilters extends BaseSearchFilters { id?: string; - name?: string; - label?: string; + parentTemplateId?: string; + parentSectionId?: string; title?: string; description?: string; displayCode?: string; - responseType?: FieldResponseType; - queryResponseType?: QueryResponseType; - required?: boolean; + responseType?: QueryResponseType; score?: number; - sequence?: number; - expectedAnswer?: string; + correctAnswer?: string; + isRequired?: boolean; hint?: string; + options?: FormFieldOption[]; + questionImageUrl?: string; rangeMin?: number; - rangeMax?: number; - defaultExpectedUnit?: string; - pageBreakAfter?: boolean; - skipLogicId?: string; - calculateLogicId?: string; - validateLogicId?: string; - templateId?: string; - parentSectionId?: string; - formId?: string; + rangeMax?: number | null; } export interface FormFieldSearchResults extends BaseSearchResults { @@ -168,31 +105,41 @@ export interface FormFieldSearchResults extends BaseSearchResults { export interface FormFieldSearchResponseDto extends BaseSearchResults { id: string; - Name: string; - Label: string; - Title?: string; - Description?: string; + Title: string; + Description: string; DisplayCode?: string; - ResponseType: FieldResponseType; - QueryResponseType?: QueryResponseType; - Required: boolean; - Value?: string; - Score?: number; - Sequence?: number; - ExpectedAnswer?: string; - Hint?: string; - Options?: string; - ImageResourceId?: string; - RangeMin?: number; - RangeMax?: number; - DefaultExpectedUnit?: string; - PageBreakAfter: boolean; - SkipLogicId?: string; - CalculateLogicId?: string; - ValidateLogicId?: string; - TemplateId?: string; - ParentSectionId?: string; - FormId?: string; + ResponseType: QueryResponseType; + Score: number; + Sequence: string; + CorrectAnswer: string; + IsRequired?: boolean; + Hint: string; + Options: FormFieldOption; + QuestionImageUrl: string; + RangeMin: number; + RangeMax: number; + ParentFormSection: { + id: string; + SectionIdentifier: string; + Title: string; + Description: string; + DisplayCode: string; + Sequence: number; + ParentSectionId: string; + CreatedAt: Date; + }; + ParentFormTemplate: { + id: string; + Title: string; + Description: string; + CurrentVersion: number; + Type: string; + DisplayCode: string; + OwnerUserId: string; + RootSectionId: string; + DefaultSectionNumbering: boolean; + CreatedAt: Date; + }; CreatedAt: Date; - UpdatedAt?: Date; + UpdatedAt: Date; } \ No newline at end of file diff --git a/src/domain.types/forms/form.submission.domain.types.ts b/src/domain.types/forms/form.submission.domain.types.ts index 5b548a3..b38cfd0 100644 --- a/src/domain.types/forms/form.submission.domain.types.ts +++ b/src/domain.types/forms/form.submission.domain.types.ts @@ -1,5 +1,6 @@ import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; -import { FormStatus } from "../../database/sql/typeorm/models/form.submission/form.submission.model"; +import { FormStatus } from "./form.submission.enums"; +import { FormType } from "./form.template.enums"; export interface FormSubmissionCreateModel { FormTemplateId: string; @@ -88,16 +89,4 @@ export interface FormSubmissionSearchResponseDto extends BaseSearchResults{ UpdatedAt: Date; }; -// export enum FormStatus { -// LinkShared = "LinkShared", -// Presented = "Presented", -// InProgress = "InProgress", -// Submitted = "Submitted", -// } - export enum FormType { - Survey = "Survey", - Questionnaire = "Questionnaire", - TestPaper = "TestPaper", - DataCollection = "DataCollection" - } diff --git a/src/domain.types/forms/form.submission.enums.ts b/src/domain.types/forms/form.submission.enums.ts new file mode 100644 index 0000000..24293e0 --- /dev/null +++ b/src/domain.types/forms/form.submission.enums.ts @@ -0,0 +1,8 @@ +// Form Submission Enums +export enum FormStatus { + LinkShared = 'LinkShared', + Saved = 'Saved', + InProgress='InProgress', + LinkExpired = 'LinkExpired', + Submitted = 'Submitted' +} \ No newline at end of file diff --git a/src/domain.types/forms/form.template.domain.types.ts b/src/domain.types/forms/form.template.domain.types.ts index a0e0cfb..64a709f 100644 --- a/src/domain.types/forms/form.template.domain.types.ts +++ b/src/domain.types/forms/form.template.domain.types.ts @@ -1,7 +1,7 @@ // import { FormType } from "../miscellaneous/system.types"; // import { FormType, ItemsPerPage, QueryResponseType } from "@prisma/client"; -import { FormType } from "../../database/sql/typeorm/models/form.template/form.template.model"; +import { FormType } from "./form.template.enums"; import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; import { uuid } from "../miscellaneous/system.types"; import { QuestionOption } from "./question.domain.types"; diff --git a/src/domain.types/forms/form.template.enums.ts b/src/domain.types/forms/form.template.enums.ts new file mode 100644 index 0000000..b2dc52c --- /dev/null +++ b/src/domain.types/forms/form.template.enums.ts @@ -0,0 +1,14 @@ +// Form Template Enums +export enum FormType { + Survey = 'Survey', + Questionnaire = 'Questionnaire', + TestPaper = 'TestPaper', + DataCollection = 'DataCollection' +} + +export enum NavigationStrategy { + OneQuestion = 'OneQuestion', + OneSection = 'OneSection', + AllAtOnce = 'AllAtOnce', + Paginated = 'Paginated' +} \ No newline at end of file diff --git a/src/domain.types/forms/function.expression.operation.domain.types.ts b/src/domain.types/forms/function.expression.operation.domain.types.ts new file mode 100644 index 0000000..70aa5f0 --- /dev/null +++ b/src/domain.types/forms/function.expression.operation.domain.types.ts @@ -0,0 +1,20 @@ +import { BaseOperationCreateModel, BaseOperationResponseDto, BaseOperationUpdateModel } from "./operation.domain.types"; + +// Function Expression Operation DTOs +export interface FunctionExpressionOperationCreateModel extends BaseOperationCreateModel { + FunctionName?: string; + Parameters?: Record; + Expression?: string; +} + +export interface FunctionExpressionOperationUpdateModel extends BaseOperationUpdateModel { + FunctionName?: string; + Parameters?: Record; + Expression?: string; +} + +export interface FunctionExpressionOperationResponseDto extends BaseOperationResponseDto { + FunctionName?: string; + Parameters?: Record; + Expression?: string; +} \ No newline at end of file diff --git a/src/domain.types/forms/iterate.operation.domain.types.ts b/src/domain.types/forms/iterate.operation.domain.types.ts new file mode 100644 index 0000000..ce3bee3 --- /dev/null +++ b/src/domain.types/forms/iterate.operation.domain.types.ts @@ -0,0 +1,25 @@ +import { BaseOperationCreateModel, BaseOperationResponseDto, BaseOperationUpdateModel } from "./operation.domain.types"; + +// Iterate Operation DTOs +export interface IterateOperationCreateModel extends BaseOperationCreateModel { + ArrayOperand: string; + ItemAlias: string; + OperationId: string; +} + +export interface IterateOperationUpdateModel extends BaseOperationUpdateModel { + ArrayOperand?: string; + ItemAlias?: string; + OperationId?: string; +} + +export interface IterateOperationResponseDto extends BaseOperationResponseDto { + ArrayOperand: string; + ItemAlias: string; + OperationId: string; + Operation?: { + id: string; + Name?: string; + Description?: string; + }; +} \ No newline at end of file diff --git a/src/domain.types/forms/logic.domain.types.ts b/src/domain.types/forms/logic.domain.types.ts index 0873389..a151ce1 100644 --- a/src/domain.types/forms/logic.domain.types.ts +++ b/src/domain.types/forms/logic.domain.types.ts @@ -1,20 +1,26 @@ import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; -import { LogicType } from "../../database/sql/typeorm/models/logic/logic.types"; +import { LogicType } from "./logic.enums"; // Base Logic DTOs export interface BaseLogicCreateModel { Type: LogicType; + FieldId: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; DefaultSkip?: boolean; } export interface BaseLogicUpdateModel { Type?: LogicType; + FieldId?: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; DefaultSkip?: boolean; } export interface BaseLogicResponseDto { id: string; Type: LogicType; + FieldId: string; // UUID foreign key to FormFieldEntity + Enabled: boolean; DefaultSkip?: boolean; CreatedAt: Date; UpdatedAt?: Date; @@ -23,11 +29,15 @@ export interface BaseLogicResponseDto { // Skip Logic DTOs export interface SkipLogicCreateModel extends BaseLogicCreateModel { Type: LogicType.Skip; + FieldId: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; DefaultSkip?: boolean; } export interface SkipLogicUpdateModel extends BaseLogicUpdateModel { Type?: LogicType.Skip; + FieldId?: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; DefaultSkip?: boolean; } @@ -39,12 +49,16 @@ export interface SkipLogicResponseDto extends BaseLogicResponseDto { // Calculation Logic DTOs export interface CalculationLogicCreateModel extends BaseLogicCreateModel { Type: LogicType.Calculation; + FieldId: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; DefaultSkip?: boolean; FallbackValue?: string; } export interface CalculationLogicUpdateModel extends BaseLogicUpdateModel { Type?: LogicType.Calculation; + FieldId?: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; DefaultSkip?: boolean; FallbackValue?: string; } @@ -58,39 +72,30 @@ export interface CalculationLogicResponseDto extends BaseLogicResponseDto { // Validation Logic DTOs export interface ValidationLogicCreateModel extends BaseLogicCreateModel { Type: LogicType.Validation; + FieldId: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; DefaultSkip?: boolean; } export interface ValidationLogicUpdateModel extends BaseLogicUpdateModel { Type?: LogicType.Validation; + FieldId?: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; DefaultSkip?: boolean; } export interface ValidationLogicResponseDto extends BaseLogicResponseDto { Type: LogicType.Validation; + Rules?: ValidationRuleResponseDto[]; } -// Legacy Logic DTOs -export interface LegacyLogicCreateModel extends BaseLogicCreateModel { - Type: LogicType; - DefaultSkip?: boolean; -} - -export interface LegacyLogicUpdateModel extends BaseLogicUpdateModel { - Type?: LogicType; - DefaultSkip?: boolean; -} - -export interface LegacyLogicResponseDto extends BaseLogicResponseDto { - Type: LogicType; - Rules?: LegacyRuleResponseDto[]; -} - // Logic Search DTOs export interface LogicSearchFilters extends BaseSearchFilters { id?: string; type?: LogicType; + fieldId?: string; // UUID foreign key to FormFieldEntity + enabled?: boolean; defaultSkip?: boolean; } @@ -101,6 +106,8 @@ export interface LogicSearchResults extends BaseSearchResults { export interface LogicSearchResponseDto extends BaseSearchResults { id: string; Type: LogicType; + FieldId: string; // UUID foreign key to FormFieldEntity + Enabled: boolean; DefaultSkip?: boolean; CreatedAt: Date; UpdatedAt?: Date; @@ -130,12 +137,4 @@ export interface ValidationRuleResponseDto { LogicId?: string; CreatedAt: Date; UpdatedAt?: Date; -} - -export interface LegacyRuleResponseDto { - id: string; - OperationId: string; - LogicId?: string; - CreatedAt: Date; - UpdatedAt?: Date; } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/logic/logic.types.ts b/src/domain.types/forms/logic.enums.ts similarity index 87% rename from src/database/sql/typeorm/models/logic/logic.types.ts rename to src/domain.types/forms/logic.enums.ts index 0b68feb..6b5d497 100644 --- a/src/database/sql/typeorm/models/logic/logic.types.ts +++ b/src/domain.types/forms/logic.enums.ts @@ -1,4 +1,4 @@ -// Logic Types +// Logic Enums export enum LogicType { Skip = 'Skip', Calculation = 'Calculation', diff --git a/src/domain.types/forms/logical.operation.domain.types.ts b/src/domain.types/forms/logical.operation.domain.types.ts new file mode 100644 index 0000000..e33a97b --- /dev/null +++ b/src/domain.types/forms/logical.operation.domain.types.ts @@ -0,0 +1,24 @@ +import { BaseOperationCreateModel, BaseOperationResponseDto, BaseOperationUpdateModel } from "./operation.domain.types"; +import { LogicalOperatorType } from "./operation.enums"; + +// Logical Operation DTOs +export interface LogicalOperationCreateModel extends BaseOperationCreateModel { + OperatorType: LogicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface LogicalOperationUpdateModel extends BaseOperationUpdateModel { + OperatorType?: LogicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface LogicalOperationResponseDto extends BaseOperationResponseDto { + OperatorType: LogicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} \ No newline at end of file diff --git a/src/domain.types/forms/mathematical.operation.domain.types.ts b/src/domain.types/forms/mathematical.operation.domain.types.ts new file mode 100644 index 0000000..34ab556 --- /dev/null +++ b/src/domain.types/forms/mathematical.operation.domain.types.ts @@ -0,0 +1,24 @@ +import { BaseOperationCreateModel, BaseOperationResponseDto, BaseOperationUpdateModel } from "./operation.domain.types"; +import { MathematicalOperatorType } from "./operation.enums"; + +// Mathematical Operation DTOs +export interface MathematicalOperationCreateModel extends BaseOperationCreateModel { + OperatorType: MathematicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface MathematicalOperationUpdateModel extends BaseOperationUpdateModel { + OperatorType?: MathematicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} + +export interface MathematicalOperationResponseDto extends BaseOperationResponseDto { + OperatorType: MathematicalOperatorType; + LeftOperand?: string; + RightOperand?: string; + Parameters?: Record; +} \ No newline at end of file diff --git a/src/domain.types/forms/operation.domain.types.ts b/src/domain.types/forms/operation.domain.types.ts index fc511b5..f5f78ba 100644 --- a/src/domain.types/forms/operation.domain.types.ts +++ b/src/domain.types/forms/operation.domain.types.ts @@ -4,110 +4,101 @@ import { LogicalOperatorType, MathematicalOperatorType, CompositionOperatorType -} from "../../database/sql/typeorm/models/operation/operation.types"; +} from "./operation.enums"; // Base Operation DTOs export interface BaseOperationCreateModel { Name?: string; Description?: string; + Type: OperationType; } export interface BaseOperationUpdateModel { Name?: string; Description?: string; + Type?: OperationType; } export interface BaseOperationResponseDto { id: string; Name?: string; Description?: string; + Type: OperationType; CreatedAt: Date; UpdatedAt?: Date; } // Logical Operation DTOs export interface LogicalOperationCreateModel extends BaseOperationCreateModel { - OperatorType: LogicalOperatorType; - LeftOperand?: string; - RightOperand?: string; - Parameters?: Record; + Operator: LogicalOperatorType; + Operands: string; // JSON serialized Operand[] } export interface LogicalOperationUpdateModel extends BaseOperationUpdateModel { - OperatorType?: LogicalOperatorType; - LeftOperand?: string; - RightOperand?: string; - Parameters?: Record; + Operator?: LogicalOperatorType; + Operands?: string; // JSON serialized Operand[] } export interface LogicalOperationResponseDto extends BaseOperationResponseDto { - OperatorType: LogicalOperatorType; - LeftOperand?: string; - RightOperand?: string; - Parameters?: Record; + Operator: LogicalOperatorType; + Operands: string; // JSON serialized Operand[] } // Mathematical Operation DTOs export interface MathematicalOperationCreateModel extends BaseOperationCreateModel { - OperatorType: MathematicalOperatorType; - LeftOperand?: string; - RightOperand?: string; - Parameters?: Record; + Operator: MathematicalOperatorType; + Operands: string; // JSON serialized Operand[] + ResultDataType: string; } export interface MathematicalOperationUpdateModel extends BaseOperationUpdateModel { - OperatorType?: MathematicalOperatorType; - LeftOperand?: string; - RightOperand?: string; - Parameters?: Record; + Operator?: MathematicalOperatorType; + Operands?: string; // JSON serialized Operand[] + ResultDataType?: string; } export interface MathematicalOperationResponseDto extends BaseOperationResponseDto { - OperatorType: MathematicalOperatorType; - LeftOperand?: string; - RightOperand?: string; - Parameters?: Record; + Operator: MathematicalOperatorType; + Operands: string; // JSON serialized Operand[] + ResultDataType: string; } // Composition Operation DTOs export interface CompositionOperationCreateModel extends BaseOperationCreateModel { - OperatorType: CompositionOperatorType; - LeftOperand?: string; - RightOperand?: string; - Parameters?: Record; + Operator: CompositionOperatorType; + Operands: string; // JSON serialized Operand[] } export interface CompositionOperationUpdateModel extends BaseOperationUpdateModel { - OperatorType?: CompositionOperatorType; - LeftOperand?: string; - RightOperand?: string; - Parameters?: Record; + Operator?: CompositionOperatorType; + Operands?: string; // JSON serialized Operand[] } export interface CompositionOperationResponseDto extends BaseOperationResponseDto { - OperatorType: CompositionOperatorType; - LeftOperand?: string; - RightOperand?: string; - Parameters?: Record; + Operator: CompositionOperatorType; + Operands: string; // JSON serialized Operand[] } // Iterate Operation DTOs export interface IterateOperationCreateModel extends BaseOperationCreateModel { - ArrayOperand: string; - ItemAlias: string; + CollectionField: string; // Field name to iterate over + ResultField: string; // Field name to store result OperationId: string; + FilterExpression?: string; // Optional filter expression } export interface IterateOperationUpdateModel extends BaseOperationUpdateModel { - ArrayOperand?: string; - ItemAlias?: string; + CollectionField?: string; // Field name to iterate over + ResultField?: string; // Field name to store result OperationId?: string; + FilterExpression?: string; // Optional filter expression } export interface IterateOperationResponseDto extends BaseOperationResponseDto { - ArrayOperand: string; - ItemAlias: string; + CollectionField: string; + ResultField: string; OperationId: string; + FilterExpression?: string; Operation?: { id: string; Name?: string; @@ -117,21 +108,21 @@ export interface IterateOperationResponseDto extends BaseOperationResponseDto { // Function Expression Operation DTOs export interface FunctionExpressionOperationCreateModel extends BaseOperationCreateModel { - FunctionName?: string; - Parameters?: Record; - Expression?: string; + Expression: string; + Variables: string; // JSON serialized Record + ResultDataType: string; } export interface FunctionExpressionOperationUpdateModel extends BaseOperationUpdateModel { - FunctionName?: string; - Parameters?: Record; Expression?: string; + Variables?: string; // JSON serialized Record + ResultDataType?: string; } export interface FunctionExpressionOperationResponseDto extends BaseOperationResponseDto { - FunctionName?: string; - Parameters?: Record; - Expression?: string; + Expression: string; + Variables: string; // JSON serialized Record + ResultDataType: string; } // Operation Search DTOs @@ -139,7 +130,7 @@ export interface OperationSearchFilters extends BaseSearchFilters { id?: string; name?: string; description?: string; - operatorType?: LogicalOperatorType | MathematicalOperatorType | CompositionOperatorType; + operator?: LogicalOperatorType | MathematicalOperatorType | CompositionOperatorType; } export interface OperationSearchResults extends BaseSearchResults { diff --git a/src/database/sql/typeorm/models/operation/operation.types.ts b/src/domain.types/forms/operation.enums.ts similarity index 98% rename from src/database/sql/typeorm/models/operation/operation.types.ts rename to src/domain.types/forms/operation.enums.ts index cfa4f05..87d504d 100644 --- a/src/database/sql/typeorm/models/operation/operation.types.ts +++ b/src/domain.types/forms/operation.enums.ts @@ -1,4 +1,4 @@ -// Operation Types +// Operation Enums export enum OperationType { Logical = 'Logical', Mathematical = 'Mathematical', diff --git a/src/domain.types/forms/response.domain.types.ts b/src/domain.types/forms/response.domain.types.ts index 82abd36..6a4b872 100644 --- a/src/domain.types/forms/response.domain.types.ts +++ b/src/domain.types/forms/response.domain.types.ts @@ -1,6 +1,6 @@ // import { FormStatus, QueryResponseType } from "../miscellaneous/system.types" -import { FormStatus } from "../../database/sql/typeorm/models/form.submission/form.submission.model"; +import { FormStatus } from "./form.submission.enums"; import { QueryResponseType } from "./query.response.types"; import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; diff --git a/src/domain.types/forms/rule.domain.types.ts b/src/domain.types/forms/rule.domain.types.ts index bacfd9a..f0b0c89 100644 --- a/src/domain.types/forms/rule.domain.types.ts +++ b/src/domain.types/forms/rule.domain.types.ts @@ -121,31 +121,7 @@ export interface ValidationRuleResponseDto extends BaseRuleResponseDto { }; } -// Legacy Rule DTOs -export interface LegacyRuleCreateModel extends BaseRuleCreateModel { - OperationId: string; - LogicId?: string; -} - -export interface LegacyRuleUpdateModel extends BaseRuleUpdateModel { - OperationId?: string; - LogicId?: string; -} -export interface LegacyRuleResponseDto extends BaseRuleResponseDto { - OperationId: string; - LogicId?: string; - Operation?: { - id: string; - Name?: string; - Description?: string; - }; - Logic?: { - id: string; - Type: string; - DefaultSkip?: boolean; - }; -} // Rule Search DTOs export interface RuleSearchFilters extends BaseSearchFilters { diff --git a/src/domain.types/forms/skip.logic.domain.types.ts b/src/domain.types/forms/skip.logic.domain.types.ts new file mode 100644 index 0000000..097c257 --- /dev/null +++ b/src/domain.types/forms/skip.logic.domain.types.ts @@ -0,0 +1,22 @@ +import { BaseLogicCreateModel, BaseLogicResponseDto, BaseLogicUpdateModel, SkipRuleResponseDto } from "./logic.domain.types"; +import { LogicType } from "./logic.enums"; + +// Skip Logic DTOs +export interface SkipLogicCreateModel extends BaseLogicCreateModel { + Type: LogicType.Skip; + FieldId: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; + DefaultSkip?: boolean; +} + +export interface SkipLogicUpdateModel extends BaseLogicUpdateModel { + Type?: LogicType.Skip; + FieldId?: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; + DefaultSkip?: boolean; +} + +export interface SkipLogicResponseDto extends BaseLogicResponseDto { + Type: LogicType.Skip; + Rules?: SkipRuleResponseDto[]; +} \ No newline at end of file diff --git a/src/domain.types/forms/skip.rule.domain.types.ts b/src/domain.types/forms/skip.rule.domain.types.ts new file mode 100644 index 0000000..3bea0a2 --- /dev/null +++ b/src/domain.types/forms/skip.rule.domain.types.ts @@ -0,0 +1,42 @@ +import { BaseRuleCreateModel, BaseRuleResponseDto, BaseRuleUpdateModel } from "./rule.domain.types"; +import { BaseSearchFilters } from "../miscellaneous/base.search.types"; + +// Skip Rule DTOs +export interface SkipRuleCreateModel extends BaseRuleCreateModel { + OperationId: string; + SkipWhenTrue: boolean; + LogicId?: string; +} + +export interface SkipRuleUpdateModel extends BaseRuleUpdateModel { + OperationId?: string; + SkipWhenTrue?: boolean; + LogicId?: string; +} + +export interface SkipRuleResponseDto extends BaseRuleResponseDto { + OperationId: string; + SkipWhenTrue: boolean; + LogicId?: string; + Operation?: { + id: string; + Name?: string; + Description?: string; + }; + Logic?: { + id: string; + Type: string; + DefaultSkip?: boolean; + }; +} + +// Skip Rule Search DTOs +export interface SkipRuleSearchFilters extends BaseSearchFilters { + id?: string; + name?: string; + description?: string; + priority?: number; + isActive?: boolean; + operationId?: string; + logicId?: string; +} \ No newline at end of file diff --git a/src/domain.types/forms/validation.logic.domain.types.ts b/src/domain.types/forms/validation.logic.domain.types.ts new file mode 100644 index 0000000..4267f97 --- /dev/null +++ b/src/domain.types/forms/validation.logic.domain.types.ts @@ -0,0 +1,22 @@ +import { BaseLogicCreateModel, BaseLogicResponseDto, BaseLogicUpdateModel, ValidationRuleResponseDto } from "./logic.domain.types"; +import { LogicType } from "./logic.enums"; + +// Validation Logic DTOs +export interface ValidationLogicCreateModel extends BaseLogicCreateModel { + Type: LogicType.Validation; + FieldId: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; + DefaultSkip?: boolean; +} + +export interface ValidationLogicUpdateModel extends BaseLogicUpdateModel { + Type?: LogicType.Validation; + FieldId?: string; // UUID foreign key to FormFieldEntity + Enabled?: boolean; + DefaultSkip?: boolean; +} + +export interface ValidationLogicResponseDto extends BaseLogicResponseDto { + Type: LogicType.Validation; + Rules?: ValidationRuleResponseDto[]; +} \ No newline at end of file diff --git a/src/domain.types/forms/validation.rule.domain.types.ts b/src/domain.types/forms/validation.rule.domain.types.ts new file mode 100644 index 0000000..b3d993e --- /dev/null +++ b/src/domain.types/forms/validation.rule.domain.types.ts @@ -0,0 +1,45 @@ +import { BaseRuleCreateModel, BaseRuleResponseDto, BaseRuleUpdateModel } from "./rule.domain.types"; +import { BaseSearchFilters } from "../miscellaneous/base.search.types"; + +// Validation Rule DTOs +export interface ValidationRuleCreateModel extends BaseRuleCreateModel { + OperationId: string; + ErrorWhenFalse: boolean; + ErrorMessage: string; + LogicId?: string; +} + +export interface ValidationRuleUpdateModel extends BaseRuleUpdateModel { + OperationId?: string; + ErrorWhenFalse?: boolean; + ErrorMessage?: string; + LogicId?: string; +} + +export interface ValidationRuleResponseDto extends BaseRuleResponseDto { + OperationId: string; + ErrorWhenFalse: boolean; + ErrorMessage: string; + LogicId?: string; + Operation?: { + id: string; + Name?: string; + Description?: string; + }; + Logic?: { + id: string; + Type: string; + DefaultSkip?: boolean; + }; +} + +// Validation Rule Search DTOs +export interface ValidationRuleSearchFilters extends BaseSearchFilters { + id?: string; + name?: string; + description?: string; + priority?: number; + isActive?: boolean; + operationId?: string; + logicId?: string; +} \ No newline at end of file diff --git a/src/domain.types/miscellaneous/base.search.types.ts b/src/domain.types/miscellaneous/base.search.types.ts index f6b7326..374b8ec 100644 --- a/src/domain.types/miscellaneous/base.search.types.ts +++ b/src/domain.types/miscellaneous/base.search.types.ts @@ -19,5 +19,5 @@ export interface BaseSearchResults { ItemsPerPage : integer; Order : string; OrderedBy : string; - // Items: string[]; + Items: any[]; } diff --git a/src/services/field.logic/calculation.logic.service.ts b/src/services/field.logic/calculation.logic.service.ts new file mode 100644 index 0000000..7e5ed94 --- /dev/null +++ b/src/services/field.logic/calculation.logic.service.ts @@ -0,0 +1,34 @@ +import { inject, injectable } from "tsyringe"; +import { ICalculationLogicRepo } from "../../database/repository.interfaces/field.logic/calculation.logic/calculation.logic.repo.interface"; +import { + CalculationLogicResponseDto, + CalculationLogicCreateModel, + CalculationLogicUpdateModel, + LogicSearchFilters +} from "../../domain.types/forms/logic.domain.types"; + +@injectable() +export class CalculationLogicService { + constructor(@inject('ICalculationLogicRepo') private _calculationLogicRepo: ICalculationLogicRepo) { } + + // Calculation Logic operations + async createCalculationLogic(model: CalculationLogicCreateModel): Promise { + return await this._calculationLogicRepo.createCalculationLogic(model); + } + + async updateCalculationLogic(id: string, model: CalculationLogicUpdateModel): Promise { + return await this._calculationLogicRepo.updateCalculationLogic(id, model); + } + + async getCalculationLogicById(id: string): Promise { + return await this._calculationLogicRepo.getCalculationLogicById(id); + } + + async deleteCalculationLogic(id: string): Promise { + return await this._calculationLogicRepo.deleteCalculationLogic(id); + } + + async searchCalculationLogic(filters: LogicSearchFilters): Promise { + return await this._calculationLogicRepo.searchCalculationLogic(filters); + } +} \ No newline at end of file diff --git a/src/services/field.logic/skip.logic.service.ts b/src/services/field.logic/skip.logic.service.ts new file mode 100644 index 0000000..7a646ee --- /dev/null +++ b/src/services/field.logic/skip.logic.service.ts @@ -0,0 +1,34 @@ +import { inject, injectable } from "tsyringe"; +import { ISkipLogicRepo } from "../../database/repository.interfaces/field.logic/skip.logic/skip.logic.repo.interface"; +import { + SkipLogicResponseDto, + SkipLogicCreateModel, + SkipLogicUpdateModel, + LogicSearchFilters +} from "../../domain.types/forms/logic.domain.types"; + +@injectable() +export class SkipLogicService { + constructor(@inject('ISkipLogicRepo') private _skipLogicRepo: ISkipLogicRepo) { } + + // Skip Logic operations + async createSkipLogic(model: SkipLogicCreateModel): Promise { + return await this._skipLogicRepo.createSkipLogic(model); + } + + async updateSkipLogic(id: string, model: SkipLogicUpdateModel): Promise { + return await this._skipLogicRepo.updateSkipLogic(id, model); + } + + async getSkipLogicById(id: string): Promise { + return await this._skipLogicRepo.getSkipLogicById(id); + } + + async deleteSkipLogic(id: string): Promise { + return await this._skipLogicRepo.deleteSkipLogic(id); + } + + async searchSkipLogic(filters: LogicSearchFilters): Promise { + return await this._skipLogicRepo.searchSkipLogic(filters); + } +} \ No newline at end of file diff --git a/src/services/field.logic/validation.logic.service.ts b/src/services/field.logic/validation.logic.service.ts new file mode 100644 index 0000000..4f593ec --- /dev/null +++ b/src/services/field.logic/validation.logic.service.ts @@ -0,0 +1,34 @@ +import { inject, injectable } from "tsyringe"; +import { IValidationLogicRepo } from "../../database/repository.interfaces/field.logic/validation.logic/validation.logic.repo.interface"; +import { + ValidationLogicResponseDto, + ValidationLogicCreateModel, + ValidationLogicUpdateModel, + LogicSearchFilters +} from "../../domain.types/forms/logic.domain.types"; + +@injectable() +export class ValidationLogicService { + constructor(@inject('IValidationLogicRepo') private _validationLogicRepo: IValidationLogicRepo) { } + + // Validation Logic operations + async createValidationLogic(model: ValidationLogicCreateModel): Promise { + return await this._validationLogicRepo.createValidationLogic(model); + } + + async updateValidationLogic(id: string, model: ValidationLogicUpdateModel): Promise { + return await this._validationLogicRepo.updateValidationLogic(id, model); + } + + async getValidationLogicById(id: string): Promise { + return await this._validationLogicRepo.getValidationLogicById(id); + } + + async deleteValidationLogic(id: string): Promise { + return await this._validationLogicRepo.deleteValidationLogic(id); + } + + async searchValidationLogic(filters: LogicSearchFilters): Promise { + return await this._validationLogicRepo.searchValidationLogic(filters); + } +} \ No newline at end of file diff --git a/src/services/field.operations/composition.operation.service.ts b/src/services/field.operations/composition.operation.service.ts new file mode 100644 index 0000000..76b1eed --- /dev/null +++ b/src/services/field.operations/composition.operation.service.ts @@ -0,0 +1,35 @@ +import { inject, injectable } from "tsyringe"; +// import { ICompositionOperationRepo } from "../../database/repository.interfaces/field.operations/composition.operation/composition.operation.repo.interface"; +import { + CompositionOperationResponseDto, + CompositionOperationCreateModel, + CompositionOperationUpdateModel, + OperationSearchFilters +} from "../../domain.types/forms/operation.domain.types"; +import { ICompositionOperationRepo } from "../../database/repository.interfaces/field.operations/composition.operation/composition.operation.repo.interface"; + +@injectable() +export class CompositionOperationService { + constructor(@inject('ICompositionOperationRepo') private _compositionOperationRepo: ICompositionOperationRepo) { } + + // Composition Operation operations + async createCompositionOperation(model: CompositionOperationCreateModel): Promise { + return await this._compositionOperationRepo.createCompositionOperation(model); + } + + async updateCompositionOperation(id: string, model: CompositionOperationUpdateModel): Promise { + return await this._compositionOperationRepo.updateCompositionOperation(id, model); + } + + async getCompositionOperationById(id: string): Promise { + return await this._compositionOperationRepo.getCompositionOperationById(id); + } + + async deleteCompositionOperation(id: string): Promise { + return await this._compositionOperationRepo.deleteCompositionOperation(id); + } + + async searchCompositionOperation(filters: OperationSearchFilters): Promise { + return await this._compositionOperationRepo.searchCompositionOperation(filters); + } +} \ No newline at end of file diff --git a/src/services/field.operations/function.expression.operation.service.ts b/src/services/field.operations/function.expression.operation.service.ts new file mode 100644 index 0000000..548ced6 --- /dev/null +++ b/src/services/field.operations/function.expression.operation.service.ts @@ -0,0 +1,34 @@ +import { inject, injectable } from "tsyringe"; +import { IFunctionExpressionOperationRepo } from "../../database/repository.interfaces/field.operations/function.expression.operation/function.expression.operation.repo.interface"; +import { + FunctionExpressionOperationResponseDto, + FunctionExpressionOperationCreateModel, + FunctionExpressionOperationUpdateModel, + OperationSearchFilters +} from "../../domain.types/forms/operation.domain.types"; + +@injectable() +export class FunctionExpressionOperationService { + constructor(@inject('IFunctionExpressionOperationRepo') private _functionExpressionOperationRepo: IFunctionExpressionOperationRepo) { } + + // Function Expression Operation operations + async createFunctionExpressionOperation(model: FunctionExpressionOperationCreateModel): Promise { + return await this._functionExpressionOperationRepo.createFunctionExpressionOperation(model); + } + + async updateFunctionExpressionOperation(id: string, model: FunctionExpressionOperationUpdateModel): Promise { + return await this._functionExpressionOperationRepo.updateFunctionExpressionOperation(id, model); + } + + async getFunctionExpressionOperationById(id: string): Promise { + return await this._functionExpressionOperationRepo.getFunctionExpressionOperationById(id); + } + + async deleteFunctionExpressionOperation(id: string): Promise { + return await this._functionExpressionOperationRepo.deleteFunctionExpressionOperation(id); + } + + async searchFunctionExpressionOperation(filters: OperationSearchFilters): Promise { + return await this._functionExpressionOperationRepo.searchFunctionExpressionOperation(filters); + } +} \ No newline at end of file diff --git a/src/services/field.operations/iterate.operation.service.ts b/src/services/field.operations/iterate.operation.service.ts new file mode 100644 index 0000000..360942f --- /dev/null +++ b/src/services/field.operations/iterate.operation.service.ts @@ -0,0 +1,34 @@ +import { inject, injectable } from "tsyringe"; +import { IIterateOperationRepo } from "../../database/repository.interfaces/field.operations/iterate.operation/iterate.operation.repo.interface"; +import { + IterateOperationResponseDto, + IterateOperationCreateModel, + IterateOperationUpdateModel, + OperationSearchFilters +} from "../../domain.types/forms/operation.domain.types"; + +@injectable() +export class IterateOperationService { + constructor(@inject('IIterateOperationRepo') private _iterateOperationRepo: IIterateOperationRepo) { } + + // Iterate Operation operations + async createIterateOperation(model: IterateOperationCreateModel): Promise { + return await this._iterateOperationRepo.createIterateOperation(model); + } + + async updateIterateOperation(id: string, model: IterateOperationUpdateModel): Promise { + return await this._iterateOperationRepo.updateIterateOperation(id, model); + } + + async getIterateOperationById(id: string): Promise { + return await this._iterateOperationRepo.getIterateOperationById(id); + } + + async deleteIterateOperation(id: string): Promise { + return await this._iterateOperationRepo.deleteIterateOperation(id); + } + + async searchIterateOperation(filters: OperationSearchFilters): Promise { + return await this._iterateOperationRepo.searchIterateOperation(filters); + } +} \ No newline at end of file diff --git a/src/services/field.operations/logical.operation.service.ts b/src/services/field.operations/logical.operation.service.ts new file mode 100644 index 0000000..fde513d --- /dev/null +++ b/src/services/field.operations/logical.operation.service.ts @@ -0,0 +1,34 @@ +import { inject, injectable } from "tsyringe"; +import { ILogicalOperationRepo } from "../../database/repository.interfaces/field.operations/logical.operation/logical.operation.repo.interface"; +import { + LogicalOperationResponseDto, + LogicalOperationCreateModel, + LogicalOperationUpdateModel, + OperationSearchFilters +} from "../../domain.types/forms/operation.domain.types"; + +@injectable() +export class LogicalOperationService { + constructor(@inject('ILogicalOperationRepo') private _logicalOperationRepo: ILogicalOperationRepo) { } + + // Logical Operation operations + async createLogicalOperation(model: LogicalOperationCreateModel): Promise { + return await this._logicalOperationRepo.createLogicalOperation(model); + } + + async updateLogicalOperation(id: string, model: LogicalOperationUpdateModel): Promise { + return await this._logicalOperationRepo.updateLogicalOperation(id, model); + } + + async getLogicalOperationById(id: string): Promise { + return await this._logicalOperationRepo.getLogicalOperationById(id); + } + + async deleteLogicalOperation(id: string): Promise { + return await this._logicalOperationRepo.deleteLogicalOperation(id); + } + + async searchLogicalOperation(filters: OperationSearchFilters): Promise { + return await this._logicalOperationRepo.searchLogicalOperation(filters); + } +} \ No newline at end of file diff --git a/src/services/field.operations/mathematical.operation.service.ts b/src/services/field.operations/mathematical.operation.service.ts new file mode 100644 index 0000000..e77c1d7 --- /dev/null +++ b/src/services/field.operations/mathematical.operation.service.ts @@ -0,0 +1,34 @@ +import { inject, injectable } from "tsyringe"; +import { IMathematicalOperationRepo } from "../../database/repository.interfaces/field.operations/mathematical.operation/mathematical.operation.repo.interface"; +import { + MathematicalOperationResponseDto, + MathematicalOperationCreateModel, + MathematicalOperationUpdateModel, + OperationSearchFilters +} from "../../domain.types/forms/operation.domain.types"; + +@injectable() +export class MathematicalOperationService { + constructor(@inject('IMathematicalOperationRepo') private _mathematicalOperationRepo: IMathematicalOperationRepo) { } + + // Mathematical Operation operations + async createMathematicalOperation(model: MathematicalOperationCreateModel): Promise { + return await this._mathematicalOperationRepo.createMathematicalOperation(model); + } + + async updateMathematicalOperation(id: string, model: MathematicalOperationUpdateModel): Promise { + return await this._mathematicalOperationRepo.updateMathematicalOperation(id, model); + } + + async getMathematicalOperationById(id: string): Promise { + return await this._mathematicalOperationRepo.getMathematicalOperationById(id); + } + + async deleteMathematicalOperation(id: string): Promise { + return await this._mathematicalOperationRepo.deleteMathematicalOperation(id); + } + + async searchMathematicalOperation(filters: OperationSearchFilters): Promise { + return await this._mathematicalOperationRepo.searchMathematicalOperation(filters); + } +} \ No newline at end of file diff --git a/src/services/field.rules/calculation.rule.service.ts b/src/services/field.rules/calculation.rule.service.ts new file mode 100644 index 0000000..eab0917 --- /dev/null +++ b/src/services/field.rules/calculation.rule.service.ts @@ -0,0 +1,32 @@ +import { inject, injectable } from 'tsyringe'; +import { CalculationRuleCreateModel, CalculationRuleUpdateModel, CalculationRuleResponseDto, RuleSearchFilters } from '../../domain.types/forms/rule.domain.types'; +import { ICalculationRuleRepo } from '../../database/repository.interfaces/field.rules/calculation.rule/calculation.rule.repo.interface'; +import { uuid } from '../../domain.types/miscellaneous/system.types'; +import { BaseSearchResults } from '../../domain.types/miscellaneous/base.search.types'; + +@injectable() +export class CalculationRuleService { + constructor( + @inject('ICalculationRuleRepo') private _repo: ICalculationRuleRepo, + ) {} + + createCalculationRule = async (model: CalculationRuleCreateModel): Promise => { + return await this._repo.create(model); + }; + + getCalculationRuleById = async (id: uuid): Promise => { + return await this._repo.getById(id); + }; + + updateCalculationRule = async (id: uuid, model: CalculationRuleUpdateModel): Promise => { + return await this._repo.update(id, model); + }; + + deleteCalculationRule = async (id: uuid): Promise => { + return await this._repo.delete(id); + }; + + searchCalculationRule = async (filters: RuleSearchFilters): Promise => { + return await this._repo.search(filters); + }; +} \ No newline at end of file diff --git a/src/services/field.rules/skip.rule.service.ts b/src/services/field.rules/skip.rule.service.ts new file mode 100644 index 0000000..d5788d2 --- /dev/null +++ b/src/services/field.rules/skip.rule.service.ts @@ -0,0 +1,32 @@ +import { inject, injectable } from 'tsyringe'; +import { SkipRuleCreateModel, SkipRuleUpdateModel, SkipRuleResponseDto, RuleSearchFilters } from '../../domain.types/forms/rule.domain.types'; +import { ISkipRuleRepo } from '../../database/repository.interfaces/field.rules/skip.rule/skip.rule.repo.interface'; +import { uuid } from '../../domain.types/miscellaneous/system.types'; +import { BaseSearchResults } from '../../domain.types/miscellaneous/base.search.types'; + +@injectable() +export class SkipRuleService { + constructor( + @inject('ISkipRuleRepo') private _repo: ISkipRuleRepo, + ) {} + + createSkipRule = async (model: SkipRuleCreateModel): Promise => { + return await this._repo.create(model); + }; + + getSkipRuleById = async (id: uuid): Promise => { + return await this._repo.getById(id); + }; + + updateSkipRule = async (id: uuid, model: SkipRuleUpdateModel): Promise => { + return await this._repo.update(id, model); + }; + + deleteSkipRule = async (id: uuid): Promise => { + return await this._repo.delete(id); + }; + + searchSkipRule = async (filters: RuleSearchFilters): Promise => { + return await this._repo.search(filters); + }; +} \ No newline at end of file diff --git a/src/services/field.rules/validation.rule.service.ts b/src/services/field.rules/validation.rule.service.ts new file mode 100644 index 0000000..0d4c80e --- /dev/null +++ b/src/services/field.rules/validation.rule.service.ts @@ -0,0 +1,32 @@ +import { inject, injectable } from 'tsyringe'; +import { ValidationRuleCreateModel, ValidationRuleUpdateModel, ValidationRuleResponseDto, RuleSearchFilters } from '../../domain.types/forms/rule.domain.types'; +import { IValidationRuleRepo } from '../../database/repository.interfaces/field.rules/validation.rule/validation.rule.repo.interface'; +import { uuid } from '../../domain.types/miscellaneous/system.types'; +import { BaseSearchResults } from '../../domain.types/miscellaneous/base.search.types'; + +@injectable() +export class ValidationRuleService { + constructor( + @inject('IValidationRuleRepo') private _repo: IValidationRuleRepo, + ) {} + + createValidationRule = async (model: ValidationRuleCreateModel): Promise => { + return await this._repo.create(model); + }; + + getValidationRuleById = async (id: uuid): Promise => { + return await this._repo.getById(id); + }; + + updateValidationRule = async (id: uuid, model: ValidationRuleUpdateModel): Promise => { + return await this._repo.update(id, model); + }; + + deleteValidationRule = async (id: uuid): Promise => { + return await this._repo.delete(id); + }; + + searchValidationRule = async (filters: RuleSearchFilters): Promise => { + return await this._repo.search(filters); + }; +} \ No newline at end of file diff --git a/src/services/question/question.service.ts b/src/services/form.field/form.field.service.ts similarity index 71% rename from src/services/question/question.service.ts rename to src/services/form.field/form.field.service.ts index 37be54b..6bc3574 100644 --- a/src/services/question/question.service.ts +++ b/src/services/form.field/form.field.service.ts @@ -1,17 +1,17 @@ -import { QuestionCreateModel, QuestionOption, QuestionResponseDto, QuestionSearchFilters, QuestionSearchResponseDto, QuestionUpdateModel } from "../../domain.types/forms/question.domain.types"; +import { FormFieldCreateModel, FormFieldOption, FormFieldResponseDto, FormFieldSearchFilters, FormFieldSearchResponseDto, FormFieldUpdateModel } from "../../domain.types/forms/form.field.domain.types"; import { ErrorHandler } from "../../common/handlers/error.handler"; import { inject, injectable } from "tsyringe"; -import { IQuestionRepo } from "../../database/repository.interfaces/question/question.repo.interface"; +import { IFormFieldRepo } from "../../database/repository.interfaces/form.field/form.field.repo.interface"; @injectable() -export class QuestionService { +export class FormFieldService { - constructor(@inject('IQuestionRepo') private _questRepo: IQuestionRepo) { + constructor(@inject('IFormFieldRepo') private _formFieldRepo: IFormFieldRepo) { } - // allQuestions = async (): Promise => { - // const response = await this.prisma.question.findMany({ + // allFormFields = async (): Promise => { + // const response = await this.prisma.formField.findMany({ // include: { // ParentFormTemplate: true, // ParentFormSection: true @@ -20,10 +20,10 @@ export class QuestionService { // DeletedAt: null // } // }); - // return QuestionMapper.toArrayDto(response); + // return FormFieldMapper.toArrayDto(response); // }; - // create = async (model: QuestionCreateModel) => { + // create = async (model: FormFieldCreateModel) => { // const jsonData: Prisma.JsonValue = { // Sequence: model.Options.Sequence, @@ -31,7 +31,7 @@ export class QuestionService { // ImageUrl: model.Options.ImageUrl, // } as Prisma.JsonObject; - // const response = await this.prisma.question.create({ + // const response = await this.prisma.formField.create({ // data: { // ParentFormTemplate: { // connect: { id: model.ParentTemplateId } @@ -60,42 +60,42 @@ export class QuestionService { // ParentFormSection: true // } // }); - // return QuestionMapper.toDto(response); + // return FormFieldMapper.toDto(response); // }; - create = async (model: QuestionCreateModel): Promise => { - const dto = await this._questRepo.create(model); + create = async (model: FormFieldCreateModel): Promise => { + const dto = await this._formFieldRepo.create(model); return dto; }; - update = async (id: string, model: QuestionUpdateModel): Promise => { - const dto = await this._questRepo.update(id, model); + update = async (id: string, model: FormFieldUpdateModel): Promise => { + const dto = await this._formFieldRepo.update(id, model); return dto; }; - getById = async (id: string): Promise => { - const dto = await this._questRepo.getById(id); + getById = async (id: string): Promise => { + const dto = await this._formFieldRepo.getById(id); return dto; }; - getByTemplateId = async (id: string): Promise => { - const dto = await this._questRepo.getByTemplateId(id); + getByTemplateId = async (id: string): Promise => { + const dto = await this._formFieldRepo.getByTemplateId(id); return dto; }; delete = async (id: string): Promise => { - const dto = await this._questRepo.delete(id); + const dto = await this._formFieldRepo.delete(id); return true; }; - public search = async (filters: QuestionSearchFilters): Promise => { - const dto = await this._questRepo.search(filters); + public search = async (filters: FormFieldSearchFilters): Promise => { + const dto = await this._formFieldRepo.search(filters); return dto; }; - // private getSearchModel = (filters: QuestionSearchFilters): Prisma.QuestionWhereInput => { - // const where: Prisma.QuestionWhereInput = { DeletedAt: null }; + // private getSearchModel = (filters: FormFieldSearchFilters): Prisma.FormFieldWhereInput => { + // const where: Prisma.FormFieldWhereInput = { DeletedAt: null }; // if (filters.id) { // where.id = { @@ -183,4 +183,4 @@ export class QuestionService { // return where; // }; -} +} \ No newline at end of file diff --git a/src/services/form.section/form.section.service.ts b/src/services/form.section/form.section.service.ts index ac12178..42969fb 100644 --- a/src/services/form.section/form.section.service.ts +++ b/src/services/form.section/form.section.service.ts @@ -3,7 +3,7 @@ import { PrismaClientInit } from "../../startup/prisma.client.init"; import { FormSectionMapper } from "../../database/sql/typeorm/mappers/form.section.mapper"; import { FormSectionCreateModel, FormSectionResponseDto, FormSectionSearchFilters, FormSectionUpdateModel } from "../../domain.types/forms/form.section.domain.types"; // import { ErrorHandler } from "../../common/error.handler"; -import { inject,injectable } from "tsyringe"; +import { inject, injectable } from "tsyringe"; import { IFormSectionRepo } from "../../database/repository.interfaces/form.section/form.section.repo.interface"; @injectable() @@ -25,34 +25,34 @@ export class FormSectionService { // return FormSectionMapper.toArrayDto(response); // }; - create = async (model: FormSectionCreateModel) : Promise => { + create = async (model: FormSectionCreateModel): Promise => { var dto = await this._formSectionRepo.create(model); return dto; }; - update = async (id: string, model: FormSectionUpdateModel) : Promise => { - var dto = await this._formSectionRepo.update(id,model); + update = async (id: string, model: FormSectionUpdateModel): Promise => { + var dto = await this._formSectionRepo.update(id, model); return dto; }; - getById = async (id: string) : Promise => { + getById = async (id: string): Promise => { var dto = await this._formSectionRepo.getById(id); return dto; }; - delete = async (id: string) : Promise => { - var dto = await this._formSectionRepo.delete(id); + delete = async (id: string): Promise => { + var dto = await this._formSectionRepo.delete(id); return dto; }; - getByTemplateId = async (id: string) : Promise => { - var dto = await this._formSectionRepo.getByTemplateId(id); + getByTemplateId = async (id: string): Promise => { + var dto = await this._formSectionRepo.getByTemplateId(id); return dto; }; - public search=async(filters: FormSectionSearchFilters) : Promise => { - var dto = await this._formSectionRepo.search(filters); - return dto; + public search = async (filters: FormSectionSearchFilters): Promise => { + var dto = await this._formSectionRepo.search(filters); + return dto; } } diff --git a/src/services/form.submission/form.submission.service.ts b/src/services/form.submission/form.submission.service.ts index fac6365..acd21a0 100644 --- a/src/services/form.submission/form.submission.service.ts +++ b/src/services/form.submission/form.submission.service.ts @@ -1,5 +1,5 @@ -import { FormStatus, PrismaClient } from "@prisma/client"; -import { PrismaClientInit } from "../../startup/prisma.client.init"; +// import { FormStatus, PrismaClient } from "@prisma/client"; +// import { PrismaClientInit } from "../../startup/prisma.client.init"; import { FormMapper } from "../../database/sql/typeorm/mappers/form.submission.mapper" import { FormSubmissionCreateModel, FormSubmissionDto, FormSubmissionSearchFilters, FormSubmissionUpdateModel } from "../../domain.types/forms/form.submission.domain.types"; import { uuid } from "../../domain.types/miscellaneous/system.types"; @@ -11,55 +11,55 @@ import { IFormSubmissionRepo } from "../../database/repository.interfaces/form.s @injectable() export class FormService { - constructor(@inject('IFormSubmissionRepo') private _formSubmissionRepo : IFormSubmissionRepo) { - - } - // const response = await this.prisma.formSubmission.create({ - // data: { - // FormTemplate: { - // connect: { id: model.FormTemplateId } - // }, - // UserId: model.UserId, - // Status: model.Status, - // ValidTill: model.ValidTill, - // }, - // include: { - // FormTemplate: true, - // } - // }); + constructor(@inject('IFormSubmissionRepo') private _formSubmissionRepo: IFormSubmissionRepo) { - // return FormMapper.toDto(response); - // }; + } + // const response = await this.prisma.formSubmission.create({ + // data: { + // FormTemplate: { + // connect: { id: model.FormTemplateId } + // }, + // UserId: model.UserId, + // Status: model.Status, + // ValidTill: model.ValidTill, + // }, + // include: { + // FormTemplate: true, + // } + // }); - create = async (model: FormSubmissionCreateModel) : Promise => { - const dto=await this._formSubmissionRepo.create(model); - return dto; - }; + // return FormMapper.toDto(response); + // }; - update = async (id: string, model: FormSubmissionUpdateModel) : Promise=> { - const dto=await this._formSubmissionRepo.update(id,model); - return dto; - }; + create = async (model: FormSubmissionCreateModel): Promise => { + const dto = await this._formSubmissionRepo.create(model); + return dto; + }; + + update = async (id: string, model: FormSubmissionUpdateModel): Promise => { + const dto = await this._formSubmissionRepo.update(id, model); + return dto; + }; - getById = async (id: string) : Promise => { - const dto=await this._formSubmissionRepo.getById(id); - return dto; - }; + getById = async (id: string): Promise => { + const dto = await this._formSubmissionRepo.getById(id); + return dto; + }; - delete = async (id: string) : Promise=> { - const dto=await this._formSubmissionRepo.delete(id); - return dto; - }; + delete = async (id: string): Promise => { + const dto = await this._formSubmissionRepo.delete(id); + return dto; + }; - submit = async (id: uuid) : Promise=> { - const dto=await this._formSubmissionRepo.submit(id); - return dto; + submit = async (id: uuid): Promise => { + const dto = await this._formSubmissionRepo.submit(id); + return dto; }; - public search = async (filters: FormSubmissionSearchFilters) : Promise=> { - const dto=await this._formSubmissionRepo.search(filters); - return dto; - }; + public search = async (filters: FormSubmissionSearchFilters): Promise => { + const dto = await this._formSubmissionRepo.search(filters); + return dto; + }; } diff --git a/src/services/form.template/form.template.service.ts b/src/services/form.template/form.template.service.ts index 6f86209..6ff2ce8 100644 --- a/src/services/form.template/form.template.service.ts +++ b/src/services/form.template/form.template.service.ts @@ -15,7 +15,7 @@ import { import { FormTemplateMapper } from "../../database/sql/typeorm/mappers/form.template.mapper"; import { ErrorHandler } from "../../common/handlers/error.handler"; import { FormSectionMapper } from "../../database/sql/typeorm/mappers/form.section.mapper"; -import { QuestionMapper } from "../../database/sql/typeorm/mappers/question.mapper"; +// import { QuestionMapper } from "../../database/sql/typeorm/mappers/question.mapper"; import { IFormTemplateRepo } from "../../database/repository.interfaces/form.template/form.template.repo.interface"; import { inject, injectable } from "tsyringe"; @@ -24,7 +24,7 @@ import { inject, injectable } from "tsyringe"; @injectable() export class FormTemplateService { // prisma: PrismaClient = null; - constructor(@inject('IFormTemplateRepo') private _formTempRepo : IFormTemplateRepo) { + constructor(@inject('IFormTemplateRepo') private _formTempRepo: IFormTemplateRepo) { // this.prisma = PrismaClientInit.instance().getPrismaInstance(); } @@ -59,19 +59,19 @@ export class FormTemplateService { // return FormTemplateMapper.toDto(response); // }; - create = async (model: FormTemplateCreateModel) : Promise => { - const dto=await this._formTempRepo.create(model); - return dto; + create = async (model: FormTemplateCreateModel): Promise => { + const dto = await this._formTempRepo.create(model); + return dto; }; - update = async (id: string, model: FormTemplateUpdateModel) : Promise => { - const dto=await this._formTempRepo.update(id,model); - return dto; + update = async (id: string, model: FormTemplateUpdateModel): Promise => { + const dto = await this._formTempRepo.update(id, model); + return dto; }; - getById = async (id: string) : Promise => { - const dto=await this._formTempRepo.getById(id); - return dto; + getById = async (id: string): Promise => { + const dto = await this._formTempRepo.getById(id); + return dto; }; // getDetailsById = async (id: string) => { @@ -141,31 +141,31 @@ export class FormTemplateService { // // return searchResult; // }; - getDetailsById = async (id: string) : Promise => { - const dto=await this._formTempRepo.getDetailsById(id); - return dto; - + getDetailsById = async (id: string): Promise => { + const dto = await this._formTempRepo.getDetailsById(id); + return dto; + }; readTemplateObjToExport = async (id: string): Promise => { - const dto=await this._formTempRepo.readTemplateObjToExport(id); - return dto; + const dto = await this._formTempRepo.readTemplateObjToExport(id); + return dto; } - previewTemplate = async (id: string) : Promise => { - const dto=await this._formTempRepo.previewTemplate(id); - return dto; + previewTemplate = async (id: string): Promise => { + const dto = await this._formTempRepo.previewTemplate(id); + return dto; }; - delete = async (id: string) : Promise => { - const dto=await this._formTempRepo.delete(id); - return dto; + delete = async (id: string): Promise => { + const dto = await this._formTempRepo.delete(id); + return dto; }; - submissions = async (id: string) : Promise => { - const dto=await this._formTempRepo.submissions(id); - return dto; + submissions = async (id: string): Promise => { + const dto = await this._formTempRepo.submissions(id); + return dto; }; // protected addSortingAndPagination = ( @@ -211,9 +211,9 @@ export class FormTemplateService { // }; - public search = async (filters: FormTemplateSearchFilters) : Promise=> { - const dto=await this._formTempRepo.search(filters); - return dto; + public search = async (filters: FormTemplateSearchFilters): Promise => { + const dto = await this._formTempRepo.search(filters); + return dto; }; // public search = async (filters: FormTemplateSearchFilters) => { // try { diff --git a/src/startup/router.ts b/src/startup/router.ts index ee2745e..b7c3c1e 100644 --- a/src/startup/router.ts +++ b/src/startup/router.ts @@ -3,12 +3,25 @@ import { register as form } from '../api/form.submission/form.router'; import { register as formTemplate } from '../api/form.template/form.template.router'; import { register as user } from '../api/user/user.router'; import { register as formSection } from '../api/form.section/form.section.router' -import { register as question } from '../api/question/question.router'; +import { register as formField } from '../api/form.field/form.field.router'; import { register as Response } from '../api/question.response/question.response.router'; import { register as favoriteTemplate } from '../api/favorite.template/favorite.template.router'; import { register as formTemplateApproval } from '../api/form.template.approval/form.template.approval.router'; import { register as templateFolder } from '../api/template.folder/template.folder.router'; +import { register as skipLogic } from '../api/field.logic/skip.logic/skip.logic.router'; +import { register as calculationLogic } from '../api/field.logic/calculation.logic/calculation.logic.router'; +import { register as validationLogic } from '../api/field.logic/validation.logic/validation.logic.router'; +import { register as mathematicalOperation } from '../api/field.operations/mathematical.operation/mathematical.operation.router'; +import { register as logicalOperation } from '../api/field.operations/logical.operation/logical.operation.router'; +import { register as compositionOperation } from '../api/field.operations/composition.operation/composition.operation.router'; +import { register as iterateOperation } from '../api/field.operations/iterate.operation/iterate.operation.router'; +import { register as functionExpressionOperation } from '../api/field.operations/function.expression.operation/function.expression.operation.router'; +import { register as skipRule } from '../api/field.rules/skip.rule/skip.rule.router'; +import { register as calculationRule } from '../api/field.rules/calculation.rule/calculation.rule.router'; +import { register as validationRule } from '../api/field.rules/validation.rule/validation.rule.router'; +import { register as inputUnitList } from '../api/input.unit.list/input.unit.list.router'; + /////////////////////////////////////////////////////////////////////////////////////// export class Router { @@ -28,11 +41,26 @@ export class Router { formTemplate(this._app); user(this._app); formSection(this._app); - question(this._app); + formField(this._app); Response(this._app); favoriteTemplate(this._app); formTemplateApproval(this._app); templateFolder(this._app); + inputUnitList(this._app); + + skipLogic(this._app); + calculationLogic(this._app); + validationLogic(this._app); + mathematicalOperation(this._app); + logicalOperation(this._app); + compositionOperation(this._app); + iterateOperation(this._app); + functionExpressionOperation(this._app); + skipRule(this._app); + calculationRule(this._app); + validationRule(this._app); + + resolve(true); } catch (error) { console.log("Error initilizing the routes") From bc30589f482679e15ede70e2bc7b1032957cbaee Mon Sep 17 00:00:00 2001 From: inflection-prashant Date: Tue, 15 Jul 2025 18:29:09 +0530 Subject: [PATCH 07/29] code cleaneup and format code remove comments --- src/app.ts | 114 +------ src/database/database.connector.ts | 2 - src/database/database.injector.ts | 5 - src/database/sql/sql.injector.ts | 6 +- .../sql/typeorm/database.connector.typeorm.ts | 14 - .../mappers/calculation.logic.mapper.ts | 27 +- .../mappers/calculation.rule.mapper.ts | 39 +-- .../mappers/composition.operation.mapper.ts | 23 +- .../sql/typeorm/mappers/form.field.mapper.ts | 2 - .../typeorm/mappers/form.section.mapper.ts | 12 - .../typeorm/mappers/form.submission.mapper.ts | 52 +--- .../typeorm/mappers/form.template.mapper.ts | 1 - .../function.expression.operation.mapper.ts | 22 +- .../mappers/iterate.operation.mapper.ts | 28 +- .../mappers/logical.operation.mapper.ts | 21 +- .../mappers/mathematical.operation.mapper.ts | 22 +- .../mappers/question.response.mapper.ts | 46 +-- .../sql/typeorm/mappers/skip.logic.mapper.ts | 27 +- .../sql/typeorm/mappers/skip.rule.mapper.ts | 33 +- .../sql/typeorm/mappers/user.login.mapper.ts | 30 +- .../sql/typeorm/mappers/user.mapper.ts | 26 +- .../mappers/validation.logic.mapper.ts | 26 +- .../typeorm/mappers/validation.rule.mapper.ts | 34 +-- .../typeorm/models/logic/base.logic.model.ts | 4 +- .../models/operation/base.operation.model.ts | 3 +- .../operation/composition.operation.model.ts | 1 - .../function.expression.operation.model.ts | 1 - .../operation/iterate.operation.model.ts | 7 +- .../operation/logical.operation.model.ts | 1 - .../operation/mathematical.operation.model.ts | 1 - .../question.response.model.ts | 10 - .../typeorm/models/rule/base.rule.model.ts | 5 +- .../models/rule/calculation.rule.model.ts | 9 - .../typeorm/models/rule/skip.rule.model.ts | 2 - .../models/rule/validation.rule.model.ts | 6 - .../field.logic.calculation.repo.ts | 8 +- .../field.logic/field.logic.skip.repo.ts | 11 +- .../field.logic.validation.repo.ts | 8 +- .../field.operations.composition.repo.ts | 8 +- ...eld.operations.function.expression.repo.ts | 8 +- .../field.operations.iterate.repo.ts | 8 +- .../field.operations.logical.repo.ts | 8 +- .../field.operations.mathematical.repo.ts | 8 +- .../field.rules.calculation.repo.ts | 8 +- .../field.rules/field.rules.skip.repo.ts | 1 - .../field.rules.validation.repo.ts | 9 +- .../form.field/form.field.repo.ts | 33 +- .../form.section/form.section.repo.ts | 34 +-- .../form.submission/form.submission.repo.ts | 42 +-- .../form.template.approval.repo.ts | 6 +- .../form.template/form.template.repo.ts | 68 +---- .../input.unit.list/input.unit.list.repo.ts | 2 +- .../question.response.repo.ts | 26 +- .../template.folder/template.folder.repo.ts | 2 +- .../typeorm/repositories/user/user.repo.ts | 282 +++++++++--------- src/database/sql/typeorm/typeorm.injector.ts | 7 - .../forms/calculation.logic.domain.types.ts | 6 +- .../forms/form.section.domain.types.ts | 12 - .../forms/form.submission.enums.ts | 3 +- .../forms/form.template.domain.types.ts | 36 --- src/domain.types/forms/form.template.enums.ts | 1 - .../forms/input.unit.list.domain.types.ts | 3 +- src/domain.types/forms/logic.domain.types.ts | 28 +- .../forms/question.domain.types.ts | 77 +---- .../forms/response.domain.types.ts | 9 - src/domain.types/forms/rule.domain.types.ts | 3 - .../forms/skip.logic.domain.types.ts | 5 +- src/domain.types/miscellaneous/helper.ts | 4 - src/modules/module.injector.ts | 6 - src/modules/reports/pdf.generator.ts | 3 - .../composition.operation.service.ts | 2 - src/services/form.field/form.field.service.ts | 147 +-------- .../form.section/form.section.service.ts | 17 -- .../form.submission.service.ts | 21 -- .../form.template/form.template.service.ts | 266 ----------------- .../question.response.service.ts | 48 --- src/services/user.login.session.service.ts | 93 ------ src/services/user/user.service.ts | 36 +-- src/startup/injector.ts | 5 +- src/startup/loader.ts | 57 ---- src/startup/prisma.client.init.ts | 20 -- 81 files changed, 349 insertions(+), 1808 deletions(-) delete mode 100644 src/services/user.login.session.service.ts delete mode 100644 src/startup/prisma.client.init.ts diff --git a/src/app.ts b/src/app.ts index ca23040..9e1bd79 100644 --- a/src/app.ts +++ b/src/app.ts @@ -2,20 +2,15 @@ import express from 'express'; import "reflect-metadata"; import cors from 'cors'; import { Router } from './startup/router'; -import { execSync } from 'child_process'; -// import { Logger } from './startup/logger'; -import mysql from 'mysql2/promise'; import { Logger } from './common/logger'; import helmet from 'helmet'; import { ConfigurationManager } from './config/configuration.manager'; import { Loader } from './startup/loader'; import { Injector } from './startup/injector'; import { DatabaseClient } from './common/database.utils/dialect.clients/database.client'; -// import { DatabaseSchemaType } from './common/database.utils/database.config'; -import { PrimaryDatabaseConnector } from './database/database.connector'; import { DBConnector } from './database/sql/typeorm/database.connector.typeorm'; -// import ErrsoleMySQL from 'errsole-mysql'; -// import errsole from 'errsole'; + +///////////////////////////////////////////////////////////////////////////////////// export default class Application { @@ -31,13 +26,6 @@ export default class Application { } public static instance(): Application { - // if (this._instance === null) { - // this._instance = new this(); - // return this._instance; - // } - // else { - // return this._instance; - // } return this._instance || (this._instance = new this()) } @@ -45,44 +33,18 @@ export default class Application { start = async (): Promise => { try { - // errsole.initialize({ - // storage: new ErrsoleMySQL({ - // host: 'localhost', - // user: 'root', - // password: process.env.PASSWORD, - // database: process.env.DATABASE - // }) - // }); - - //Load configurations ConfigurationManager.loadConfigurations(); - //Register dependency injections Injector.registerInjections(); - //Load the modules await Loader.init(); - //Connect databases await connectDatabase_Primary(); - //Set-up middlewares await this.setupMiddlewares(); - - - - - // this.migrate(); - - - //Set the routes await this._router.init(); - //Seed the service - // await Loader.seeder.init(); - - //Start listening await this.listen(); } catch (error) { @@ -95,13 +57,9 @@ export default class Application { return new Promise((resolve, reject) => { try { this._app.use(express.urlencoded({ limit: '50mb', extended: true })); - this._app.use(express.json( { limit: '50mb' })); + this._app.use(express.json({ limit: '50mb' })); this._app.use(helmet()); this._app.use(cors()); - - //TODO: Move this to upload specific routes. Use router.use() method - // this.useFileUploadMiddleware(); - resolve(true); } catch (error) { @@ -122,78 +80,12 @@ export default class Application { } }) } - - // public migrate = async () => { - // try { - // const output = execSync('npx prisma migrate dev --name init'); - - // const str = output.toString(); - // Logger.instance().log('Database migrated successfully!'); - // Logger.instance().log(str); - - // return true; - // } catch (error) { - // Logger.instance().log(error.message); - // } - // return false; - // }; - - - // public migrate = async () => { - // const databaseUrl = process.env.DATABASE_URL; - - // if (!databaseUrl) { - // throw new Error('DATABASE_URL is not defined in the .env file'); - // } - - // // Parse the database URL to extract connection parameters - // const regex = /mysql:\/\/(.*?):(.*?)@(.*?):(.*?)\/(.*?)$/; - // const matches = databaseUrl.match(regex); - // if (!matches) { - // throw new Error('DATABASE_URL format is incorrect'); - // } - // const [_, user, password, host, port, database] = matches; - - // try { - // const connection = await mysql.createConnection({ - // host, - // port: parseInt(port), - // user, - // password - // }); - // // Directly construct the query string without placeholders - // const query = `SHOW DATABASES LIKE '${database}'`; - // const [rows]: [mysql.RowDataPacket[], mysql.FieldPacket[]] = await connection.execute(query); - // if (rows.length > 0) { - // Logger.instance().log(`Database ${database} already exists. Connecting and syncing...`); - // // Here you would add code to sync with the existing database if needed - // } else { - // Logger.instance().log(`Database ${database} does not exist. Migrating and syncing...`); - // execSync('npx prisma migrate dev --name init'); - // Logger.instance().log('Database migrated and synced successfully!'); - // } - - // await connection.end(); - // return true; - // } catch (error) { - // Logger.instance().error('Migration failed:', 500, error.message); - // Logger.instance().error('Migration failed:', 500, error.stack); - // // Logger.instance().log(error.message); - // // Logger.instance().log(error.stack); // Log stack trace for debugging purposes - // return false; - // } - // }; - - } async function connectDatabase_Primary() { if (process.env.NODE_ENV === 'test') { - // const databaseClient = Injector.Container.resolve(DatabaseClient); - // await databaseClient.dropDb(DatabaseSchemaType.Primary); await DatabaseClient.dropDatabase(); } - // const primaryDatabaseConnector = Injector.Container.resolve(PrimaryDatabaseConnector); await DatabaseClient.createDatabase(); await DBConnector.initialize(); } diff --git a/src/database/database.connector.ts b/src/database/database.connector.ts index 08ae3cf..ae76f36 100644 --- a/src/database/database.connector.ts +++ b/src/database/database.connector.ts @@ -40,5 +40,3 @@ export class PrimaryDatabaseConnector { }; } - -//////////////////////////////////////////////////////////////////////// diff --git a/src/database/database.injector.ts b/src/database/database.injector.ts index d9defee..f9d714f 100644 --- a/src/database/database.injector.ts +++ b/src/database/database.injector.ts @@ -13,10 +13,5 @@ export class DatabaseInjector { if (databaseType === 'SQL') { SQLInjector.registerInjections(container); } - // else if (databaseType === 'NoSQL') { - // NoSQLInjector.registerInjections(container); - // } - } - } diff --git a/src/database/sql/sql.injector.ts b/src/database/sql/sql.injector.ts index c87a207..395de41 100644 --- a/src/database/sql/sql.injector.ts +++ b/src/database/sql/sql.injector.ts @@ -5,16 +5,12 @@ import { TypeOrmInjector } from './typeorm/typeorm.injector'; //////////////////////////////////////////////////////////////////////////////// -export class SQLInjector -{ - +export class SQLInjector { static registerInjections(container: DependencyContainer) { const databaseORM = ConfigurationManager.DatabaseORM(); if (databaseORM === 'TypeORM') { TypeOrmInjector.registerInjections(container); } - } - } diff --git a/src/database/sql/typeorm/database.connector.typeorm.ts b/src/database/sql/typeorm/database.connector.typeorm.ts index 52d88b2..e2d6276 100644 --- a/src/database/sql/typeorm/database.connector.typeorm.ts +++ b/src/database/sql/typeorm/database.connector.typeorm.ts @@ -141,20 +141,6 @@ class DatabaseConnector implements IPrimaryDatabaseConnector { } }; - // public migrate = async () => { - // try { - // const output = execSync('npx sequelize-cli db:migrate'); - - // const str = output.toString(); - // Logger.instance().log('Migration completed successfully!'); - // Logger.instance().log(str); - - // return true; - // } catch (error) { - // Logger.instance().log(error.message); - // } - // return false; - // }; public migrate = async (): Promise => { try { if (!DatabaseConnector._source.isInitialized) { diff --git a/src/database/sql/typeorm/mappers/calculation.logic.mapper.ts b/src/database/sql/typeorm/mappers/calculation.logic.mapper.ts index 27edb47..c68422a 100644 --- a/src/database/sql/typeorm/mappers/calculation.logic.mapper.ts +++ b/src/database/sql/typeorm/mappers/calculation.logic.mapper.ts @@ -21,27 +21,10 @@ export class CalculationLogicMapper { return dto; }; - static toCalculationLogicDto = (record: any): CalculationLogicResponseDto => { - if (record === null) { - return null; + static toArrayDto(records: any[]): CalculationLogicResponseDto[] { + if (records === null) { + return []; } - - const dto: CalculationLogicResponseDto = { - id: record.id, - Type: LogicType.Calculation, - FieldId: record.FieldId, - Enabled: record.Enabled, - FallbackValue: record.FallbackValue, - Rules: record.Rules ? record.Rules.map((rule: any) => ({ - id: rule.id, - OperationId: rule.OperationId, - LogicId: rule.LogicId, - CreatedAt: rule.CreatedAt, - UpdatedAt: rule.UpdatedAt - })) : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; + return records.map(record => CalculationLogicMapper.toDto(record)); + } } \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/calculation.rule.mapper.ts b/src/database/sql/typeorm/mappers/calculation.rule.mapper.ts index a4ad8cb..f50a92f 100644 --- a/src/database/sql/typeorm/mappers/calculation.rule.mapper.ts +++ b/src/database/sql/typeorm/mappers/calculation.rule.mapper.ts @@ -23,39 +23,10 @@ export class CalculationRuleMapper { return dto; }; - static toCalculationRuleDto = (record: any): CalculationRuleResponseDto => { - if (record === null) { - return null; + static toArrayDto(records: any[]): CalculationRuleResponseDto[] { + if (records === null) { + return []; } - - const dto: CalculationRuleResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Priority: record.Priority, - IsActive: record.IsActive, - ConditionForOperationId: record.ConditionForOperationId, - OperationId: record.OperationId, - LogicId: record.LogicId, - ConditionForOperation: record.ConditionForOperation ? { - id: record.ConditionForOperation.id, - Name: record.ConditionForOperation.Name, - Description: record.ConditionForOperation.Description - } : undefined, - Operation: record.Operation ? { - id: record.Operation.id, - Name: record.Operation.Name, - Description: record.Operation.Description - } : undefined, - Logic: record.Logic ? { - id: record.Logic.id, - Type: record.Logic.Type, - DefaultSkip: record.Logic.DefaultSkip, - FallbackValue: record.Logic.FallbackValue - } : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; + return records.map(record => CalculationRuleMapper.toDto(record)); + } } \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/composition.operation.mapper.ts b/src/database/sql/typeorm/mappers/composition.operation.mapper.ts index 87d7762..5854689 100644 --- a/src/database/sql/typeorm/mappers/composition.operation.mapper.ts +++ b/src/database/sql/typeorm/mappers/composition.operation.mapper.ts @@ -21,21 +21,10 @@ export class CompositionOperationMapper { return dto; }; - static toCompositionOperationDto = (record: any): CompositionOperationResponseDto => { - if (record === null) { - return null; + static toArrayDto(records: any[]): CompositionOperationResponseDto[] { + if (records === null) { + return []; } - - const dto: CompositionOperationResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Type: record.Type, - Operator: record.Operator, - Operands: record.Operands, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; -} \ No newline at end of file + return records.map(record => CompositionOperationMapper.toDto(record)); + } +} \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/form.field.mapper.ts b/src/database/sql/typeorm/mappers/form.field.mapper.ts index 118cf6b..1515f80 100644 --- a/src/database/sql/typeorm/mappers/form.field.mapper.ts +++ b/src/database/sql/typeorm/mappers/form.field.mapper.ts @@ -6,7 +6,6 @@ export class FormFieldMapper { return null; } - // Parse the Options JSON if it's present let options: FormFieldOption[] = []; if (record.Options !== null && record.Options !== undefined) { try { @@ -16,7 +15,6 @@ export class FormFieldMapper { } } - // Map the record to FormFieldResponseDto const dto: FormFieldResponseDto = { id: record.id, Title: record.Title, diff --git a/src/database/sql/typeorm/mappers/form.section.mapper.ts b/src/database/sql/typeorm/mappers/form.section.mapper.ts index eee5e95..25cd031 100644 --- a/src/database/sql/typeorm/mappers/form.section.mapper.ts +++ b/src/database/sql/typeorm/mappers/form.section.mapper.ts @@ -7,18 +7,6 @@ export class FormSectionMapper { } const dto: FormSectionResponseDto = { - // id: record.id, - // ParentFormTemplate: { - // id: record.ParentFormTemplate.id, - // Title: record.ParentFormTemplate.Title, - // Description: record.ParentFormTemplate.Description, - // CurrentVersion: record.ParentFormTemplate.CurrentVersion, - // Type: record.ParentFormTemplate.Type, - // DisplayCode: record.ParentFormTemplate.DisplayCode, - // OwnerUserId: record.ParentFormTemplate.OwnerUserId, - // RootSectionId: record.ParentFormTemplate.RootSectionId, - // DefaultSectionNumbering: record.ParentFormTemplate.DefaultSectionNumbering - // }, id: record.id, FormTemplateId: record.FormTemplateId, SectionIdentifier: record.SectionIdentifier, diff --git a/src/database/sql/typeorm/mappers/form.submission.mapper.ts b/src/database/sql/typeorm/mappers/form.submission.mapper.ts index 116ae36..743f426 100644 --- a/src/database/sql/typeorm/mappers/form.submission.mapper.ts +++ b/src/database/sql/typeorm/mappers/form.submission.mapper.ts @@ -1,6 +1,6 @@ import { FormSubmissionDto } from "../../../../domain.types/forms/form.submission.domain.types"; -export class FormMapper { +export class FormSubmissionMapper { static toDto = (record: any): FormSubmissionDto => { if (record === null) { return null; @@ -23,48 +23,10 @@ export class FormMapper { return dto; }; - // static toArrayDto(record: any[]): FormSubmissionResponseDto[] { - // if (record === null) { - // return null; - // } - - // const dtos: FormSubmissionResponseDto[] = []; - - // for (let i = 0; i < record.length; i++) { - // const element = record[i]; - // dtos.push({ - // id: element.id, - // ParentFormTemplateId: element.FormTemplateId, - // ParentFormTemplate: { - // id: element.FormTemplate.id, - // Title: element.FormTemplate.Title, - // Description: element.FormTemplate.Description, - // CurrentVersion: element.FormTemplate.CorrectAnswer, - // Type: element.FormTemplate.Type, - // DisplayCode: element.FormTemplate.DisplayCode, - // OwnerUserId: element.FormTemplate.OwnerUserId, - // RootSectionId: element.FormTemplate.RootSectionId, - // DefaultSectionNumbering: element.DefaultSectionNumbering, - // CreatedAt: element.FormTemplate.CreatedAt, - // UpdatedAt: element.FormTemplate.UpdatedAt, - // }, - // // Submitter: { - // // id: element.Submitter.id, - // // FirstName: element.Submitter.FirstName, - // // LastName: element.Submitter.LastName, - // // Phone: element.Submitter.Phone, - // // Email: element.Submitter.Email, - // // UserName: element.Submitter.UserName, - // // CountryCode: element.Submitter.CountryCode - // // }, - // FormUrl: element.FormUrl, - // AnsweredByUserId: element.AnsweredByUserId, - // Status: element.FormStatus, - // SubmissionTimestamp: element.SubmissionTimestamp, - // CreatedAt: element.CreatedAt, - // UpdatedAt: element.UpdatedAt, - // }); - // } - // return dtos; - // } + static toArrayDto(records: any[]): FormSubmissionDto[] { + if (records === null) { + return []; + } + return records.map(record => FormSubmissionMapper.toDto(record)); + } } diff --git a/src/database/sql/typeorm/mappers/form.template.mapper.ts b/src/database/sql/typeorm/mappers/form.template.mapper.ts index 8b7f197..2d57a36 100644 --- a/src/database/sql/typeorm/mappers/form.template.mapper.ts +++ b/src/database/sql/typeorm/mappers/form.template.mapper.ts @@ -13,7 +13,6 @@ export class FormTemplateMapper { CurrentVersion: record.CurrentVersion, TenantCode: record.TenantCode, Type: record.Type, - // ItemsPerPage: record.ItemsPerPage, DisplayCode: record.DisplayCode, OwnerUserId: record.OwnerUserId, RootSectionId: record.RootSectionId, diff --git a/src/database/sql/typeorm/mappers/function.expression.operation.mapper.ts b/src/database/sql/typeorm/mappers/function.expression.operation.mapper.ts index 36a4485..d703e66 100644 --- a/src/database/sql/typeorm/mappers/function.expression.operation.mapper.ts +++ b/src/database/sql/typeorm/mappers/function.expression.operation.mapper.ts @@ -22,22 +22,10 @@ export class FunctionExpressionOperationMapper { return dto; }; - static toFunctionExpressionOperationDto = (record: any): FunctionExpressionOperationResponseDto => { - if (record === null) { - return null; + static toArrayDto(records: any[]): FunctionExpressionOperationResponseDto[] { + if (records === null) { + return []; } - - const dto: FunctionExpressionOperationResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Type: record.Type, - Expression: record.Expression, - Variables: record.Variables, - ResultDataType: record.ResultDataType, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; + return records.map(record => FunctionExpressionOperationMapper.toDto(record)); + } } \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/iterate.operation.mapper.ts b/src/database/sql/typeorm/mappers/iterate.operation.mapper.ts index 0360dba..45430fe 100644 --- a/src/database/sql/typeorm/mappers/iterate.operation.mapper.ts +++ b/src/database/sql/typeorm/mappers/iterate.operation.mapper.ts @@ -23,28 +23,10 @@ export class IterateOperationMapper { return dto; }; - static toIterateOperationDto = (record: any): IterateOperationResponseDto => { - if (record === null) { - return null; + static toArrayDto(records: any[]): IterateOperationResponseDto[] { + if (records === null) { + return []; } - - const dto: IterateOperationResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Type: record.Type, - CollectionField: record.CollectionField, - ResultField: record.ResultField, - OperationId: record.OperationId, - FilterExpression: record.FilterExpression, - Operation: record.Operation ? { - id: record.Operation.id, - Name: record.Operation.Name, - Description: record.Operation.Description - } : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; + return records.map(record => IterateOperationMapper.toDto(record)); + } } \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/logical.operation.mapper.ts b/src/database/sql/typeorm/mappers/logical.operation.mapper.ts index ce49f1d..b6dce8d 100644 --- a/src/database/sql/typeorm/mappers/logical.operation.mapper.ts +++ b/src/database/sql/typeorm/mappers/logical.operation.mapper.ts @@ -21,21 +21,10 @@ export class LogicalOperationMapper { return dto; }; - static toLogicalOperationDto = (record: any): LogicalOperationResponseDto => { - if (record === null) { - return null; + static toArrayDto(records: any[]): LogicalOperationResponseDto[] { + if (records === null) { + return []; } - - const dto: LogicalOperationResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Type: record.Type, - Operator: record.Operator, - Operands: record.Operands, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; + return records.map(record => LogicalOperationMapper.toDto(record)); + } } \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/mathematical.operation.mapper.ts b/src/database/sql/typeorm/mappers/mathematical.operation.mapper.ts index 1e2ea04..8bcaae6 100644 --- a/src/database/sql/typeorm/mappers/mathematical.operation.mapper.ts +++ b/src/database/sql/typeorm/mappers/mathematical.operation.mapper.ts @@ -22,22 +22,10 @@ export class MathematicalOperationMapper { return dto; }; - static toMathematicalOperationDto = (record: any): MathematicalOperationResponseDto => { - if (record === null) { - return null; + static toArrayDto(records: any[]): MathematicalOperationResponseDto[] { + if (records === null) { + return []; } - - const dto: MathematicalOperationResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Type: record.Type, - Operator: record.Operator, - Operands: record.Operands, - ResultDataType: record.ResultDataType, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; + return records.map(record => MathematicalOperationMapper.toDto(record)); + } } \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/question.response.mapper.ts b/src/database/sql/typeorm/mappers/question.response.mapper.ts index ddbf89d..a3bd6bc 100644 --- a/src/database/sql/typeorm/mappers/question.response.mapper.ts +++ b/src/database/sql/typeorm/mappers/question.response.mapper.ts @@ -8,29 +8,29 @@ export class ResponseMapper { const dto: QuestionResponseResponseDto = { id : record.id, - // FormSubmission: { - // id : record.FormSubmission.id, - // TemplateId : record.FormSubmission.TemplateId, - // FormUrl : record.FormSubmission.FormUrl, - // UserId : record.FormSubmission.UserId, - // Status : record.FormSubmission.Status, - // SubmissionTimestamp: record.FormSubmission.SubmissionTimestamp, - // CreatedAt : record.FormSubmission.CreatedAt - // }, - // Question: { - // id : record.Question.id, - // Title : record.Question.Title, - // Description : record.Question.Description, - // DisplayCode : record.Question.DisplayCode, - // ResponseType : record.Question.ResponseType, - // Score : record.Question.Score, - // CorrectAnswer: record.Question.CorrectAnswer, - // Hint : record.Question.Hint, - // TemplateId : record.Question.TemplateId, - // SectionId : record.Question.SectionId, - // CreatedAt : record.Question.CreatedAt, - // UpdatedAt : record.Question.UpdatedAt - // }, + FormSubmission: { + id: record.FormSubmission.id, + TemplateId: record.FormSubmission.TemplateId, + FormUrl: record.FormSubmission.FormUrl, + UserId: record.FormSubmission.UserId, + Status: record.FormSubmission.Status, + SubmissionTimestamp: record.FormSubmission.SubmissionTimestamp, + CreatedAt: record.FormSubmission.CreatedAt, + }, + Question: { + id: record.Question.id, + Title: record.Question.Title, + Description: record.Question.Description, + DisplayCode: record.Question.DisplayCode, + ResponseType: record.Question.ResponseType, + Score: record.Question.Score, + CorrectAnswer: record.Question.CorrectAnswer, + Hint: record.Question.Hint, + TemplateId: record.Question.TemplateId, + SectionId: record.Question.SectionId, + CreatedAt: record.Question.CreatedAt, + UpdatedAt: record.Question.UpdatedAt, + }, ResponseType : record.ResponseType, IntegerValue : record.IntegerValue, FloatValue : record.FloatValue, diff --git a/src/database/sql/typeorm/mappers/skip.logic.mapper.ts b/src/database/sql/typeorm/mappers/skip.logic.mapper.ts index 0b227ea..816d0c8 100644 --- a/src/database/sql/typeorm/mappers/skip.logic.mapper.ts +++ b/src/database/sql/typeorm/mappers/skip.logic.mapper.ts @@ -21,28 +21,11 @@ export class SkipLogicMapper { return dto; }; - static toSkipLogicDto = (record: any): SkipLogicResponseDto => { - if (record === null) { - return null; + static toArrayDto(records: any[]): SkipLogicResponseDto[] { + if (records === null) { + return []; } + return records.map(record => SkipLogicMapper.toDto(record)); + } - const dto: SkipLogicResponseDto = { - id: record.id, - Type: LogicType.Skip, - FieldId: record.FieldId, - Enabled: record.Enabled, - DefaultSkip: record.DefaultSkip, - Rules: record.Rules ? record.Rules.map((rule: any) => ({ - id: rule.id, - OperationId: rule.OperationId, - SkipWhenTrue: rule.SkipWhenTrue, - LogicId: rule.LogicId, - CreatedAt: rule.CreatedAt, - UpdatedAt: rule.UpdatedAt - })) : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; } \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/skip.rule.mapper.ts b/src/database/sql/typeorm/mappers/skip.rule.mapper.ts index 2d588f6..9192dca 100644 --- a/src/database/sql/typeorm/mappers/skip.rule.mapper.ts +++ b/src/database/sql/typeorm/mappers/skip.rule.mapper.ts @@ -23,33 +23,10 @@ export class SkipRuleMapper { return dto; }; - static toSkipRuleDto = (record: any): SkipRuleResponseDto => { - if (record === null) { - return null; + static toArrayDto(records: any[]): SkipRuleResponseDto[] { + if (records === null) { + return []; } - - const dto: SkipRuleResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Priority: record.Priority, - IsActive: record.IsActive, - OperationId: record.OperationId, - SkipWhenTrue: record.SkipWhenTrue, - LogicId: record.LogicId, - Operation: record.Operation ? { - id: record.Operation.id, - Name: record.Operation.Name, - Description: record.Operation.Description - } : undefined, - Logic: record.Logic ? { - id: record.Logic.id, - Type: record.Logic.Type, - DefaultSkip: record.Logic.DefaultSkip - } : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; + return records.map(record => SkipRuleMapper.toDto(record)); + } } \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/user.login.mapper.ts b/src/database/sql/typeorm/mappers/user.login.mapper.ts index 29383e7..6f50b50 100644 --- a/src/database/sql/typeorm/mappers/user.login.mapper.ts +++ b/src/database/sql/typeorm/mappers/user.login.mapper.ts @@ -27,32 +27,10 @@ export class UserLoginSessionMapper { return dto; }; - static toArrayDto(record: any[]): UserLoginSessionResponseDto[] { - if (record === null) { - return null; - } - - const dtos: UserLoginSessionResponseDto[] = []; - - for (let i = 0; i < record.length; i++) { - const element = record[i]; - dtos.push({ - id: element.id, - User: { - FirstName: element.User.FirstName, - LastName: element.User.LastName, - CountryCode: element.User.CountryCode, - Phone: element.User.Phone, - Email: element.User.Email, - Username: element.User.Username, - Password: element.User.Password, - CreatedAt: element.User.CreatedAt - }, - IsActiveSession: element.IsActiveSession, - StartedAt: element.StartedAt, - ValidTill: element.ValidTill - }); + static toArrayDto(records: any[]): UserLoginSessionResponseDto[] { + if (records === null) { + return []; } - return dtos; + return records.map(record => UserLoginSessionMapper.toDto(record)); } } diff --git a/src/database/sql/typeorm/mappers/user.mapper.ts b/src/database/sql/typeorm/mappers/user.mapper.ts index fc2a4e1..205a344 100644 --- a/src/database/sql/typeorm/mappers/user.mapper.ts +++ b/src/database/sql/typeorm/mappers/user.mapper.ts @@ -20,28 +20,10 @@ export class UserMapper { return dto; }; - static toArrayDto(record: any[]): UserResponseDto[] { - if (record === null) { - return null; - } - - const dtos: UserResponseDto[] = []; - - for (let i = 0; i < record.length; i++) { - const element = record[i]; - dtos.push({ - id: element.id, - FirstName: element.FirstName, - LastName: element.LastName, - CountryCode: element.CountryCode, - Phone: element.Phone, - Email: element.Email, - Username: element.Username, - Password: element.Password, - CreatedAt: element.CreatedAt - }); + static toArrayDto(records: any[]): UserResponseDto[] { + if (records === null) { + return []; } - return dtos; - + return records.map(record => UserMapper.toDto(record)); } } diff --git a/src/database/sql/typeorm/mappers/validation.logic.mapper.ts b/src/database/sql/typeorm/mappers/validation.logic.mapper.ts index 6d4b4bf..6d87289 100644 --- a/src/database/sql/typeorm/mappers/validation.logic.mapper.ts +++ b/src/database/sql/typeorm/mappers/validation.logic.mapper.ts @@ -20,26 +20,10 @@ export class ValidationLogicMapper { return dto; }; - static toValidationLogicDto = (record: any): ValidationLogicResponseDto => { - if (record === null) { - return null; + static toArrayDto(records: any[]): ValidationLogicResponseDto[] { + if (records === null) { + return []; } - - const dto: ValidationLogicResponseDto = { - id: record.id, - Type: LogicType.Validation, - FieldId: record.FieldId, - Enabled: record.Enabled, - Rules: record.Rules ? record.Rules.map((rule: any) => ({ - id: rule.id, - OperationId: rule.OperationId, - LogicId: rule.LogicId, - CreatedAt: rule.CreatedAt, - UpdatedAt: rule.UpdatedAt - })) : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; + return records.map(record => ValidationLogicMapper.toDto(record)); + } } \ No newline at end of file diff --git a/src/database/sql/typeorm/mappers/validation.rule.mapper.ts b/src/database/sql/typeorm/mappers/validation.rule.mapper.ts index 5c29bed..b082bae 100644 --- a/src/database/sql/typeorm/mappers/validation.rule.mapper.ts +++ b/src/database/sql/typeorm/mappers/validation.rule.mapper.ts @@ -24,34 +24,10 @@ export class ValidationRuleMapper { return dto; }; - static toValidationRuleDto = (record: any): ValidationRuleResponseDto => { - if (record === null) { - return null; + static toArrayDto(records: any[]): ValidationRuleResponseDto[] { + if (records === null) { + return []; } - - const dto: ValidationRuleResponseDto = { - id: record.id, - Name: record.Name, - Description: record.Description, - Priority: record.Priority, - IsActive: record.IsActive, - OperationId: record.OperationId, - ErrorWhenFalse: record.ErrorWhenFalse, - ErrorMessage: record.ErrorMessage, - LogicId: record.LogicId, - Operation: record.Operation ? { - id: record.Operation.id, - Name: record.Operation.Name, - Description: record.Operation.Description - } : undefined, - Logic: record.Logic ? { - id: record.Logic.id, - Type: record.Logic.Type, - DefaultSkip: record.Logic.DefaultSkip - } : undefined, - CreatedAt: record.CreatedAt, - UpdatedAt: record.UpdatedAt - }; - return dto; - }; + return records.map(record => ValidationRuleMapper.toDto(record)); + } } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/logic/base.logic.model.ts b/src/database/sql/typeorm/models/logic/base.logic.model.ts index 99f3017..ebf2608 100644 --- a/src/database/sql/typeorm/models/logic/base.logic.model.ts +++ b/src/database/sql/typeorm/models/logic/base.logic.model.ts @@ -1,8 +1,6 @@ -import { Entity, Column } from 'typeorm'; +import { Column } from 'typeorm'; import { BaseEntity } from '../base.entity'; -import { LogicType } from '../../../../../domain.types/forms/logic.enums'; -// Base Logic Entity (Abstract - no table) export abstract class BaseLogicEntity extends BaseEntity { @Column({ type: 'uuid', nullable: false }) FieldId: string; diff --git a/src/database/sql/typeorm/models/operation/base.operation.model.ts b/src/database/sql/typeorm/models/operation/base.operation.model.ts index f8da943..f2b91c4 100644 --- a/src/database/sql/typeorm/models/operation/base.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/base.operation.model.ts @@ -1,8 +1,7 @@ -import { Entity, Column } from 'typeorm'; +import { Column } from 'typeorm'; import { BaseEntity } from '../base.entity'; import { OperationType } from '../../../../../domain.types/forms/operation.enums'; -// Base Operation Entity (Abstract - no table) export abstract class BaseOperationEntity extends BaseEntity { @Column({ type: 'varchar', length: 255, nullable: true }) Name?: string; diff --git a/src/database/sql/typeorm/models/operation/composition.operation.model.ts b/src/database/sql/typeorm/models/operation/composition.operation.model.ts index a547817..ac8f155 100644 --- a/src/database/sql/typeorm/models/operation/composition.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/composition.operation.model.ts @@ -2,7 +2,6 @@ import { Entity, Column } from 'typeorm'; import { BaseOperationEntity } from './base.operation.model'; import { CompositionOperatorType } from '../../../../../domain.types/forms/operation.enums'; -// Composition Operation Entity @Entity({ name: 'eval_composition_operations' }) export class CompositionOperationEntity extends BaseOperationEntity { @Column({ type: 'varchar', length: 50, nullable: false }) diff --git a/src/database/sql/typeorm/models/operation/function.expression.operation.model.ts b/src/database/sql/typeorm/models/operation/function.expression.operation.model.ts index c210030..dfc2352 100644 --- a/src/database/sql/typeorm/models/operation/function.expression.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/function.expression.operation.model.ts @@ -1,7 +1,6 @@ import { Entity, Column } from 'typeorm'; import { BaseOperationEntity } from './base.operation.model'; -// Function Expression Operation Entity @Entity({ name: 'eval_function_expression_operations' }) export class FunctionExpressionOperationEntity extends BaseOperationEntity { @Column({ type: 'text', nullable: false }) diff --git a/src/database/sql/typeorm/models/operation/iterate.operation.model.ts b/src/database/sql/typeorm/models/operation/iterate.operation.model.ts index 0026a23..bfc0c9f 100644 --- a/src/database/sql/typeorm/models/operation/iterate.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/iterate.operation.model.ts @@ -1,7 +1,6 @@ -import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { Entity, Column} from 'typeorm'; import { BaseOperationEntity } from './base.operation.model'; -// Iterate Operation Entity @Entity({ name: 'eval_iterate_operations' }) export class IterateOperationEntity extends BaseOperationEntity { @Column({ type: 'varchar', length: 255, nullable: false }) @@ -13,10 +12,6 @@ export class IterateOperationEntity extends BaseOperationEntity { @Column({ type: 'uuid', nullable: false }) OperationId: string; - // @ManyToOne(() => BaseOperationEntity, { nullable: false }) - // @JoinColumn({ name: 'OperationId' }) - // Operation: BaseOperationEntity; - @Column({ type: 'text', nullable: true }) FilterExpression?: string; // Optional filter expression } \ No newline at end of file diff --git a/src/database/sql/typeorm/models/operation/logical.operation.model.ts b/src/database/sql/typeorm/models/operation/logical.operation.model.ts index cd332b9..693744d 100644 --- a/src/database/sql/typeorm/models/operation/logical.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/logical.operation.model.ts @@ -2,7 +2,6 @@ import { Entity, Column } from 'typeorm'; import { BaseOperationEntity } from './base.operation.model'; import { LogicalOperatorType } from '../../../../../domain.types/forms/operation.enums'; -// Logical Operation Entity @Entity({ name: 'eval_logical_operations' }) export class LogicalOperationEntity extends BaseOperationEntity { @Column({ type: 'varchar', length: 50, nullable: false }) diff --git a/src/database/sql/typeorm/models/operation/mathematical.operation.model.ts b/src/database/sql/typeorm/models/operation/mathematical.operation.model.ts index eb395ab..08cf121 100644 --- a/src/database/sql/typeorm/models/operation/mathematical.operation.model.ts +++ b/src/database/sql/typeorm/models/operation/mathematical.operation.model.ts @@ -2,7 +2,6 @@ import { Entity, Column } from 'typeorm'; import { BaseOperationEntity } from './base.operation.model'; import { MathematicalOperatorType } from '../../../../../domain.types/forms/operation.enums'; -// Mathematical Operation Entity @Entity({ name: 'eval_mathematical_operations' }) export class MathematicalOperationEntity extends BaseOperationEntity { @Column({ type: 'varchar', length: 50, nullable: false }) diff --git a/src/database/sql/typeorm/models/question.response/question.response.model.ts b/src/database/sql/typeorm/models/question.response/question.response.model.ts index a9ab3d4..953937e 100644 --- a/src/database/sql/typeorm/models/question.response/question.response.model.ts +++ b/src/database/sql/typeorm/models/question.response/question.response.model.ts @@ -2,10 +2,8 @@ import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseEntity } from '../base.entity'; import { FormSubmission } from '../form.submission/form.submission.model'; import { QueryResponseType } from '../../../../../domain.types/forms/query.response.types'; -// import { Question } from '../question/question.model'; import { FormFieldEntity } from '../form.field/form.field.model'; - @Entity('question_responses') export class QuestionResponse extends BaseEntity { @Column({ type: 'uuid', nullable: false }) @@ -44,13 +42,9 @@ export class QuestionResponse extends BaseEntity { @Column({ type: 'varchar', length: 2048, nullable: true }) Text?: string; - //Represent the response of the user @Column({ type: 'text', nullable: true }) UserResponse: string; - // @Column() - // SubmissionTimestamp: Date; - @Column({ nullable: true }) SubmissionTimestamp: Date; @@ -61,10 +55,6 @@ export class QuestionResponse extends BaseEntity { @JoinColumn({ name: 'FormSubmissionId' }) FormSubmission: FormSubmission; - // @ManyToOne(() => Question, (question) => question.Responses) - // @JoinColumn({ name: 'QuestionId' }) - // Question: Question; - @ManyToOne(() => FormFieldEntity, (formField) => formField.Responses) @JoinColumn({ name: 'FormFieldId' }) FormField: FormFieldEntity; diff --git a/src/database/sql/typeorm/models/rule/base.rule.model.ts b/src/database/sql/typeorm/models/rule/base.rule.model.ts index d279226..6398b45 100644 --- a/src/database/sql/typeorm/models/rule/base.rule.model.ts +++ b/src/database/sql/typeorm/models/rule/base.rule.model.ts @@ -1,9 +1,6 @@ -import { Entity, Column } from 'typeorm'; +import { Column } from 'typeorm'; import { BaseEntity } from '../base.entity'; - - -// Base Rule Entity (Abstract - no table) export abstract class BaseRuleEntity extends BaseEntity { @Column({ type: 'varchar', length: 255, nullable: false }) Name: string; diff --git a/src/database/sql/typeorm/models/rule/calculation.rule.model.ts b/src/database/sql/typeorm/models/rule/calculation.rule.model.ts index 5b9a88b..810326e 100644 --- a/src/database/sql/typeorm/models/rule/calculation.rule.model.ts +++ b/src/database/sql/typeorm/models/rule/calculation.rule.model.ts @@ -2,23 +2,14 @@ import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseRuleEntity } from './base.rule.model'; import { CalculationLogicEntity } from '../logic/calculation.logic.model'; -// Calculation Rule Entity @Entity({ name: 'eval_calculation_rules' }) export class CalculationRuleEntity extends BaseRuleEntity { @Column({ type: 'uuid', nullable: true }) ConditionForOperationId?: string; - // @ManyToOne(() => BaseOperationEntity, { nullable: true }) - // @JoinColumn({ name: 'ConditionForOperationId' }) - // ConditionForOperation?: BaseOperationEntity; - @Column({ type: 'uuid', nullable: false }) OperationId: string; - // @ManyToOne(() => BaseOperationEntity, { nullable: false }) - // @JoinColumn({ name: 'OperationId' }) - // Operation: BaseOperationEntity; - @Column({ type: 'uuid', nullable: true }) LogicId?: string; diff --git a/src/database/sql/typeorm/models/rule/skip.rule.model.ts b/src/database/sql/typeorm/models/rule/skip.rule.model.ts index e681ae1..6ddea9f 100644 --- a/src/database/sql/typeorm/models/rule/skip.rule.model.ts +++ b/src/database/sql/typeorm/models/rule/skip.rule.model.ts @@ -2,8 +2,6 @@ import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseRuleEntity } from './base.rule.model'; import { SkipLogicEntity } from '../logic/skip.logic.model'; - -// Skip Rule Entity @Entity({ name: 'eval_skip_rules' }) export class SkipRuleEntity extends BaseRuleEntity { @Column({ type: 'uuid', nullable: false }) diff --git a/src/database/sql/typeorm/models/rule/validation.rule.model.ts b/src/database/sql/typeorm/models/rule/validation.rule.model.ts index bee0c42..783ae39 100644 --- a/src/database/sql/typeorm/models/rule/validation.rule.model.ts +++ b/src/database/sql/typeorm/models/rule/validation.rule.model.ts @@ -2,17 +2,11 @@ import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseRuleEntity } from './base.rule.model'; import { ValidationLogicEntity } from '../logic/validation.logic.model'; - -// Validation Rule Entity @Entity({ name: 'eval_validation_rules' }) export class ValidationRuleEntity extends BaseRuleEntity { @Column({ type: 'uuid', nullable: false }) OperationId: string; - // @ManyToOne(() => BaseOperationEntity, { nullable: false }) - // @JoinColumn({ name: 'OperationId' }) - // Operation: BaseOperationEntity; - @Column({ type: 'boolean', nullable: false, default: false }) ErrorWhenFalse: boolean; diff --git a/src/database/sql/typeorm/repositories/field.logic/field.logic.calculation.repo.ts b/src/database/sql/typeorm/repositories/field.logic/field.logic.calculation.repo.ts index 5572bb9..cea06dd 100644 --- a/src/database/sql/typeorm/repositories/field.logic/field.logic.calculation.repo.ts +++ b/src/database/sql/typeorm/repositories/field.logic/field.logic.calculation.repo.ts @@ -28,7 +28,7 @@ export class CalculationLogicRepo extends BaseRepo implements ICalculationLogicR FallbackValue: model.FallbackValue }); const record = await this._calculationLogicRepo.save(data); - return CalculationLogicMapper.toCalculationLogicDto(record); + return CalculationLogicMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -62,7 +62,7 @@ export class CalculationLogicRepo extends BaseRepo implements ICalculationLogicR updateData.UpdatedAt = new Date(); const record = await this._calculationLogicRepo.save(updateData); - return CalculationLogicMapper.toCalculationLogicDto(record); + return CalculationLogicMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -76,7 +76,7 @@ export class CalculationLogicRepo extends BaseRepo implements ICalculationLogicR DeletedAt: null, }, }); - return CalculationLogicMapper.toCalculationLogicDto(record); + return CalculationLogicMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -118,7 +118,7 @@ export class CalculationLogicRepo extends BaseRepo implements ICalculationLogicR ItemsPerPage: limit, Order: order === "DESC" ? "descending" : "ascending", OrderedBy: orderByColumn, - Items: list.map((x) => CalculationLogicMapper.toCalculationLogicDto(x)), + Items: CalculationLogicMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/field.logic/field.logic.skip.repo.ts b/src/database/sql/typeorm/repositories/field.logic/field.logic.skip.repo.ts index 1a8715b..90c457f 100644 --- a/src/database/sql/typeorm/repositories/field.logic/field.logic.skip.repo.ts +++ b/src/database/sql/typeorm/repositories/field.logic/field.logic.skip.repo.ts @@ -1,4 +1,3 @@ - import { ISkipLogicRepo } from "../../../../repository.interfaces/field.logic/skip.logic/skip.logic.repo.interface"; import { SkipLogicResponseDto, @@ -18,7 +17,7 @@ import { FindManyOptions, Repository } from "typeorm"; export class SkipLogicRepo extends BaseRepo implements ISkipLogicRepo { _skipLogicRepo: Repository = Source.getRepository(SkipLogicEntity); - // Skip Logic operations + createSkipLogic = async (model: SkipLogicCreateModel): Promise => { try { const data = this._skipLogicRepo.create({ @@ -28,7 +27,7 @@ export class SkipLogicRepo extends BaseRepo implements ISkipLogicRepo { DefaultSkip: model.DefaultSkip }); const record = await this._skipLogicRepo.save(data); - return SkipLogicMapper.toSkipLogicDto(record); + return SkipLogicMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -62,7 +61,7 @@ export class SkipLogicRepo extends BaseRepo implements ISkipLogicRepo { updateData.UpdatedAt = new Date(); const record = await this._skipLogicRepo.save(updateData); - return SkipLogicMapper.toSkipLogicDto(record); + return SkipLogicMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -76,7 +75,7 @@ export class SkipLogicRepo extends BaseRepo implements ISkipLogicRepo { DeletedAt: null, }, }); - return SkipLogicMapper.toSkipLogicDto(record); + return SkipLogicMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -118,7 +117,7 @@ export class SkipLogicRepo extends BaseRepo implements ISkipLogicRepo { ItemsPerPage: limit, Order: order === "DESC" ? "descending" : "ascending", OrderedBy: orderByColumn, - Items: list.map((x) => SkipLogicMapper.toSkipLogicDto(x)), + Items: SkipLogicMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/field.logic/field.logic.validation.repo.ts b/src/database/sql/typeorm/repositories/field.logic/field.logic.validation.repo.ts index 2163761..c56202c 100644 --- a/src/database/sql/typeorm/repositories/field.logic/field.logic.validation.repo.ts +++ b/src/database/sql/typeorm/repositories/field.logic/field.logic.validation.repo.ts @@ -28,7 +28,7 @@ export class ValidationLogicRepo extends BaseRepo implements IValidationLogicRep // DefaultSkip: model.DefaultSkip }); const record = await this._validationLogicRepo.save(data); - return ValidationLogicMapper.toValidationLogicDto(record); + return ValidationLogicMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -59,7 +59,7 @@ export class ValidationLogicRepo extends BaseRepo implements IValidationLogicRep updateData.UpdatedAt = new Date(); const record = await this._validationLogicRepo.save(updateData); - return ValidationLogicMapper.toValidationLogicDto(record); + return ValidationLogicMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -73,7 +73,7 @@ export class ValidationLogicRepo extends BaseRepo implements IValidationLogicRep DeletedAt: null, }, }); - return ValidationLogicMapper.toValidationLogicDto(record); + return ValidationLogicMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -115,7 +115,7 @@ export class ValidationLogicRepo extends BaseRepo implements IValidationLogicRep ItemsPerPage: limit, Order: order === "DESC" ? "descending" : "ascending", OrderedBy: orderByColumn, - Items: list.map((x) => ValidationLogicMapper.toValidationLogicDto(x)), + Items: ValidationLogicMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/field.operations/field.operations.composition.repo.ts b/src/database/sql/typeorm/repositories/field.operations/field.operations.composition.repo.ts index 0e97d4e..1f1d816 100644 --- a/src/database/sql/typeorm/repositories/field.operations/field.operations.composition.repo.ts +++ b/src/database/sql/typeorm/repositories/field.operations/field.operations.composition.repo.ts @@ -29,7 +29,7 @@ export class CompositionOperationRepo extends BaseRepo implements ICompositionOp Operands: model.Operands }); const record = await this._compositionOperationRepo.save(data); - return CompositionOperationMapper.toCompositionOperationDto(record); + return CompositionOperationMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -66,7 +66,7 @@ export class CompositionOperationRepo extends BaseRepo implements ICompositionOp updateData.UpdatedAt = new Date(); const record = await this._compositionOperationRepo.save(updateData); - return CompositionOperationMapper.toCompositionOperationDto(record); + return CompositionOperationMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -80,7 +80,7 @@ export class CompositionOperationRepo extends BaseRepo implements ICompositionOp DeletedAt: null, }, }); - return CompositionOperationMapper.toCompositionOperationDto(record); + return CompositionOperationMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -122,7 +122,7 @@ export class CompositionOperationRepo extends BaseRepo implements ICompositionOp ItemsPerPage: limit, Order: order === "DESC" ? "descending" : "ascending", OrderedBy: orderByColumn, - Items: list.map((x) => CompositionOperationMapper.toCompositionOperationDto(x)), + Items: CompositionOperationMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/field.operations/field.operations.function.expression.repo.ts b/src/database/sql/typeorm/repositories/field.operations/field.operations.function.expression.repo.ts index 4a4f062..d5b9c0a 100644 --- a/src/database/sql/typeorm/repositories/field.operations/field.operations.function.expression.repo.ts +++ b/src/database/sql/typeorm/repositories/field.operations/field.operations.function.expression.repo.ts @@ -29,7 +29,7 @@ export class FunctionExpressionOperationRepo extends BaseRepo implements IFuncti ResultDataType: model.ResultDataType }); const record = await this._functionExpressionOperationRepo.save(data); - return FunctionExpressionOperationMapper.toFunctionExpressionOperationDto(record); + return FunctionExpressionOperationMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -70,7 +70,7 @@ export class FunctionExpressionOperationRepo extends BaseRepo implements IFuncti updateData.UpdatedAt = new Date(); const record = await this._functionExpressionOperationRepo.save(updateData); - return FunctionExpressionOperationMapper.toFunctionExpressionOperationDto(record); + return FunctionExpressionOperationMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -84,7 +84,7 @@ export class FunctionExpressionOperationRepo extends BaseRepo implements IFuncti DeletedAt: null, }, }); - return FunctionExpressionOperationMapper.toFunctionExpressionOperationDto(record); + return FunctionExpressionOperationMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -126,7 +126,7 @@ export class FunctionExpressionOperationRepo extends BaseRepo implements IFuncti ItemsPerPage: limit, Order: order === "DESC" ? "descending" : "ascending", OrderedBy: orderByColumn, - Items: list.map((x) => FunctionExpressionOperationMapper.toFunctionExpressionOperationDto(x)), + Items: FunctionExpressionOperationMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/field.operations/field.operations.iterate.repo.ts b/src/database/sql/typeorm/repositories/field.operations/field.operations.iterate.repo.ts index 6575b95..8fd5552 100644 --- a/src/database/sql/typeorm/repositories/field.operations/field.operations.iterate.repo.ts +++ b/src/database/sql/typeorm/repositories/field.operations/field.operations.iterate.repo.ts @@ -30,7 +30,7 @@ export class IterateOperationRepo extends BaseRepo implements IIterateOperationR FilterExpression: model.FilterExpression }); const record = await this._iterateOperationRepo.save(data); - return IterateOperationMapper.toIterateOperationDto(record); + return IterateOperationMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -73,7 +73,7 @@ export class IterateOperationRepo extends BaseRepo implements IIterateOperationR updateData.UpdatedAt = new Date(); const record = await this._iterateOperationRepo.save(updateData); - return IterateOperationMapper.toIterateOperationDto(record); + return IterateOperationMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -87,7 +87,7 @@ export class IterateOperationRepo extends BaseRepo implements IIterateOperationR DeletedAt: null, }, }); - return IterateOperationMapper.toIterateOperationDto(record); + return IterateOperationMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -129,7 +129,7 @@ export class IterateOperationRepo extends BaseRepo implements IIterateOperationR ItemsPerPage: limit, Order: order === "DESC" ? "descending" : "ascending", OrderedBy: orderByColumn, - Items: list.map((x) => IterateOperationMapper.toIterateOperationDto(x)), + Items: IterateOperationMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/field.operations/field.operations.logical.repo.ts b/src/database/sql/typeorm/repositories/field.operations/field.operations.logical.repo.ts index 2877023..773cbbe 100644 --- a/src/database/sql/typeorm/repositories/field.operations/field.operations.logical.repo.ts +++ b/src/database/sql/typeorm/repositories/field.operations/field.operations.logical.repo.ts @@ -29,7 +29,7 @@ export class LogicalOperationRepo extends BaseRepo implements ILogicalOperationR Operands: model.Operands }); const record = await this._logicalOperationRepo.save(data); - return LogicalOperationMapper.toLogicalOperationDto(record); + return LogicalOperationMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -66,7 +66,7 @@ export class LogicalOperationRepo extends BaseRepo implements ILogicalOperationR updateData.UpdatedAt = new Date(); const record = await this._logicalOperationRepo.save(updateData); - return LogicalOperationMapper.toLogicalOperationDto(record); + return LogicalOperationMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -80,7 +80,7 @@ export class LogicalOperationRepo extends BaseRepo implements ILogicalOperationR DeletedAt: null, }, }); - return LogicalOperationMapper.toLogicalOperationDto(record); + return LogicalOperationMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -122,7 +122,7 @@ export class LogicalOperationRepo extends BaseRepo implements ILogicalOperationR ItemsPerPage: limit, Order: order === "DESC" ? "descending" : "ascending", OrderedBy: orderByColumn, - Items: list.map((x) => LogicalOperationMapper.toLogicalOperationDto(x)), + Items: LogicalOperationMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/field.operations/field.operations.mathematical.repo.ts b/src/database/sql/typeorm/repositories/field.operations/field.operations.mathematical.repo.ts index 7b264a6..ef0030d 100644 --- a/src/database/sql/typeorm/repositories/field.operations/field.operations.mathematical.repo.ts +++ b/src/database/sql/typeorm/repositories/field.operations/field.operations.mathematical.repo.ts @@ -30,7 +30,7 @@ export class MathematicalOperationRepo extends BaseRepo implements IMathematical ResultDataType: model.ResultDataType }); const record = await this._mathematicalOperationRepo.save(data); - return MathematicalOperationMapper.toMathematicalOperationDto(record); + return MathematicalOperationMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -70,7 +70,7 @@ export class MathematicalOperationRepo extends BaseRepo implements IMathematical updateData.UpdatedAt = new Date(); const record = await this._mathematicalOperationRepo.save(updateData); - return MathematicalOperationMapper.toMathematicalOperationDto(record); + return MathematicalOperationMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -84,7 +84,7 @@ export class MathematicalOperationRepo extends BaseRepo implements IMathematical DeletedAt: null, }, }); - return MathematicalOperationMapper.toMathematicalOperationDto(record); + return MathematicalOperationMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -126,7 +126,7 @@ export class MathematicalOperationRepo extends BaseRepo implements IMathematical ItemsPerPage: limit, Order: order === "DESC" ? "descending" : "ascending", OrderedBy: orderByColumn, - Items: list.map((x) => MathematicalOperationMapper.toMathematicalOperationDto(x)), + Items: MathematicalOperationMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/field.rules/field.rules.calculation.repo.ts b/src/database/sql/typeorm/repositories/field.rules/field.rules.calculation.repo.ts index 0f99a3c..dd0cfa5 100644 --- a/src/database/sql/typeorm/repositories/field.rules/field.rules.calculation.repo.ts +++ b/src/database/sql/typeorm/repositories/field.rules/field.rules.calculation.repo.ts @@ -24,7 +24,7 @@ export class CalculationRuleRepo extends BaseRepo implements ICalculationRuleRep LogicId: model.LogicId }); const record = await this._calculationRuleRepo.save(data); - return CalculationRuleMapper.toCalculationRuleDto(record); + return CalculationRuleMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -38,7 +38,7 @@ export class CalculationRuleRepo extends BaseRepo implements ICalculationRuleRep DeletedAt: null } }); - return CalculationRuleMapper.toCalculationRuleDto(record); + return CalculationRuleMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -72,7 +72,7 @@ export class CalculationRuleRepo extends BaseRepo implements ICalculationRuleRep updateData.LogicId = model.LogicId; } var record = await this._calculationRuleRepo.save(updateData); - return CalculationRuleMapper.toCalculationRuleDto(record); + return CalculationRuleMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -111,7 +111,7 @@ export class CalculationRuleRepo extends BaseRepo implements ICalculationRuleRep ItemsPerPage: limit, Order: order === 'DESC' ? 'descending' : 'ascending', OrderedBy: orderByColumn, - Items: list.map(x => CalculationRuleMapper.toCalculationRuleDto(x)), + Items: CalculationRuleMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/field.rules/field.rules.skip.repo.ts b/src/database/sql/typeorm/repositories/field.rules/field.rules.skip.repo.ts index eb87b59..a45d805 100644 --- a/src/database/sql/typeorm/repositories/field.rules/field.rules.skip.repo.ts +++ b/src/database/sql/typeorm/repositories/field.rules/field.rules.skip.repo.ts @@ -37,7 +37,6 @@ export class SkipRuleRepo extends BaseRepo implements ISkipRuleRepo { id: id, DeletedAt: null }, - // relations: ['Operation', 'Logic'] }); return SkipRuleMapper.toDto(record); } catch (error) { diff --git a/src/database/sql/typeorm/repositories/field.rules/field.rules.validation.repo.ts b/src/database/sql/typeorm/repositories/field.rules/field.rules.validation.repo.ts index c64804c..e9212e2 100644 --- a/src/database/sql/typeorm/repositories/field.rules/field.rules.validation.repo.ts +++ b/src/database/sql/typeorm/repositories/field.rules/field.rules.validation.repo.ts @@ -25,7 +25,7 @@ export class ValidationRuleRepo extends BaseRepo implements IValidationRuleRepo LogicId: model.LogicId }); const record = await this._validationRuleRepo.save(data); - return ValidationRuleMapper.toValidationRuleDto(record); + return ValidationRuleMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -38,9 +38,8 @@ export class ValidationRuleRepo extends BaseRepo implements IValidationRuleRepo id: id, DeletedAt: null }, - // relations: ['Operation', 'Logic'] }); - return ValidationRuleMapper.toValidationRuleDto(record); + return ValidationRuleMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -83,7 +82,7 @@ export class ValidationRuleRepo extends BaseRepo implements IValidationRuleRepo updateData.LogicId = model.LogicId; } var record = await this._validationRuleRepo.save(updateData); - return ValidationRuleMapper.toValidationRuleDto(record); + return ValidationRuleMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -122,7 +121,7 @@ export class ValidationRuleRepo extends BaseRepo implements IValidationRuleRepo ItemsPerPage: limit, Order: order === 'DESC' ? 'descending' : 'ascending', OrderedBy: orderByColumn, - Items: list.map(x => ValidationRuleMapper.toValidationRuleDto(x)), + Items: ValidationRuleMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/form.field/form.field.repo.ts b/src/database/sql/typeorm/repositories/form.field/form.field.repo.ts index 39dd226..54e4a16 100644 --- a/src/database/sql/typeorm/repositories/form.field/form.field.repo.ts +++ b/src/database/sql/typeorm/repositories/form.field/form.field.repo.ts @@ -19,11 +19,8 @@ export class FormFieldRepo extends BaseRepo implements IFormFieldRepo { create = async (model: FormFieldCreateModel): Promise => { try { - // let jsonData: Prisma.JsonValue | undefined; - let jsonData: FormFieldOption[] | undefined; - // Map model.Options to the appropriate structure for JSON storage if (model.Options && model.Options.length > 0) { jsonData = model.Options.map((option) => ({ Text: option.Text, @@ -40,17 +37,13 @@ export class FormFieldRepo extends BaseRepo implements IFormFieldRepo { DisplayCode: model.DisplayCode, ResponseType: model.ResponseType as QueryResponseType, Score: model.Score, - // CorrectAnswer: model.CorrectAnswer, IsRequired: model.IsRequired, Hint: model.Hint, Sequence: model.Sequence, - Options: JSON.stringify(jsonData), // Only assign if jsonData is defined - // QuestionImageUrl: model.QuestionImageUrl, + Options: JSON.stringify(jsonData), RangeMax: model.RangeMax, RangeMin: model.RangeMin, CreatedAt: new Date(), - // UpdatedAt: new Date(), // Uncomment and modify as needed - // DeletedAt: null, // Uncomment and modify as needed }); const record = await this._formFieldRepo.save(data); return FormFieldMapper.toDto(record); @@ -62,11 +55,8 @@ export class FormFieldRepo extends BaseRepo implements IFormFieldRepo { update = async (id: string, model: FormFieldUpdateModel): Promise => { try { - // let jsonData: Prisma.JsonValue | undefined; - let jsonData: FormFieldOption[] | undefined; - // Map model.Options to the appropriate structure for JSON storage if (model.Options && model.Options.length > 0) { jsonData = model.Options.map((option) => ({ Text: option.Text, @@ -85,9 +75,6 @@ export class FormFieldRepo extends BaseRepo implements IFormFieldRepo { if (!updateData) { ErrorHandler.throwNotFoundError("FormField Data not found!"); } - // if (model.SectionIdentifier) { - // updateData.SectionIdentifier = model.SectionIdentifier; - // } if (model.Title) { updateData.Title = model.Title; } @@ -102,10 +89,6 @@ export class FormFieldRepo extends BaseRepo implements IFormFieldRepo { updateData.Score = model.Score; } - // if (model.CorrectAnswer) { - // updateData.CorrectAnswer = model.CorrectAnswer; - // } - if (model.IsRequired) { updateData.IsRequired = model.IsRequired; } @@ -116,10 +99,6 @@ export class FormFieldRepo extends BaseRepo implements IFormFieldRepo { updateData.Options = JSON.stringify(jsonData); - // if (model.QuestionImageUrl) { - // updateData.QuestionImageUrl = model.QuestionImageUrl; - // } - if (model.RangeMax) { updateData.RangeMax = model.RangeMax; } @@ -171,7 +150,7 @@ export class FormFieldRepo extends BaseRepo implements IFormFieldRepo { ItemsPerPage: records.length, Order: 'ASC', OrderedBy: 'CreatedAt', - Items: records.map((item) => FormFieldMapper.toDto(item)), + Items: FormFieldMapper.toArrayDto(records), }; return searchResults; @@ -191,11 +170,11 @@ export class FormFieldRepo extends BaseRepo implements IFormFieldRepo { }, }); if (!record) { - return false; // Record not found + return false; } - record.DeletedAt = new Date(); // Soft delete + record.DeletedAt = new Date(); await this._formFieldRepo.save(record); - return true; // Soft delete successful + return true; } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -215,7 +194,7 @@ export class FormFieldRepo extends BaseRepo implements IFormFieldRepo { ItemsPerPage: limit, Order: order, OrderedBy: orderByColumn, - Items: list.map((item) => FormFieldMapper.toDto(item)), + Items: FormFieldMapper.toArrayDto(list), }; return searchResults; diff --git a/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts b/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts index 99843ce..cdf6aab 100644 --- a/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts +++ b/src/database/sql/typeorm/repositories/form.section/form.section.repo.ts @@ -15,25 +15,8 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo { _formSectionRepo: Repository = Source.getRepository(FormSection); create = async (model: FormSectionCreateModel): Promise => { - - // const entity={ - // ParentFormTemplate: { - // connect: { id: model.ParentFormTemplateId } - // }, - // SectionIdentifier: model.SectionIdentifier, - // Title: model.Title, - // Description: model.Description, - // DisplayCode: model.DisplayCode, - // Sequence: model.Sequence, - // ParentSectionId: model.ParentSectionId, - // } - try { const data = await this._formSectionRepo.create({ - // FormTemplate: { - // connect: { id: model.ParentFormTemplateId } - // }, - // SectionIdentifier: model.SectionIdentifier, FormTemplateId: model.ParentFormTemplateId, Title: model.Title, Description: model.Description, @@ -60,9 +43,6 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo { if (!updateData) { ErrorHandler.throwNotFoundError('Form Section Data not found!'); } - // if (model.SectionIdentifier) { - // updateData.SectionIdentifier = model.SectionIdentifier; - // } if (model.Title) { updateData.Title = model.Title; } @@ -111,11 +91,11 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo { }, }); if (!record) { - return false; // Record not found + return false; } - record.DeletedAt = new Date(); // Soft delete + record.DeletedAt = new Date(); await this._formSectionRepo.save(record); - return true; // Soft delete successful + return true; } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -149,7 +129,7 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo { ItemsPerPage: limit, Order: order === 'DESC' ? 'descending' : 'ascending', OrderedBy: orderByColumn, - Items: list.map(x => FormSectionMapper.toDto(x)), + Items: FormSectionMapper.toArrayDto(list), }; return searchResults; } catch (error) { @@ -173,12 +153,6 @@ export class FormSectionRepo extends BaseRepo implements IFormSectionRepo { search.where['FormTemplateId'] = filters.parentFormTemplateId; } - // if (filters.sectionIdentifier) { - // where.SectionIdentifier = { - // equals: filters.sectionIdentifier, - // }; - // } - if (filters.title) { search.where['Title'] = filters.title; } diff --git a/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts b/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts index 24bff75..612194e 100644 --- a/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts +++ b/src/database/sql/typeorm/repositories/form.submission/form.submission.repo.ts @@ -4,12 +4,12 @@ import { FormSubmissionDto } from "../../../../../domain.types/forms/form.submis import { FormSubmission } from "../../models/form.submission/form.submission.model"; import { FormStatus } from "../../../../../domain.types/forms/form.submission.enums"; import { Source } from "../../database.connector.typeorm"; -import { FormMapper } from "../../mappers/form.submission.mapper"; import { ErrorHandler } from "../../../../../common/handlers/error.handler"; import { Logger } from "../../../../../common/logger"; import { FindManyOptions, Repository } from "typeorm"; import { uuid } from "../../../../../domain.types/miscellaneous/system.types"; import { BaseRepo } from "../base.repo"; +import { FormSubmissionMapper } from "../../mappers/form.submission.mapper"; export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo { @@ -18,13 +18,6 @@ export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo create = async (model: FormSubmissionCreateModel): Promise => { try { const data = await this._formSubmissionRepo.create({ - // FormTemplate: { - // connect: { id: model.ParentFormTemplateId } - // }, - // SectionIdentifier: model.SectionIdentifier, - // FormTemplate: { - // connect: { id: model.FormTemplateId } - // }, FormTemplateId: model.FormTemplateId, Title: model.Title, UserId: model.UserId, @@ -33,7 +26,7 @@ export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo }); const record = await this._formSubmissionRepo.save(data); - return FormMapper.toDto(record); + return FormSubmissionMapper.toDto(record); } catch (error) { @@ -53,9 +46,6 @@ export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo if (!updateData) { ErrorHandler.throwNotFoundError('Form Section Data not found!'); } - // if (model.SectionIdentifier) { - // updateData.SectionIdentifier = model.SectionIdentifier; - // } if (model.UserId) { updateData.UserId = model.UserId; } @@ -70,10 +60,6 @@ export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo updateData.Link = model.Link; } - // if (model.QueryParams) { - // updateData.QueryParams = model.QueryParams; - // } - if (model.LinkQueryParams) { updateData.LinkQueryParams = model.LinkQueryParams; } @@ -86,16 +72,8 @@ export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo updateData.SubmittedAt = model.SubmittedAt; } - // if (model.Status) { - // updateData.Status = model.Status; - // } - - // if (model.Category) { - // updateData.Category = model.Category; - // } - var record = await this._formSubmissionRepo.save(updateData); - return FormMapper.toDto(record); + return FormSubmissionMapper.toDto(record); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); @@ -110,7 +88,7 @@ export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo DeletedAt: null, }, }); - return FormMapper.toDto(record); + return FormSubmissionMapper.toDto(record); } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -126,11 +104,11 @@ export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo }, }); if (!record) { - return false; // Record not found + return false; } - record.DeletedAt = new Date(); // Soft delete + record.DeletedAt = new Date(); await this._formSubmissionRepo.save(record); - return true; // Soft delete successful + return true; } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); @@ -154,7 +132,7 @@ export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo record.UpdatedAt = new Date(); var submittedData = await this._formSubmissionRepo.save(record); - return FormMapper.toDto(submittedData); + return FormSubmissionMapper.toDto(submittedData); } catch (error) { ErrorHandler.throwInternalServerError(error.message, 500); } @@ -173,7 +151,7 @@ export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo ItemsPerPage: limit, Order: order === 'DESC' ? 'descending' : 'ascending', OrderedBy: orderByColumn, - Items: list.map(x => FormMapper.toDto(x)), + Items: FormSubmissionMapper.toArrayDto(list), }; return searchResults; } catch (error) { @@ -212,8 +190,6 @@ export class FormSubmissionRepo extends BaseRepo implements IFormSubmissionRepo if (filters.SubmittedAt) { search.where["SubmittedAt"] = filters.SubmittedAt; } - - return search; }; diff --git a/src/database/sql/typeorm/repositories/form.template.approval/form.template.approval.repo.ts b/src/database/sql/typeorm/repositories/form.template.approval/form.template.approval.repo.ts index 9a385c5..d642ec5 100644 --- a/src/database/sql/typeorm/repositories/form.template.approval/form.template.approval.repo.ts +++ b/src/database/sql/typeorm/repositories/form.template.approval/form.template.approval.repo.ts @@ -103,12 +103,12 @@ export class FormTemplateApprovalRepo extends BaseRepo implements IFormTemplateA }); if (!record) { - return false; // Record not found + return false; } - record.DeletedAt = new Date(); // Soft delete + record.DeletedAt = new Date(); await this._formTemplateApprovalRepo.save(record); - return true; // Soft delete successful + return true; } catch (error) { Logger.instance().log(error.message); ErrorHandler.throwInternalServerError(error.message, 500); diff --git a/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts b/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts index 80bff0f..16df3b1 100644 --- a/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts +++ b/src/database/sql/typeorm/repositories/form.template/form.template.repo.ts @@ -11,8 +11,6 @@ import { ErrorHandler } from "../../../../../common/handlers/error.handler"; import { Logger } from "../../../../../common/logger"; import { BaseRepo } from "../base.repo"; import { FindManyOptions, IsNull, Repository } from "typeorm"; -// import { Question } from "../../models/question/question.model"; -// import { QuestionMapper } from "../../mappers/question.mapper"; import { FormSection } from "../../models/form.section/form.section.model"; import { FormFieldEntity } from "../../models/form.field/form.field.model"; import { FormFieldMapper } from "../../mappers/form.field.mapper"; @@ -27,18 +25,13 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { create = async (model: FormTemplateCreateModel): Promise => { - try { const data = this._formTemplateRepo.create({ Title: model.Title, Description: model.Description, - // CurrentVersion: model.CurrentVersion, - // TenantCode: model.TenantCode, Type: model.Type as FormType, - // ItemsPerPage: model.ItemsPerPage, DisplayCode: model.DisplayCode, - // OwnerUserId: model.OwnerUserId, RootSectionId: model.RootSectionId, DefaultSectionNumbering: model.DefaultSectionNumbering, }); @@ -66,43 +59,16 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { if (!updateData) { ErrorHandler.throwNotFoundError("Form Section Data not found!"); } - // if (model.SectionIdentifier) { - // updateData.SectionIdentifier = model.SectionIdentifier; - // } if (model.Title) { updateData.Title = model.Title; } if (model.Description) { updateData.Description = model.Description; } - // if (model.CurrentVersion) { - // updateData.CurrentVersion = model.CurrentVersion; - // } - - // if (model.TenantCode) { - // updateData.TenantCode = model.TenantCode; - // } - - // if (model.QueryParams) { - // updateData.QueryParams = model.QueryParams; - // } if (model.Type) { updateData.Type = model.Type; } - - // if (model.ItemsPerPage) { - // updateData.ItemsPerPage = model.ItemsPerPage; - // } - - // if (model.Status) { - // updateData.Status = model.Status; - // } - - // if (model.Category) { - // updateData.Category = model.Category; - // } - updateData.UpdatedAt = new Date(); var record = await this._formTemplateRepo.save(updateData); @@ -210,12 +176,8 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { id: template.id, Title: template.Title, Description: template.Description, - // CurrentVersion: template.CurrentVersion, - // TenantCode: template.TenantCode, Type: template.Type as FormType, - // ItemsPerPage: template.ItemsPerPage, DisplayCode: template.DisplayCode, - // OwnerUserId: template.OwnerUserId, RootSectionId: template.RootSectionId, DefaultSectionNumbering: template.DefaultSectionNumbering, CreatedAt: template.CreatedAt, @@ -224,14 +186,7 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { }; // Fetch all top-level sections related to the template - // const sections = await this._formTemplateRepo.find({ - // where: { - // ParentFormTemplateId: id, - // ParentSectionId: null, - // DeletedAt: null, - // }, - // include: { ParentFormTemplate: true }, - // }); + const sections = await this._formSection.find({ where: { @@ -245,11 +200,9 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { for (const section of sections) { const dtoSection: SectionDto = { id: section.id, - // SectionIdentifier: section.SectionIdentifier, Title: section.Title, Description: section.Description, DisplayCode: section.DisplayCode, - // Sequence: section.Sequence, ParentSectionId: section.ParentSectionId, CreatedAt: section.CreatedAt, UpdatedAt: section.UpdatedAt, @@ -273,7 +226,6 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { for (const subsection of subsections) { const dtoSubsection: SubsectionDto = { id: subsection.id, - // SectionIdentifier: subsection.SectionIdentifier, Title: subsection.Title, Description: subsection.Description, DisplayCode: subsection.DisplayCode, @@ -322,12 +274,8 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { id: template.id, Title: template.Title, Description: template.Description, - // CurrentVersion: template.CurrentVersion, - // TenantCode: template.TenantCode, Type: template.Type as FormType, - // ItemsPerPage: template.ItemsPerPage, DisplayCode: template.DisplayCode, - // OwnerUserId: template.OwnerUserId, RootSectionId: template.RootSectionId, DefaultSectionNumbering: template.DefaultSectionNumbering, CreatedAt: template.CreatedAt, @@ -335,15 +283,7 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { RootSection: [], }; - // const sections = await this.prisma.formSection.findMany({ - // where: { ParentFormTemplateId: id, DeletedAt: null }, - // include: { - // ParentFormTemplate: true, - // Questions: true - // } - // }) - // console.log('****', JSON.stringify(sections)) const rootSection = await this._formSection.findOne({ where: { FormTemplateId: id, @@ -375,11 +315,7 @@ export class FormTemplateRepo extends BaseRepo implements IFormTemplateRepo { }, }, }); - // return allSections; - return await this.mapSections1(allSections); - - console.log("****", JSON.stringify(allSections)); - + return const mapSections = async ( parentId: string | null ): Promise => { diff --git a/src/database/sql/typeorm/repositories/input.unit.list/input.unit.list.repo.ts b/src/database/sql/typeorm/repositories/input.unit.list/input.unit.list.repo.ts index 24d9810..984b6c8 100644 --- a/src/database/sql/typeorm/repositories/input.unit.list/input.unit.list.repo.ts +++ b/src/database/sql/typeorm/repositories/input.unit.list/input.unit.list.repo.ts @@ -102,7 +102,7 @@ export class InputUnitListRepo extends BaseRepo implements IInputUnitListRepo { ItemsPerPage: limit, Order: order === "DESC" ? "descending" : "ascending", OrderedBy: orderByColumn, - Items: list.map((x) => InputUnitListMapper.toDto(x)), + Items: InputUnitListMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts b/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts index be9a02a..6005d2f 100644 --- a/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts +++ b/src/database/sql/typeorm/repositories/question.response/question.response.repo.ts @@ -11,8 +11,6 @@ import { ErrorHandler } from "../../../../../common/handlers/error.handler"; import { ResponseMapper } from "../../mappers/question.response.mapper"; import * as fs from 'fs'; import { FindManyOptions, Repository } from "typeorm"; -// import { QuestionMapper } from "../../mappers/question.mapper"; -// import { Question } from "../../models/question/question.model"; import PDFDocument from 'pdfkit'; import { BaseRepo } from "../base.repo"; import path from "path"; @@ -85,9 +83,6 @@ export class ResponseRepo extends BaseRepo implements IResponseRepo { if (!updateData) { ErrorHandler.throwNotFoundError('Question Response Data not found!'); } - // if (model.SectionIdentifier) { - // updateData.SectionIdentifier = model.SectionIdentifier; - // } if (model.ResponseType) { updateData.ResponseType = model.ResponseType; } @@ -102,10 +97,6 @@ export class ResponseRepo extends BaseRepo implements IResponseRepo { updateData.BooleanValue = model.BooleanValue; } - // if (model.QueryParams) { - // updateData.QueryParams = model.QueryParams; - // } - if (model.DateTimeValue) { updateData.DateTimeValue = model.DateTimeValue; } @@ -126,14 +117,6 @@ export class ResponseRepo extends BaseRepo implements IResponseRepo { updateData.UpdatedAt = new Date(); - // if (model.Status) { - // updateData.Status = model.Status; - // } - - // if (model.Category) { - // updateData.Category = model.Category; - // } - var record = await this._responseRepo.save(updateData); return ResponseMapper.toDto(record); } @@ -185,11 +168,11 @@ export class ResponseRepo extends BaseRepo implements IResponseRepo { }); if (!record) { - return false; // Record not found + return false; } - record.DeletedAt = new Date(); // Soft delete + record.DeletedAt = new Date(); await this._responseRepo.save(record); - return true; // Soft delete successful + return true; } catch (error) { Logger.instance().log(error.message); @@ -203,7 +186,6 @@ export class ResponseRepo extends BaseRepo implements IResponseRepo { var prompts = await this._responseRepo.find(); for (var i of prompts) { const record = ResponseMapper.toDto(i); - // const record = i; data.push(record); } return data; @@ -292,7 +274,7 @@ export class ResponseRepo extends BaseRepo implements IResponseRepo { ItemsPerPage: limit, Order: order === 'DESC' ? 'descending' : 'ascending', OrderedBy: orderByColumn, - Items: list.map(x => ResponseMapper.toDto(x)), + Items: ResponseMapper.toArrayDto(list), }; return searchResults; diff --git a/src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts b/src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts index 1434354..9e9488e 100644 --- a/src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts +++ b/src/database/sql/typeorm/repositories/template.folder/template.folder.repo.ts @@ -103,7 +103,7 @@ export class TemplateFolderRepo extends BaseRepo implements ITemplateFolderRepo ItemsPerPage: limit, Order: order === "DESC" ? "descending" : "ascending", OrderedBy: orderByColumn, - Items: list.map((x) => TemplateFolderMapper.toDto(x)), + Items: TemplateFolderMapper.toArrayDto(list), }; return searchResults; } catch (error) { diff --git a/src/database/sql/typeorm/repositories/user/user.repo.ts b/src/database/sql/typeorm/repositories/user/user.repo.ts index 10844e0..847d0fd 100644 --- a/src/database/sql/typeorm/repositories/user/user.repo.ts +++ b/src/database/sql/typeorm/repositories/user/user.repo.ts @@ -11,9 +11,9 @@ import { ErrorHandler } from "../../../../../common/handlers/error.handler"; import { FindManyOptions, Repository } from "typeorm"; import { BaseRepo } from "../base.repo"; -export class UserRepo extends BaseRepo implements IUserRepo{ +export class UserRepo extends BaseRepo implements IUserRepo { - _userRepo : Repository = Source.getRepository(User); + _userRepo: Repository = Source.getRepository(User); allUsers = async () => { const response = await this._userRepo.find({ @@ -25,152 +25,150 @@ export class UserRepo extends BaseRepo implements IUserRepo{ }; - create=async (model: UserCreateModel) : Promise => { - try { - const data = this._userRepo.create({ - FirstName: model.FirstName, - LastName: model.LastName, - CountryCode: model.CountryCode, - Phone: model.Phone, - Email: model.Email, - Username: model.Username, - Password: model.Password, - }); - const record = await this._userRepo.save(data); - return UserMapper.toDto(record); - } catch (error) { - ErrorHandler.throwInternalServerError(error.message, 500); - } + create = async (model: UserCreateModel): Promise => { + try { + const data = this._userRepo.create({ + FirstName: model.FirstName, + LastName: model.LastName, + CountryCode: model.CountryCode, + Phone: model.Phone, + Email: model.Email, + Username: model.Username, + Password: model.Password, + }); + const record = await this._userRepo.save(data); + return UserMapper.toDto(record); + } catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } }; - - update=async (id: string, model: UserUpdateModel) : Promise => { + + update = async (id: string, model: UserUpdateModel): Promise => { try { - - const updateData = await this._userRepo.findOne({ - where : { - id : id, - DeletedAt: null, - }, - }); - - - if (!updateData) { - ErrorHandler.throwNotFoundError('Question Response Data not found!'); - } - - if (model.FirstName) { - updateData.FirstName = model.FirstName; - } - if ( model.LastName) { - updateData.LastName = model.LastName; - } - if (model.CountryCode) { - updateData.CountryCode = model.CountryCode; - } - - if (model.Phone) { - updateData.Phone = model.Phone; - } - - if (model.Email) { - updateData.Email = model.Email; - } - - if (model.Username) { - updateData.Username = model.Username; - } - - if (model.Password) { - updateData.Password = model.Password; - } - - updateData.UpdatedAt=new Date(); - - - var record = await this._userRepo.save(updateData); - return UserMapper.toDto(record); - } - catch (error) - { - ErrorHandler.throwInternalServerError(error.message, 500); - } + + const updateData = await this._userRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + + if (!updateData) { + ErrorHandler.throwNotFoundError('Question Response Data not found!'); + } + + if (model.FirstName) { + updateData.FirstName = model.FirstName; + } + if (model.LastName) { + updateData.LastName = model.LastName; + } + if (model.CountryCode) { + updateData.CountryCode = model.CountryCode; + } + + if (model.Phone) { + updateData.Phone = model.Phone; + } + + if (model.Email) { + updateData.Email = model.Email; + } + + if (model.Username) { + updateData.Username = model.Username; + } + + if (model.Password) { + updateData.Password = model.Password; + } + + updateData.UpdatedAt = new Date(); + + + var record = await this._userRepo.save(updateData); + return UserMapper.toDto(record); + } + catch (error) { + ErrorHandler.throwInternalServerError(error.message, 500); + } }; - - getById=async (id: string) : Promise => { + + getById = async (id: string): Promise => { try { - var record = await this._userRepo.findOne({ - where: { - id: id, - DeletedAt: null, - }, - }); - return UserMapper.toDto(record); - } - - catch (error) { - Logger.instance().log(error.message); - ErrorHandler.throwInternalServerError(error.message, 500); - } + var record = await this._userRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + return UserMapper.toDto(record); + } + + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } }; - - delete=async (id: string) : Promise => { - try { - var record = await this._userRepo.findOne({ - where : { - id : id, - DeletedAt: null, - }, - }); - - if (!record) { - return false; // Record not found - } - record.DeletedAt = new Date(); // Soft delete - await this._userRepo.save(record); - return true; // Soft delete successful - } - catch (error) - { - Logger.instance().log(error.message); - ErrorHandler.throwInternalServerError(error.message, 500); - } + + delete = async (id: string): Promise => { + try { + var record = await this._userRepo.findOne({ + where: { + id: id, + DeletedAt: null, + }, + }); + + if (!record) { + return false; + } + record.DeletedAt = new Date(); + await this._userRepo.save(record); + return true; + } + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwInternalServerError(error.message, 500); + } }; - - search=async (filters: UserSearchFilters): Promise => { - try { - var search = this.getSearchModel(filters); - var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); - const [list, count] = await this._userRepo.findAndCount(search); - - const searchResults = { - TotalCount : count, - RetrievedCount : list.length, - PageIndex : pageIndex, - ItemsPerPage : limit, - Order : order === 'DESC' ? 'descending' : 'ascending', - OrderedBy : orderByColumn, - Items : list.map(x => UserMapper.toDto(x)), - }; - - return searchResults; - } - - catch (error) { - Logger.instance().log(error.message); - ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); - } + + search = async (filters: UserSearchFilters): Promise => { + try { + var search = this.getSearchModel(filters); + var { search, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination(search, filters); + const [list, count] = await this._userRepo.findAndCount(search); + + const searchResults = { + TotalCount: count, + RetrievedCount: list.length, + PageIndex: pageIndex, + ItemsPerPage: limit, + Order: order === 'DESC' ? 'descending' : 'ascending', + OrderedBy: orderByColumn, + Items: UserMapper.toArrayDto(list), + }; + + return searchResults; + } + + catch (error) { + Logger.instance().log(error.message); + ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); + } }; - private getSearchModel = (filters: UserSearchFilters) => { - - var search: FindManyOptions = { - relations: {}, - where: {}, - }; - - if (filters.firstName) { - search.where["FirstName"] = filters.firstName; - }; + private getSearchModel = (filters: UserSearchFilters) => { + + var search: FindManyOptions = { + relations: {}, + where: {}, + }; + + if (filters.firstName) { + search.where["FirstName"] = filters.firstName; + }; if (filters.lastName) { search.where["LastName"] = filters.lastName; @@ -189,9 +187,9 @@ export class UserRepo extends BaseRepo implements IUserRepo{ } if (filters.password) { - search.where["Password"] = filters.password; + search.where["Password"] = filters.password; } return search; - }; + }; } \ No newline at end of file diff --git a/src/database/sql/typeorm/typeorm.injector.ts b/src/database/sql/typeorm/typeorm.injector.ts index 3a0faa5..a9de5fc 100644 --- a/src/database/sql/typeorm/typeorm.injector.ts +++ b/src/database/sql/typeorm/typeorm.injector.ts @@ -1,6 +1,5 @@ import 'reflect-metadata'; import { DependencyContainer } from 'tsyringe'; -// import { DatabaseConnector_TypeOrm } from './database.connector.typeorm'; import { FormSectionRepo } from './repositories/form.section/form.section.repo'; import { FormSubmissionRepo } from './repositories/form.submission/form.submission.repo'; import { FormTemplateRepo } from './repositories/form.template/form.template.repo'; @@ -23,16 +22,12 @@ import { SkipRuleRepo } from './repositories/field.rules/field.rules.skip.repo'; import { CalculationRuleRepo } from './repositories/field.rules/field.rules.calculation.repo'; import { ValidationRuleRepo } from './repositories/field.rules/field.rules.validation.repo'; - - //////////////////////////////////////////////////////////////////////////////// export class TypeOrmInjector { static registerInjections(container: DependencyContainer) { - // container.register('IPrimaryDatabaseConnector', DatabaseConnector_TypeOrm); - container.register('IFormSectionRepo', FormSectionRepo); container.register('IFormSubmissionRepo', FormSubmissionRepo); container.register('IFormTemplateRepo', FormTemplateRepo); @@ -55,7 +50,5 @@ export class TypeOrmInjector { container.register('ISkipRuleRepo', SkipRuleRepo); container.register('ICalculationRuleRepo', CalculationRuleRepo); container.register('IValidationRuleRepo', ValidationRuleRepo); - - } } diff --git a/src/domain.types/forms/calculation.logic.domain.types.ts b/src/domain.types/forms/calculation.logic.domain.types.ts index fd32d73..3b92535 100644 --- a/src/domain.types/forms/calculation.logic.domain.types.ts +++ b/src/domain.types/forms/calculation.logic.domain.types.ts @@ -1,10 +1,8 @@ import { BaseLogicCreateModel, BaseLogicResponseDto, BaseLogicUpdateModel, CalculationRuleResponseDto } from "./logic.domain.types"; import { LogicType } from "./logic.enums"; - -// Calculation Logic DTOs export interface CalculationLogicCreateModel extends BaseLogicCreateModel { Type: LogicType.Calculation; - FieldId: string; // UUID foreign key to FormFieldEntity + FieldId: string; Enabled?: boolean; DefaultSkip?: boolean; FallbackValue?: string; @@ -12,7 +10,7 @@ export interface CalculationLogicCreateModel extends BaseLogicCreateModel { export interface CalculationLogicUpdateModel extends BaseLogicUpdateModel { Type?: LogicType.Calculation; - FieldId?: string; // UUID foreign key to FormFieldEntity + FieldId?: string; Enabled?: boolean; DefaultSkip?: boolean; FallbackValue?: string; diff --git a/src/domain.types/forms/form.section.domain.types.ts b/src/domain.types/forms/form.section.domain.types.ts index 7df732a..f130281 100644 --- a/src/domain.types/forms/form.section.domain.types.ts +++ b/src/domain.types/forms/form.section.domain.types.ts @@ -6,7 +6,6 @@ export interface FormSectionCreateModel { Title?: string; Description?: string; DisplayCode: string; - // Sequence?: string; Sequence?: number; ParentSectionId?: string; } @@ -23,17 +22,6 @@ export interface FormSectionUpdateModel { export interface FormSectionResponseDto { id: string; FormTemplateId: string; - // ParentFormTemplate?: { - // id: string; - // Title: string; - // Description: string; - // CurrentVersion: number; - // Type: string; - // DisplayCode: string; - // OwnerUserId: string; - // RootSectionId: string; - // DefaultSectionNumbering: Boolean - // } SectionIdentifier: string; Title: string; Description: string; diff --git a/src/domain.types/forms/form.submission.enums.ts b/src/domain.types/forms/form.submission.enums.ts index 24293e0..89ab0b2 100644 --- a/src/domain.types/forms/form.submission.enums.ts +++ b/src/domain.types/forms/form.submission.enums.ts @@ -1,8 +1,7 @@ -// Form Submission Enums export enum FormStatus { LinkShared = 'LinkShared', Saved = 'Saved', - InProgress='InProgress', + InProgress = 'InProgress', LinkExpired = 'LinkExpired', Submitted = 'Submitted' } \ No newline at end of file diff --git a/src/domain.types/forms/form.template.domain.types.ts b/src/domain.types/forms/form.template.domain.types.ts index 64a709f..1500798 100644 --- a/src/domain.types/forms/form.template.domain.types.ts +++ b/src/domain.types/forms/form.template.domain.types.ts @@ -1,6 +1,3 @@ -// import { FormType } from "../miscellaneous/system.types"; - -// import { FormType, ItemsPerPage, QueryResponseType } from "@prisma/client"; import { FormType } from "./form.template.enums"; import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; import { uuid } from "../miscellaneous/system.types"; @@ -12,7 +9,6 @@ export interface FormTemplateCreateModel { CurrentVersion?: number; TenantCode?: string; Type: FormType; - // ItemsPerPage: ItemsPerPage; DisplayCode?: string; OwnerUserId?: string; RootSectionId?: string; @@ -25,7 +21,6 @@ export interface FormTemplateUpdateModel { CurrentVersion?: number; TenantCode?: string; Type?: FormType; - // ItemsPerPage?: ItemsPerPage; DisplayCode?: string; OwnerUserId?: string; RootSectionId?: string; @@ -39,7 +34,6 @@ export interface FormTemplateResponseDto { CurrentVersion?: number; TenantCode?: string; Type: FormType; - // ItemsPerPage: ItemsPerPage; DisplayCode: string; OwnerUserId?: string; RootSectionId: string; @@ -111,7 +105,6 @@ export interface SubsectionDto { Title: string; Description: string; DisplayCode: string; - // Sequence: string; Sequence: number; ParentSectionId: string; CreatedAt: Date; @@ -124,7 +117,6 @@ export interface QuestionDto { Title: string; Description?: string; DisplayCode: string | null; - // ResponseType: QueryResponseType; Score: number; Sequence: string; CorrectAnswer: string; @@ -137,39 +129,12 @@ export interface QuestionDto { UpdatedAt?: Date; } - - -// { -// Template: { -// Sections: [ -// { -// Subsections:[ -// { -// Questions: [] -// }, -// { -// Questions: [] -// }, -// { -// Questions: [] -// } -// ] -// } -// ], -// Questions: [] -// }, - -// ] -// } -// } - export interface SectionPreviewDto { id: string; SectionIdentifier?: string; Title: string; Description: string; DisplayCode: string; - // Sequence: string; Sequence: number; ParentSectionId: string | null; CreatedAt: Date; @@ -185,7 +150,6 @@ export interface TemplatePreviewDto { CurrentVersion?: number; TenantCode?: string; Type: FormType; - // ItemsPerPage: ItemsPerPage; DisplayCode: string; OwnerUserId?: string; RootSectionId: string; diff --git a/src/domain.types/forms/form.template.enums.ts b/src/domain.types/forms/form.template.enums.ts index b2dc52c..531dfbf 100644 --- a/src/domain.types/forms/form.template.enums.ts +++ b/src/domain.types/forms/form.template.enums.ts @@ -1,4 +1,3 @@ -// Form Template Enums export enum FormType { Survey = 'Survey', Questionnaire = 'Questionnaire', diff --git a/src/domain.types/forms/input.unit.list.domain.types.ts b/src/domain.types/forms/input.unit.list.domain.types.ts index b0cee8f..02ed208 100644 --- a/src/domain.types/forms/input.unit.list.domain.types.ts +++ b/src/domain.types/forms/input.unit.list.domain.types.ts @@ -1,10 +1,9 @@ import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; -import { uuid } from "../miscellaneous/system.types"; export interface InputUnitListCreateModel { Name: string; Description: string; - Units: any[]; // Array of units (JSON array) + Units: any[]; } export interface InputUnitListUpdateModel { diff --git a/src/domain.types/forms/logic.domain.types.ts b/src/domain.types/forms/logic.domain.types.ts index a151ce1..a47ac99 100644 --- a/src/domain.types/forms/logic.domain.types.ts +++ b/src/domain.types/forms/logic.domain.types.ts @@ -1,17 +1,16 @@ import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; import { LogicType } from "./logic.enums"; -// Base Logic DTOs export interface BaseLogicCreateModel { Type: LogicType; - FieldId: string; // UUID foreign key to FormFieldEntity + FieldId: string; Enabled?: boolean; DefaultSkip?: boolean; } export interface BaseLogicUpdateModel { Type?: LogicType; - FieldId?: string; // UUID foreign key to FormFieldEntity + FieldId?: string; Enabled?: boolean; DefaultSkip?: boolean; } @@ -19,24 +18,23 @@ export interface BaseLogicUpdateModel { export interface BaseLogicResponseDto { id: string; Type: LogicType; - FieldId: string; // UUID foreign key to FormFieldEntity + FieldId: string; Enabled: boolean; DefaultSkip?: boolean; CreatedAt: Date; UpdatedAt?: Date; } -// Skip Logic DTOs export interface SkipLogicCreateModel extends BaseLogicCreateModel { Type: LogicType.Skip; - FieldId: string; // UUID foreign key to FormFieldEntity + FieldId: string; Enabled?: boolean; DefaultSkip?: boolean; } export interface SkipLogicUpdateModel extends BaseLogicUpdateModel { Type?: LogicType.Skip; - FieldId?: string; // UUID foreign key to FormFieldEntity + FieldId?: string; Enabled?: boolean; DefaultSkip?: boolean; } @@ -46,10 +44,9 @@ export interface SkipLogicResponseDto extends BaseLogicResponseDto { Rules?: SkipRuleResponseDto[]; } -// Calculation Logic DTOs export interface CalculationLogicCreateModel extends BaseLogicCreateModel { Type: LogicType.Calculation; - FieldId: string; // UUID foreign key to FormFieldEntity + FieldId: string; Enabled?: boolean; DefaultSkip?: boolean; FallbackValue?: string; @@ -57,7 +54,7 @@ export interface CalculationLogicCreateModel extends BaseLogicCreateModel { export interface CalculationLogicUpdateModel extends BaseLogicUpdateModel { Type?: LogicType.Calculation; - FieldId?: string; // UUID foreign key to FormFieldEntity + FieldId?: string; Enabled?: boolean; DefaultSkip?: boolean; FallbackValue?: string; @@ -69,17 +66,16 @@ export interface CalculationLogicResponseDto extends BaseLogicResponseDto { Rules?: CalculationRuleResponseDto[]; } -// Validation Logic DTOs export interface ValidationLogicCreateModel extends BaseLogicCreateModel { Type: LogicType.Validation; - FieldId: string; // UUID foreign key to FormFieldEntity + FieldId: string; Enabled?: boolean; DefaultSkip?: boolean; } export interface ValidationLogicUpdateModel extends BaseLogicUpdateModel { Type?: LogicType.Validation; - FieldId?: string; // UUID foreign key to FormFieldEntity + FieldId?: string; Enabled?: boolean; DefaultSkip?: boolean; } @@ -90,11 +86,10 @@ export interface ValidationLogicResponseDto extends BaseLogicResponseDto { Rules?: ValidationRuleResponseDto[]; } -// Logic Search DTOs export interface LogicSearchFilters extends BaseSearchFilters { id?: string; type?: LogicType; - fieldId?: string; // UUID foreign key to FormFieldEntity + fieldId?: string; enabled?: boolean; defaultSkip?: boolean; } @@ -106,14 +101,13 @@ export interface LogicSearchResults extends BaseSearchResults { export interface LogicSearchResponseDto extends BaseSearchResults { id: string; Type: LogicType; - FieldId: string; // UUID foreign key to FormFieldEntity + FieldId: string; Enabled: boolean; DefaultSkip?: boolean; CreatedAt: Date; UpdatedAt?: Date; } -// Import rule DTOs (these will be defined in rule.domain.types.ts) export interface SkipRuleResponseDto { id: string; OperationId: string; diff --git a/src/domain.types/forms/question.domain.types.ts b/src/domain.types/forms/question.domain.types.ts index 2e2ee7a..51066ba 100644 --- a/src/domain.types/forms/question.domain.types.ts +++ b/src/domain.types/forms/question.domain.types.ts @@ -1,33 +1,6 @@ -// import { QueryResponseType } from "../miscellaneous/system.types"; - -// import { QueryResponseType } from "@prisma/client"; import { QueryResponseType } from "./query.response.types"; import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; -// export interface QuestionOption { -// Sequence: string; -// Data: string; -// ImageUrl: string; -// } - -// export interface QuestionCreateModel { -// ParentTemplateId: string; -// ParentSectionId: string; -// Title: string; -// Description?: string; -// DisplayCode: string; -// ResponseType: QueryResponseType; -// Score: number; -// Sequence: string; -// CorrectAnswer: string; -// Hint: string; -// Options: QuestionOption; -// // FileResourceId : string; -// QuestionImageUrl: string; -// RangeMin: number; -// RangeMax: number | null; -// } - export interface QuestionOption { Text: string; Sequence: string; @@ -42,13 +15,11 @@ export interface QuestionCreateModel { DisplayCode: string; ResponseType: QueryResponseType; Score?: number; - // Sequence?: string; Sequence?: number; CorrectAnswer: string; IsRequired?: boolean; Hint: string; - Options?: QuestionOption[]; // Change from QuestionOption to QuestionOption[] - // FileResourceId: string; + Options?: QuestionOption[]; QuestionImageUrl: string; RangeMin: number; RangeMax: number | null; @@ -65,53 +36,11 @@ export interface QuestionUpdateModel { IsRequired?: boolean; Hint?: string; Options?: QuestionOption[]; - // FileResourceId ?: string; QuestionImageUrl?: string; RangeMin?: number; RangeMax?: number; } -// export interface QuestionResponseDto { -// id: string; -// Title: string; -// Description: string; -// DisplayCode?: string; -// ResponseType: QueryResponseType; -// Score: number; -// Sequence: string; -// CorrectAnswer: string; -// Hint: string; -// Options?: QuestionOption; -// // FileResourceId : string; -// QuestionImageUrl: string; -// RangeMin: number; -// RangeMax: number; -// ParentFormSection?: { -// id: string; -// SectionIdentifier: string; -// Title: string; -// Description: string; -// DisplayCode: string; -// Sequence: number; -// ParentSectionId: string; -// CreatedAt: Date; -// }; -// ParentFormTemplate?: { -// id: string; -// Title: string; -// Description: string; -// CurrentVersion: number; -// Type: string; -// DisplayCode: string; -// OwnerUserId: string; -// RootSectionId: string; -// DefaultSectionNumbering: boolean; -// CreatedAt: Date; -// }; -// CreatedAt: Date; -// UpdatedAt: Date; -// } - export interface QuestionResponseDto { id: string; Title: string; @@ -123,7 +52,7 @@ export interface QuestionResponseDto { CorrectAnswer: string; IsRequired?: boolean; Hint: string; - Options: QuestionOption[]; // Update this to an array type + Options: QuestionOption[]; QuestionImageUrl: string; RangeMin: number; RangeMax: number | null; @@ -166,7 +95,6 @@ export interface QuestionSearchFilters extends BaseSearchFilters { isRequired?: boolean; hint?: string; options?: QuestionOption[]; - // FileResourceId : string; questionImageUrl?: string; rangeMin?: number; rangeMax?: number | null; @@ -188,7 +116,6 @@ export interface QuestionSearchResponseDto extends BaseSearchResults { IsRequired?: boolean; Hint: string; Options: QuestionOption; - // FileResourceId : string; QuestionImageUrl: string; RangeMin: number; RangeMax: number; diff --git a/src/domain.types/forms/response.domain.types.ts b/src/domain.types/forms/response.domain.types.ts index 6a4b872..67e6719 100644 --- a/src/domain.types/forms/response.domain.types.ts +++ b/src/domain.types/forms/response.domain.types.ts @@ -1,5 +1,3 @@ -// import { FormStatus, QueryResponseType } from "../miscellaneous/system.types" - import { FormStatus } from "./form.submission.enums"; import { QueryResponseType } from "./query.response.types"; import { BaseSearchFilters, BaseSearchResults } from "../miscellaneous/base.search.types"; @@ -10,7 +8,6 @@ export interface QuestionResponseCreateModel { ResponseType : QueryResponseType; IntegerValue : number; FloatValue : number; - // BooleanValue : boolean; BooleanValue : string; DateTimeValue : Date; Url : string; @@ -26,7 +23,6 @@ export interface QuestionResponseUpdateModel { ResponseType ?: QueryResponseType; IntegerValue ?: number; FloatValue ?: number; - // BooleanValue ?: boolean; BooleanValue ?: string; DateTimeValue ?: Date; Url ?: string; @@ -68,7 +64,6 @@ export interface QuestionResponseResponseDto { ResponseType : QueryResponseType; IntegerValue : number; FloatValue : GLfloat; - // BooleanValue : boolean; BooleanValue ?: string; DateTimeValue : Date; Url : string; @@ -84,7 +79,6 @@ export interface QuestionResponseSearchFilters extends BaseSearchFilters { ResponseType ?: QueryResponseType; IntegerValue ?: number; FloatValue ?: GLfloat; - // BooleanValue ?: boolean; BooleanValue ?: string; DateTimeValue ?: Date; Url ?: string; @@ -125,7 +119,6 @@ export interface QuestionResponseSearchResponseDto extends BaseSearchResults{ ResponseType : QueryResponseType; IntegerValue : number; FloatValue : GLfloat; - // BooleanValue : boolean; BooleanValue ?: string; DateTimeValue : Date; Url : string; @@ -134,5 +127,3 @@ export interface QuestionResponseSearchResponseDto extends BaseSearchResults{ SubmissionTimestamp: Date; LastSaveTimestamp : Date } - -// export type QueryResponseType = "Text" | "Float" | "Integer" | "Boolean" | "Object" | "TextArray" | "SingleChoiceSelection" | "MultiChoiceSelection" | "File" | "Date" | "DateTime" | "Rating" | "Location" | "Url" | "Range"; diff --git a/src/domain.types/forms/rule.domain.types.ts b/src/domain.types/forms/rule.domain.types.ts index f0b0c89..1f380f7 100644 --- a/src/domain.types/forms/rule.domain.types.ts +++ b/src/domain.types/forms/rule.domain.types.ts @@ -121,9 +121,6 @@ export interface ValidationRuleResponseDto extends BaseRuleResponseDto { }; } - - -// Rule Search DTOs export interface RuleSearchFilters extends BaseSearchFilters { id?: string; name?: string; diff --git a/src/domain.types/forms/skip.logic.domain.types.ts b/src/domain.types/forms/skip.logic.domain.types.ts index 097c257..66df5f8 100644 --- a/src/domain.types/forms/skip.logic.domain.types.ts +++ b/src/domain.types/forms/skip.logic.domain.types.ts @@ -1,17 +1,16 @@ import { BaseLogicCreateModel, BaseLogicResponseDto, BaseLogicUpdateModel, SkipRuleResponseDto } from "./logic.domain.types"; import { LogicType } from "./logic.enums"; -// Skip Logic DTOs export interface SkipLogicCreateModel extends BaseLogicCreateModel { Type: LogicType.Skip; - FieldId: string; // UUID foreign key to FormFieldEntity + FieldId: string; Enabled?: boolean; DefaultSkip?: boolean; } export interface SkipLogicUpdateModel extends BaseLogicUpdateModel { Type?: LogicType.Skip; - FieldId?: string; // UUID foreign key to FormFieldEntity + FieldId?: string; Enabled?: boolean; DefaultSkip?: boolean; } diff --git a/src/domain.types/miscellaneous/helper.ts b/src/domain.types/miscellaneous/helper.ts index 0af3303..a26ee60 100644 --- a/src/domain.types/miscellaneous/helper.ts +++ b/src/domain.types/miscellaneous/helper.ts @@ -2,10 +2,6 @@ import mime = require('mime-types'); import fs from 'fs'; import path from "path"; import { ExportFormTemplateDto } from '../forms/form.template.domain.types'; -// import { ExportFormTemplateDto, TemplateDto } from '../forms/form.template.domain.types'; -// import { TimeHelper } from './time.helper'; -// import { DateStringFormat } from './time.types'; - //////////////////////////////////////////////////////////////////////////// diff --git a/src/modules/module.injector.ts b/src/modules/module.injector.ts index dfef8e2..6c72c19 100644 --- a/src/modules/module.injector.ts +++ b/src/modules/module.injector.ts @@ -1,7 +1,5 @@ import 'reflect-metadata'; import { DependencyContainer } from 'tsyringe'; -// import { CommunicationInjector } from './communication/communication.injector'; -// import { EhrInjector } from './ehr/ehr.injector'; import { FileStorageInjector } from './storage/file.storage.injector'; //////////////////////////////////////////////////////////////////////////////// @@ -9,11 +7,7 @@ import { FileStorageInjector } from './storage/file.storage.injector'; export class ModuleInjector { static registerInjections(container: DependencyContainer) { - - // EhrInjector.registerInjections(container); - // CommunicationInjector.registerInjections(container); FileStorageInjector.registerInjections(container); - } } diff --git a/src/modules/reports/pdf.generator.ts b/src/modules/reports/pdf.generator.ts index f1cbe9b..af7de45 100644 --- a/src/modules/reports/pdf.generator.ts +++ b/src/modules/reports/pdf.generator.ts @@ -1,9 +1,6 @@ import fs from 'fs'; import path from 'path'; -// import { Logger } from '../../common/logger'; -// import { ApiError } from '../../common/api.error'; -// import { Helper } from '../../common/helper'; import { TimeHelper } from '../../common/time.helper'; import pdfkit from 'pdfkit'; import { ConfigurationManager } from "../../config/configuration.manager"; diff --git a/src/services/field.operations/composition.operation.service.ts b/src/services/field.operations/composition.operation.service.ts index 76b1eed..598ab6b 100644 --- a/src/services/field.operations/composition.operation.service.ts +++ b/src/services/field.operations/composition.operation.service.ts @@ -1,5 +1,4 @@ import { inject, injectable } from "tsyringe"; -// import { ICompositionOperationRepo } from "../../database/repository.interfaces/field.operations/composition.operation/composition.operation.repo.interface"; import { CompositionOperationResponseDto, CompositionOperationCreateModel, @@ -12,7 +11,6 @@ import { ICompositionOperationRepo } from "../../database/repository.interfaces/ export class CompositionOperationService { constructor(@inject('ICompositionOperationRepo') private _compositionOperationRepo: ICompositionOperationRepo) { } - // Composition Operation operations async createCompositionOperation(model: CompositionOperationCreateModel): Promise { return await this._compositionOperationRepo.createCompositionOperation(model); } diff --git a/src/services/form.field/form.field.service.ts b/src/services/form.field/form.field.service.ts index 6bc3574..8d2c4fd 100644 --- a/src/services/form.field/form.field.service.ts +++ b/src/services/form.field/form.field.service.ts @@ -1,5 +1,4 @@ -import { FormFieldCreateModel, FormFieldOption, FormFieldResponseDto, FormFieldSearchFilters, FormFieldSearchResponseDto, FormFieldUpdateModel } from "../../domain.types/forms/form.field.domain.types"; -import { ErrorHandler } from "../../common/handlers/error.handler"; +import { FormFieldCreateModel, FormFieldResponseDto, FormFieldSearchFilters, FormFieldUpdateModel } from "../../domain.types/forms/form.field.domain.types"; import { inject, injectable } from "tsyringe"; import { IFormFieldRepo } from "../../database/repository.interfaces/form.field/form.field.repo.interface"; @@ -10,60 +9,6 @@ export class FormFieldService { constructor(@inject('IFormFieldRepo') private _formFieldRepo: IFormFieldRepo) { } - // allFormFields = async (): Promise => { - // const response = await this.prisma.formField.findMany({ - // include: { - // ParentFormTemplate: true, - // ParentFormSection: true - // }, - // where: { - // DeletedAt: null - // } - // }); - // return FormFieldMapper.toArrayDto(response); - // }; - - // create = async (model: FormFieldCreateModel) => { - - // const jsonData: Prisma.JsonValue = { - // Sequence: model.Options.Sequence, - // Option: model.Options.Data, - // ImageUrl: model.Options.ImageUrl, - // } as Prisma.JsonObject; - - // const response = await this.prisma.formField.create({ - // data: { - // ParentFormTemplate: { - // connect: { id: model.ParentTemplateId } - // }, - // ParentFormSection: { - // connect: { id: model.ParentSectionId } - // }, - // Title: model.Title, - // Description: model.Description, - // DisplayCode: model.DisplayCode, - // ResponseType: model.ResponseType as QueryResponseType, - // Score: model.Score, - // CorrectAnswer: model.CorrectAnswer, - // Hint: model.Hint, - // Sequence: model.Sequence, - // Options: jsonData, - // QuestionImageUrl: model.QuestionImageUrl, - // RangeMax: model.RangeMax, - // RangeMin: model.RangeMin, - // CreatedAt: new Date(), - // // UpdatedAt: new Date(), - // // DeletedAt: null, - // }, - // include: { - // ParentFormTemplate: true, - // ParentFormSection: true - // } - // }); - // return FormFieldMapper.toDto(response); - - // }; - create = async (model: FormFieldCreateModel): Promise => { const dto = await this._formFieldRepo.create(model); return dto; @@ -93,94 +38,4 @@ export class FormFieldService { const dto = await this._formFieldRepo.search(filters); return dto; }; - - // private getSearchModel = (filters: FormFieldSearchFilters): Prisma.FormFieldWhereInput => { - // const where: Prisma.FormFieldWhereInput = { DeletedAt: null }; - - // if (filters.id) { - // where.id = { - // equals: filters.id, - // }; - // } - // if (filters.parentTemplateId) { - // where.ParentTemplateId = { - // equals: filters.parentTemplateId, - // }; - // } - // if (filters.parentSectionId) { - // where.ParentSectionId = { - // equals: filters.parentSectionId, - // }; - // } - - // if (filters.title) { - // where.Title = { - // equals: filters.title, - // }; - // } - - // if (filters.description) { - // where.Description = { - // equals: filters.description, - // }; - // } - - // if (filters.displayCode) { - // where.DisplayCode = { - // equals: filters.displayCode, - // }; - // } - - // if (filters.responseType) { - // where.ResponseType = { - // equals: filters.responseType, - // }; - // } - - // if (filters.score) { - // where.Score = { - // equals: filters.score, - // }; - // } - - // if (filters.isRequired) { - // where.IsRequired = { - // equals: filters.isRequired, - // }; - // } - - // if (filters.hint) { - // where.Hint = { - // equals: filters.hint, - // }; - // } - // // if (filters.options) { - // // where.Options = { - // // equals: filters.options, - // // }; - // // } - // if (filters.questionImageUrl) { - // where.QuestionImageUrl = { - // equals: filters.questionImageUrl, - // }; - // } - // if (filters.rangeMin) { - // where.RangeMin = { - // equals: filters.rangeMin, - // }; - // } - // if (filters.rangeMax) { - // where.RangeMax = { - // equals: filters.rangeMax, - // }; - // } - // if (filters.correctAnswer) { - // where.CorrectAnswer = { - // equals: filters.correctAnswer, - // }; - // } - - // return where; - // }; - } \ No newline at end of file diff --git a/src/services/form.section/form.section.service.ts b/src/services/form.section/form.section.service.ts index 42969fb..be31ea6 100644 --- a/src/services/form.section/form.section.service.ts +++ b/src/services/form.section/form.section.service.ts @@ -1,8 +1,4 @@ -import { Prisma, PrismaClient } from "@prisma/client"; -import { PrismaClientInit } from "../../startup/prisma.client.init"; -import { FormSectionMapper } from "../../database/sql/typeorm/mappers/form.section.mapper"; import { FormSectionCreateModel, FormSectionResponseDto, FormSectionSearchFilters, FormSectionUpdateModel } from "../../domain.types/forms/form.section.domain.types"; -// import { ErrorHandler } from "../../common/error.handler"; import { inject, injectable } from "tsyringe"; import { IFormSectionRepo } from "../../database/repository.interfaces/form.section/form.section.repo.interface"; @@ -10,21 +6,8 @@ import { IFormSectionRepo } from "../../database/repository.interfaces/form.sect export class FormSectionService { constructor(@inject('IFormSectionRepo') private _formSectionRepo: IFormSectionRepo) { - } - // allFormSections = async (): Promise => { - // const response = await this.prisma.formSection.findMany({ - // include: { - // ParentFormTemplate: true - // }, - // where: { - // DeletedAt: null, - // } - // }); - // return FormSectionMapper.toArrayDto(response); - // }; - create = async (model: FormSectionCreateModel): Promise => { var dto = await this._formSectionRepo.create(model); return dto; diff --git a/src/services/form.submission/form.submission.service.ts b/src/services/form.submission/form.submission.service.ts index acd21a0..b5c289e 100644 --- a/src/services/form.submission/form.submission.service.ts +++ b/src/services/form.submission/form.submission.service.ts @@ -1,9 +1,5 @@ -// import { FormStatus, PrismaClient } from "@prisma/client"; -// import { PrismaClientInit } from "../../startup/prisma.client.init"; -import { FormMapper } from "../../database/sql/typeorm/mappers/form.submission.mapper" import { FormSubmissionCreateModel, FormSubmissionDto, FormSubmissionSearchFilters, FormSubmissionUpdateModel } from "../../domain.types/forms/form.submission.domain.types"; import { uuid } from "../../domain.types/miscellaneous/system.types"; -import { ErrorHandler } from "../../common/handlers/error.handler"; import { inject, injectable } from "tsyringe"; import { IFormSubmissionRepo } from "../../database/repository.interfaces/form.submission/form.submission.repo.interface"; @@ -14,22 +10,6 @@ export class FormService { constructor(@inject('IFormSubmissionRepo') private _formSubmissionRepo: IFormSubmissionRepo) { } - // const response = await this.prisma.formSubmission.create({ - // data: { - // FormTemplate: { - // connect: { id: model.FormTemplateId } - // }, - // UserId: model.UserId, - // Status: model.Status, - // ValidTill: model.ValidTill, - // }, - // include: { - // FormTemplate: true, - // } - // }); - - // return FormMapper.toDto(response); - // }; create = async (model: FormSubmissionCreateModel): Promise => { const dto = await this._formSubmissionRepo.create(model); @@ -51,7 +31,6 @@ export class FormService { return dto; }; - submit = async (id: uuid): Promise => { const dto = await this._formSubmissionRepo.submit(id); return dto; diff --git a/src/services/form.template/form.template.service.ts b/src/services/form.template/form.template.service.ts index 6ff2ce8..faf9051 100644 --- a/src/services/form.template/form.template.service.ts +++ b/src/services/form.template/form.template.service.ts @@ -1,21 +1,10 @@ -// import { FormType, ItemsPerPage, Prisma, PrismaClient } from "@prisma/client"; -// import { PrismaClientInit } from "../../startup/prisma.client.init"; import { ExportFormTemplateDto, FormTemplateCreateModel, FormTemplateResponseDto, FormTemplateSearchFilters, FormTemplateUpdateModel, - SectionDto, - SectionPreviewDto, - SubsectionDto, - TemplateDto, - TemplatePreviewDto } from "../../domain.types/forms/form.template.domain.types"; -import { FormTemplateMapper } from "../../database/sql/typeorm/mappers/form.template.mapper"; -import { ErrorHandler } from "../../common/handlers/error.handler"; -import { FormSectionMapper } from "../../database/sql/typeorm/mappers/form.section.mapper"; -// import { QuestionMapper } from "../../database/sql/typeorm/mappers/question.mapper"; import { IFormTemplateRepo } from "../../database/repository.interfaces/form.template/form.template.repo.interface"; import { inject, injectable } from "tsyringe"; @@ -23,42 +12,9 @@ import { inject, injectable } from "tsyringe"; @injectable() export class FormTemplateService { - // prisma: PrismaClient = null; constructor(@inject('IFormTemplateRepo') private _formTempRepo: IFormTemplateRepo) { - // this.prisma = PrismaClientInit.instance().getPrismaInstance(); } - // allFormTemplates = async () => { - // const response = await this.prisma.formTemplate.findMany({ - // where: { - // DeletedAt: null, - // } - // }); - // return FormTemplateMapper.toArrayDto(response); - // }; - - // create = async (model: FormTemplateCreateModel) => { - // const response = await this.prisma.formTemplate.create({ - // data: { - // User: { - // connect: { id: model.OwnerUserId } - // }, - // Title: model.Title, - // Description: model.Description, - // CurrentVersion: model.CurrentVersion, - // TenantCode: model.TenantCode, - // Type: model.Type as FormType, - // ItemsPerPage: model.ItemsPerPage as ItemsPerPage, - // DisplayCode: model.DisplayCode, - // // OwnerUserId: model.OwnerUserId, - // RootSectionId: model.RootSectionId, - // DefaultSectionNumbering: model.DefaultSectionNumbering, - // // DeletedAt : null - // }, - // }); - // return FormTemplateMapper.toDto(response); - // }; - create = async (model: FormTemplateCreateModel): Promise => { const dto = await this._formTempRepo.create(model); return dto; @@ -74,73 +30,6 @@ export class FormTemplateService { return dto; }; - // getDetailsById = async (id: string) => { - // const record = await this.prisma.formTemplate.findUnique({ - // where: { - // id: id, - // DeletedAt: null - // }, - // include: { - // FormSections: { - // where: { - // DeletedAt: null - // }, - // orderBy: { - // CreatedAt: 'asc' // Sort sections within each template - // }, - // include: { - // Questions: { - // where: { - // DeletedAt: null - // }, - // orderBy: { - // CreatedAt: 'asc' // Sort questions within each section - // } - // }, - // ParentFormTemplate: true - // } - // } - // } - // }) - - // const subsections = await this.mapSections(record.FormSections); - // record.FormSections = subsections; - - // return record; - // // const template = await this.prisma.formTemplate.findUnique({ - // // where: { - // // id: id, - // // DeletedAt: null - // // }, - // // }); - // // const sections = await this.prisma.formSection.findMany({ - // // where: { - // // ParentFormTemplateId: id, - // // DeletedAt: null - // // }, - // // include: { - // // ParentFormTemplate: true - // // } - // // }); - // // const questions = await this.prisma.question.findMany({ - // // where: { - // // ParentTemplateId: id, - // // DeletedAt: null - // // }, - // // include: { - // // ParentFormTemplate: true, - // // ParentFormSection: true - // // } - // // }); - - // // const searchResult = { - // // Template: FormTemplateMapper.toDto(template), - // // Sections: sections.map((x) => FormSectionMapper.toDto(x)), - // // Questions: questions.map((x) => QuestionMapper.toDto(x)) - // // }; - // // return searchResult; - // }; - getDetailsById = async (id: string): Promise => { const dto = await this._formTempRepo.getDetailsById(id); return dto; @@ -168,164 +57,9 @@ export class FormTemplateService { return dto; }; - // protected addSortingAndPagination = ( - // search: Prisma.FormTemplateFindManyArgs, - // filters: FormTemplateSearchFilters - // ) => { - // // Sorting - // let orderByColumn: keyof typeof Prisma.FormTemplateScalarFieldEnum = 'CreatedAt'; - // if (filters.OrderBy) { - // orderByColumn = filters.OrderBy as keyof typeof Prisma.FormTemplateScalarFieldEnum; - // } - // let order: Prisma.SortOrder = 'asc'; - // if (filters.Order === 'descending') { - // order = 'desc'; - // } - - // search.orderBy = { - // [orderByColumn]: order, - // }; - - // // Pagination - // let limit = 25; - // if (filters.ItemsPerPage) { - // limit = filters.ItemsPerPage; - // } - // let offset = 0; - // let pageIndex = 1; - // if (filters.PageIndex) { - // pageIndex = filters.PageIndex < 1 ? 1 : filters.PageIndex; - // offset = (pageIndex - 1) * limit; - // } - - // search.take = limit; - // search.skip = offset; - - // // Update where clause - // const whereClause = this.getSearchModel(filters); - // if (Object.keys(whereClause).length > 0) { - // search.where = whereClause; - // } - - // return { search, pageIndex, limit, order, orderByColumn }; - // }; - - public search = async (filters: FormTemplateSearchFilters): Promise => { const dto = await this._formTempRepo.search(filters); return dto; }; - // public search = async (filters: FormTemplateSearchFilters) => { - // try { - // const { search: prismaSearch, pageIndex, limit, order, orderByColumn } = this.addSortingAndPagination({}, filters); - - // const list = await this.prisma.formTemplate.findMany({ - // where: prismaSearch.where, - // take: limit, - // skip: (pageIndex - 1) * limit, - // orderBy: { - // [orderByColumn]: order === 'desc' ? 'desc' : 'asc', - // }, - // }); - - // const count = await this.prisma.formTemplate.count({ - // where: prismaSearch.where, - // }); - - // const searchResults = { - // TotalCount: count, - // RetrievedCount: list.length, - // PageIndex: pageIndex, - // ItemsPerPage: limit, - // Order: order === 'desc' ? 'descending' : 'ascending', - // OrderedBy: orderByColumn, - // Items: list.map((x) => FormTemplateMapper.toDto(x)), - // }; - - // return searchResults; - // } catch (error) { - // ErrorHandler.throwDbAccessError('DB Error: Unable to search records!', error); - // } - // }; - - private getSearchModel = (filters: FormTemplateSearchFilters) => { - // const where: Prisma.FormTemplateWhereInput = { - // DeletedAt: null - // }; - const searchFilter = { - where: { - DeletedAt: null, - } - } - - if (filters.id) { - searchFilter.where['id'] = filters.id - } - - if (filters.Title) { - searchFilter.where['Title'] = filters.Title - } - - if (filters.TenantCode) { - searchFilter.where['TenantCode'] = filters.TenantCode - } - - if (filters.Description) { - searchFilter.where['Description'] = filters.Description - } - - if (filters.CurrentVersion) { - searchFilter.where['CurrentVersion'] = filters.CurrentVersion - } - - if (filters.Type) { - searchFilter.where['Type'] = filters.Type - } - - if (filters.DisplayCode) { - searchFilter.where['DisplayCode'] = filters.DisplayCode - } - - if (filters.OwnerUserId) { - searchFilter.where['OwnerUserId'] = filters.OwnerUserId - } - if (filters.RootSectionId) { - searchFilter.where['RootSectionId'] = filters.RootSectionId - } - if (filters.DefaultSectionNumbering) { - searchFilter.where['DefaultSectionNumbering'] = filters.DefaultSectionNumbering - } - - let limit = 25; - if (filters.ItemsPerPage) { - limit = filters.ItemsPerPage; - } - - let order = 'asc'; - if (filters.Order === 'descending') { - order = 'desc'; - } - - let orderByColum = 'CreatedAt'; - if (filters.OrderBy) { - searchFilter['orderBy'] = { - [orderByColum]: order - } - } - - let offset = 0; - - let pageIndex = 0; - - if (filters.PageIndex) { - pageIndex = filters.PageIndex < 0 ? 0 : filters.PageIndex; - offset = pageIndex * limit; - } - - searchFilter['take'] = limit; - searchFilter['skip'] = offset; - - return searchFilter; - }; } diff --git a/src/services/question.response/question.response.service.ts b/src/services/question.response/question.response.service.ts index 7d550e9..8d43c0c 100644 --- a/src/services/question.response/question.response.service.ts +++ b/src/services/question.response/question.response.service.ts @@ -5,58 +5,10 @@ import { QuestionResponseDto } from "../../domain.types/forms/question.domain.ty @injectable() export class ResponseService { - // prisma: PrismaClient = null; - // private exportDirectory = path.join(__dirname, '../exports'); constructor(@inject('IResponseRepo') private _respRepo: IResponseRepo) { - // this.prisma = PrismaClientInit.instance().getPrismaInstance(); - // if (!fs.existsSync(this.exportDirectory)) { - // fs.mkdirSync(this.exportDirectory); - // } } - // allResponses = async (): Promise => { - // const response = await this.prisma.questionResponse.findMany({ - // include: { - // FormSubmission: true, - // Question: true - // }, - // where: { - // DeletedAt: null - // } - // }); - // return ResponseMapper.toArrayDto(response); - // }; - - // create = async (model: QuestionResponseCreateModel) => { - // const response = await this.prisma.questionResponse.create({ - // data: { - // Question: { - // connect: { id: model.QuestionId } - // }, - // FormSubmission: { - // connect: { id: model.FormSubmissionId } - // }, - // ResponseType: model.ResponseType as QueryResponseType, - // IntegerValue: model.IntegerValue, - // FloatValue: model.FloatValue, - // BooleanValue: model.BooleanValue, - // DateTimeValue: model.DateTimeValue, - // Url: model.Url, - // FileResourceId: model.FileResourceId, - // TextValue: model.TextValue, - // SubmissionTimestamp: null, - // LastSaveTimestamp: new Date(), - // // DeletedAt : null, - // }, - // include: { - // FormSubmission: true, - // Question: true - // } - // }); - // return ResponseMapper.toDto(response); - // }; - create = async (model: QuestionResponseCreateModel): Promise => { const dto = await this._respRepo.create(model); return dto; diff --git a/src/services/user.login.session.service.ts b/src/services/user.login.session.service.ts deleted file mode 100644 index 05c7585..0000000 --- a/src/services/user.login.session.service.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { PrismaClient } from "@prisma/client"; -import { PrismaClientInit } from "../startup/prisma.client.init"; -import { UserLoginSessionMapper } from "../database/sql/typeorm/mappers/user.login.mapper"; -import { UserLoginSessionCreateModel, UserLoginSessionUpdateModel } from "../domain.types/forms/user.login.session.domain.types"; - - -export class UserLoginSessionService { - prisma: PrismaClient = null; - constructor() { - this.prisma = PrismaClientInit.instance().getPrismaInstance(); - } - - // allUserLoginSessions = async (): Promise => { - // const response = await this.prisma.userLoginSession.findMany({ - // include: { - // User: true, - // }, - // where: { - // DeletedAt: null - // } - // }); - // return UserLoginSessionMapper.toArrayDto(response); - // // return response; - // }; - - create = async (model: UserLoginSessionCreateModel) => { - const response = await this.prisma.userLoginSession.create({ - include: { - User: true - }, - data: { - User: { - connect: { id: model.UserId } - }, - ValidTill: model.ValidTill, - IsActiveSession: model.IsActiveSession, - // DeletedAt: null - }, - }); - return UserLoginSessionMapper.toDto(response); - }; - - update = async (id: string, model: UserLoginSessionUpdateModel) => { - const response = await this.prisma.userLoginSession.update({ - data: { - User: { - connect: { id: model.UserId } - }, - ValidTill: model.ValidTill, - IsActiveSession: model.IsActiveSession, - UpdatedAt: new Date() - }, - include: { - User: true - }, - where: { - id: id, - DeletedAt: null - }, - - }); - return UserLoginSessionMapper.toDto(response); - // return response; - }; - - getById = async (id: string) => { - const response = await this.prisma.userLoginSession.findUnique({ - where: { - id: id, - DeletedAt: null - }, - include: { - User: true - }, - }); - return UserLoginSessionMapper.toDto(response); - }; - - delete = async (id: string) => { - const response = await this.prisma.userLoginSession.update({ - where: { - id: id, - }, - data: { - DeletedAt: new Date(), - }, - include: { - User: true - }, - }); - return UserLoginSessionMapper.toDto(response); - }; -} diff --git a/src/services/user/user.service.ts b/src/services/user/user.service.ts index cfb467f..baae3c0 100644 --- a/src/services/user/user.service.ts +++ b/src/services/user/user.service.ts @@ -1,48 +1,42 @@ -import { Prisma, PrismaClient } from "@prisma/client"; -import { PrismaClientInit } from "../../startup/prisma.client.init"; -import { UserMapper } from "../../database/sql/typeorm/mappers/user.mapper"; -import { UserCreateModel, UserSearchFilters, UserSearchResponseDto, UserUpdateModel } from "../../domain.types/forms/user.domain.types"; -import { ErrorHandler } from "../../common/handlers/error.handler"; +import { UserCreateModel, UserSearchFilters, UserUpdateModel } from "../../domain.types/forms/user.domain.types"; import { IUserRepo } from "../../database/repository.interfaces/user/user.repo.interface"; import { inject, injectable } from "tsyringe"; @injectable() export class UserService { - // prisma: PrismaClient = null; - constructor(@inject('IUserRepo') private _userRepo : IUserRepo) { - // this.prisma = PrismaClientInit.instance().getPrismaInstance(); + constructor(@inject('IUserRepo') private _userRepo: IUserRepo) { } allUsers = async () => { - const dto=await this._userRepo.allUsers(); - return dto; + const dto = await this._userRepo.allUsers(); + return dto; }; create = async (model: UserCreateModel) => { - const dto=await this._userRepo.create(model); - return dto; + const dto = await this._userRepo.create(model); + return dto; }; update = async (id: string, model: UserUpdateModel) => { - const dto=await this._userRepo.update(id,model); - return dto; + const dto = await this._userRepo.update(id, model); + return dto; }; getById = async (id: string) => { - const dto=await this._userRepo.getById(id); - return dto; + const dto = await this._userRepo.getById(id); + return dto; }; delete = async (id: string) => { - const dto=await this._userRepo.delete(id); - return dto; + const dto = await this._userRepo.delete(id); + return dto; }; - + public search = async (filters: UserSearchFilters) => { - const dto=await this._userRepo.search(filters); - return dto; + const dto = await this._userRepo.search(filters); + return dto; }; diff --git a/src/startup/injector.ts b/src/startup/injector.ts index d641224..e35e5cb 100644 --- a/src/startup/injector.ts +++ b/src/startup/injector.ts @@ -1,11 +1,9 @@ import 'reflect-metadata'; import { ModuleInjector } from '../modules/module.injector'; import { DependencyContainer, container } from 'tsyringe'; -// import { AuthInjector } from '../auth/auth.injector'; import { DatabaseInjector } from '../database/database.injector'; -////////////////////////////////////////////////////////////////////////////////////////////////// - +/////////////////////////////////////////////////////////////////////////////////////// export class Injector { private static _container: DependencyContainer = container; @@ -15,7 +13,6 @@ export class Injector { } static registerInjections() { - // AuthInjector.registerInjections(Injector.Container); DatabaseInjector.registerInjections(Injector.Container); ModuleInjector.registerInjections(Injector.Container); } diff --git a/src/startup/loader.ts b/src/startup/loader.ts index e766319..abfaccd 100644 --- a/src/startup/loader.ts +++ b/src/startup/loader.ts @@ -1,71 +1,14 @@ import 'reflect-metadata'; -// import { CareplanHandler } from '../modules/careplan/careplan.handler'; -import { container } from 'tsyringe'; import { Logger } from '../common/logger'; -// import { MessagingService } from '../modules/communication/messaging.service/messaging.service'; -// import { NotificationService } from '../modules/communication/notification.service/notification.service'; -// import { StorageService } from '../modules/ehr/services/storage.service'; import { Injector } from './injector'; -// import { Scheduler } from './scheduler'; -// import { Seeder } from './seeder'; -import { ConfigurationManager } from '../config/configuration.manager'; - -////////////////////////////////////////////////////////////////////////////////////////////////// export class Loader { - // private static _seeder: Seeder = null; - - // private static _scheduler: Scheduler = Scheduler.instance(); - - // private static _messagingService: MessagingService = null; - - // private static _notificationService: NotificationService = null; - - // private static _ehrStore: StorageService = null; - - // public static get seeder() { - // return Loader._seeder; - // } - - // public static get scheduler() { - // return Loader._scheduler; - // } - - // public static get storage() { - // return Loader._ehrStore; - // } - - // public static get messagingService() { - // return Loader._messagingService; - // } - - // public static get notificationService() { - // return Loader._notificationService; - // } - public static init = async (): Promise => { try { //Register injections here... Injector.registerInjections(); - - // Loader._seeder = container.resolve(Seeder); - - // const ehrEnabled = ConfigurationManager.EhrEnabled(); - // if (ehrEnabled) { - // Loader._ehrStore = container.resolve(StorageService); - // await Loader._ehrStore.init(); - // } - - // Loader._notificationService = container.resolve(NotificationService); - // Loader._notificationService.init(); - - // Loader._messagingService = container.resolve(MessagingService); - // Loader._messagingService.init(); - - // await CareplanHandler.init(); - return true; } catch (error) { diff --git a/src/startup/prisma.client.init.ts b/src/startup/prisma.client.init.ts deleted file mode 100644 index ccc9775..0000000 --- a/src/startup/prisma.client.init.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { PrismaClient } from "@prisma/client"; - -export class PrismaClientInit { - private _prisma: PrismaClient = null; - - private static _instance: PrismaClientInit = null; - - private constructor() { - this._prisma = new PrismaClient(); - } - - public static instance(): PrismaClientInit { - return this._instance || (this._instance = new this()); - } - - public getPrismaInstance(): PrismaClient { - return this._prisma; - } - -} From 8a84e48781a59fdc3af06dc0f0ae54df85cb862a Mon Sep 17 00:00:00 2001 From: inflection-prashant Date: Wed, 16 Jul 2025 11:37:26 +0530 Subject: [PATCH 08/29] complete the documentation of docsify --- README.md | 151 ++++++- docs/.nojekyll | 1 + docs/README.md | 187 +++++++++ docs/_sidebar.md | 56 +++ docs/about/configuration.md | 578 +++++++++++++++++++++++++ docs/about/installation.md | 355 ++++++++++++++++ docs/about/quickstart.md | 271 ++++++++++++ docs/api/authentication.md | 519 +++++++++++++++++++++++ docs/api/form-templates.md | 696 ++++++++++++++++++++++++++++++ docs/api/overview.md | 632 ++++++++++++++++++++++++++++ docs/examples/basic-form.md | 744 +++++++++++++++++++++++++++++++++ docs/guides/skip-logic.md | 743 ++++++++++++++++++++++++++++++++ docs/guides/troubleshooting.md | 569 +++++++++++++++++++++++++ docs/index.html | 123 ++++++ docs/schema/database-schema.md | 623 +++++++++++++++++++++++++++ docs/schema/system-overview.md | 519 +++++++++++++++++++++++ docs/system-overview.md | 533 +++++++++++++++++++++++ package.json | 3 +- serve-docs.bat | 16 + serve-docs.js | 26 ++ src/app.ts | 7 + 21 files changed, 7349 insertions(+), 3 deletions(-) create mode 100644 docs/.nojekyll create mode 100644 docs/README.md create mode 100644 docs/_sidebar.md create mode 100644 docs/about/configuration.md create mode 100644 docs/about/installation.md create mode 100644 docs/about/quickstart.md create mode 100644 docs/api/authentication.md create mode 100644 docs/api/form-templates.md create mode 100644 docs/api/overview.md create mode 100644 docs/examples/basic-form.md create mode 100644 docs/guides/skip-logic.md create mode 100644 docs/guides/troubleshooting.md create mode 100644 docs/index.html create mode 100644 docs/schema/database-schema.md create mode 100644 docs/schema/system-overview.md create mode 100644 docs/system-overview.md create mode 100644 serve-docs.bat create mode 100644 serve-docs.js diff --git a/README.md b/README.md index 28b22be..41c4b8b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,149 @@ -# forms-service -A generic API service to manage forms data, build, publish and share forms. +# Form Service + +> A comprehensive Node.js microservice for dynamic form building, management, and processing with advanced skip logic and conditional field operations. + +## 🚀 Quick Start + +```bash +# Clone the repository +git clone https://github.com/your-org/form-service.git +cd form-service + +# Install dependencies +npm install + +# Start the service +npm run dev +``` + +The service will be available at `http://localhost:3000` + +## 📚 Documentation + +**📖 [View Full Documentation](http://localhost:3000/api/docs)** + +Once the service is running, you can access the complete documentation at: + +- **Local Development**: +- **Production**: + +### Documentation Features + +- **Interactive API Reference**: Test endpoints directly from the docs +- **Code Examples**: Ready-to-use code snippets in multiple languages +- **Visual Diagrams**: Mermaid diagrams for complex concepts +- **Search Functionality**: Find what you need quickly +- **Responsive Design**: Works on desktop and mobile + +## ✨ Key Features + +- **Dynamic Form Builder**: Create complex forms with conditional logic +- **Skip Logic Engine**: Advanced field visibility and skip conditions +- **Validation Rules**: Comprehensive field validation with custom rules +- **Calculation Logic**: Mathematical and logical operations on form fields +- **Field Operations**: Composition, iteration, and function expressions +- **RESTful API**: Complete CRUD operations for all entities +- **Multi-database Support**: PostgreSQL, MySQL, and SQLite +- **TypeScript**: Full type safety and IntelliSense support + +## 🛠️ Development + +### Prerequisites + +- Node.js 18+ +- PostgreSQL/MySQL/SQLite +- npm or yarn + +### Local Development + +```bash +# Install dependencies +npm install + +# Run in development mode +npm run dev + +# Run tests +npm test + +# Build for production +npm run build +``` + +### API Testing + +The project includes Bruno API collections for testing: + +- **Bruno Collection**: `bruno/form-service/` +- **Postman Collection**: `Postman/form-service.postman_collection.json` + +## 📖 Documentation Sections + +### Getting Started + +- [Quick Start Guide](http://localhost:3000/api/docs/#/about/quickstart) - Get up and running in 5 minutes +- [Installation Guide](http://localhost:3000/api/docs/#/about/installation) - Detailed installation instructions +- [Configuration](http://localhost:3000/api/docs/#/about/configuration) - Environment and service configuration + +### API Reference + +- [REST API Overview](http://localhost:3000/api/docs/#/api/overview) - Complete API documentation +- [Form Templates](http://localhost:3000/api/docs/#/api/form-templates) - Form template management +- [Form Submissions](http://localhost:3000/api/docs/#/api/form-submissions) - Data collection and processing +- [Field Logic](http://localhost:3000/api/docs/#/api/field-logic) - Skip logic and validation + +### Core Concepts + +- [Skip Logic](http://localhost:3000/api/docs/#/guides/skip-logic) - Conditional field visibility +- [Validation Rules](http://localhost:3000/api/docs/#/guides/validation-rules) - Field validation +- [Calculation Logic](http://localhost:3000/api/docs/#/guides/calculation-logic) - Mathematical operations + +### Examples + +- [Basic Form Creation](http://localhost:3000/api/docs/#/examples/basic-form) - Simple form example +- [Conditional Fields](http://localhost:3000/api/docs/#/examples/conditional-fields) - Skip logic examples +- [API Integration](http://localhost:3000/api/docs/#/examples/api-integration-examples) - Client integration + +## 🔧 API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| `GET` | `/api/health` | Health check | +| `GET` | `/api/docs` | Documentation | +| `POST` | `/api/forms/templates` | Create form template | +| `GET` | `/api/forms/templates` | List form templates | +| `POST` | `/api/forms/submissions` | Submit form data | +| `GET` | `/api/forms/submissions` | Get form submissions | + +## 🏗️ Architecture + +The service follows clean architecture principles with: + +- **Domain Layer**: Core business logic and entities +- **Application Layer**: Use cases and application services +- **Infrastructure Layer**: Database, external services, and frameworks +- **Presentation Layer**: API controllers and validators + +## 🤝 Contributing + +We welcome contributions! Please see our [Contributing Guide](http://localhost:3000/api/docs/#/about/contributing) for details. + +## 📄 License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## 🆘 Support + +- **Documentation**: [http://localhost:3000/api/docs](http://localhost:3000/api/docs) +- **Issues**: [GitHub Issues](https://github.com/your-org/form-service/issues) +- **Discussions**: [GitHub Discussions](https://github.com/your-org/form-service/discussions) +- **Email**: + +--- + + diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..d794dbe --- /dev/null +++ b/docs/.nojekyll @@ -0,0 +1 @@ +# This file tells GitHub Pages not to process this directory with Jekyll \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..2f02566 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,187 @@ +# Form Service Documentation + +> A comprehensive Node.js microservice for dynamic form building, management, and processing with advanced skip logic and conditional field operations. + +
+ Latest Version: 1.0.0
+ Node.js Version: 18+
+ Database: PostgreSQL, MySQL, SQLite
+ License: MIT +
+ +## 🚀 Quick Start + +Get up and running with Form Service in minutes: + +```bash +# Clone the repository +git clone https://github.com/your-org/form-service.git +cd form-service + +# Install dependencies +npm install + +# Configure environment +cp config.local.json.example config.local.json +# Edit config.local.json with your settings + +# Start the service +npm start +``` + +## 📚 Viewing Documentation + +### Option 1: Through Form Service (Recommended) +```bash +# Start your form service +npm run dev + +# Then open in browser: +# http://localhost:3000/api/docs +``` + +### Option 2: Standalone Documentation Server +```bash +# Start documentation server only +npm run docs + +# Then open in browser: +# http://localhost:8080 +``` + +### Option 3: Windows Users +```bash +# Double-click the batch file +serve-docs.bat +``` + +## ✨ Key Features + +- **Dynamic Form Builder**: Create complex forms with conditional logic +- **Skip Logic Engine**: Advanced field visibility and skip conditions +- **Validation Rules**: Comprehensive field validation with custom rules +- **Calculation Logic**: Mathematical and logical operations on form fields +- **Field Operations**: Composition, iteration, and function expressions +- **RESTful API**: Complete CRUD operations for all entities +- **Multi-database Support**: PostgreSQL, MySQL, and SQLite +- **TypeScript**: Full type safety and IntelliSense support + +## 🏗️ Architecture Overview + +```mermaid +graph TB + A[Client Applications] --> B[Form Service API] + B --> C[Authentication Layer] + B --> D[Form Templates] + B --> E[Form Submissions] + B --> F[Field Logic Engine] + B --> G[Validation Engine] + B --> H[Calculation Engine] + + D --> I[Database Layer] + E --> I + F --> I + G --> I + H --> I + + I --> J[(PostgreSQL/MySQL/SQLite)] +``` + +## 📚 Documentation Sections + +### Getting Started +- [Quick Start Guide](about/quickstart.md) - Get up and running in 5 minutes +- [Installation](about/installation.md) - Detailed installation instructions +- [Configuration](about/configuration.md) - Environment and service configuration + +### API Reference +- [REST API Overview](api/overview.md) - Complete API documentation +- [Authentication](api/authentication.md) - Security and authentication +- [Form Templates](api/form-templates.md) - Form template management +- [Form Fields](api/form-fields.md) - Field configuration and types +- [Form Submissions](api/form-submissions.md) - Data collection and processing + +### Core Concepts +- [Skip Logic](guides/skip-logic.md) - Conditional field visibility +- [Validation Rules](guides/validation-rules.md) - Field validation +- [Calculation Logic](guides/calculation-logic.md) - Mathematical operations +- [Field Operations](guides/field-operations.md) - Advanced field manipulation + +### Examples +- [Basic Form Creation](examples/basic-form.md) - Simple form example +- [Conditional Fields](examples/conditional-fields.md) - Skip logic examples +- [Validation Examples](examples/validation-examples.md) - Validation scenarios +- [API Integration](examples/api-integration-examples.md) - Client integration + +## 🔧 API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| `GET` | `/api/health` | Health check | +| `POST` | `/api/forms/templates` | Create form template | +| `GET` | `/api/forms/templates` | List form templates | +| `POST` | `/api/forms/submissions` | Submit form data | +| `GET` | `/api/forms/submissions` | Get form submissions | +| `POST` | `/api/fields/logic` | Create field logic | +| `POST` | `/api/fields/rules` | Create field rules | + +## 🛠️ Development + +### Prerequisites +- Node.js 18+ +- PostgreSQL/MySQL/SQLite +- npm or yarn + +### Local Development +```bash +# Install dependencies +npm install + +# Run in development mode +npm run dev + +# Run tests +npm test + +# Build for production +npm run build +``` + +### Environment Variables +```bash +# Database Configuration +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=form_service +DB_USER=postgres +DB_PASSWORD=password + +# Server Configuration +PORT=3000 +NODE_ENV=development + +# JWT Configuration +JWT_SECRET=your-secret-key +JWT_EXPIRES_IN=24h +``` + +## 🤝 Contributing + +We welcome contributions! Please see our [Contributing Guide](about/contributing.md) for details. + +## 📄 License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## 🆘 Support + +- **Documentation**: [docs.yourservice.com](https://docs.yourservice.com) +- **Issues**: [GitHub Issues](https://github.com/your-org/form-service/issues) +- **Discussions**: [GitHub Discussions](https://github.com/your-org/form-service/discussions) +- **Email**: support@yourservice.com + +--- + +
+ Note: This documentation is for Form Service v1.0.0. For older versions, please check the [changelog](about/changelog.md). +
\ No newline at end of file diff --git a/docs/_sidebar.md b/docs/_sidebar.md new file mode 100644 index 0000000..265278c --- /dev/null +++ b/docs/_sidebar.md @@ -0,0 +1,56 @@ + + +* [🏠 Home](/) + +## 📚 Getting Started +* [Quick Start](about/quickstart.md) +* [Installation](about/installation.md) +* [Configuration](about/configuration.md) + +## 🔧 API Reference +* [REST API Overview](api/overview.md) +* [Authentication](api/authentication.md) +* [Form Templates](api/form-templates.md) +* [Form Fields](api/form-fields.md) +* [Form Submissions](api/form-submissions.md) +* [Field Logic](api/field-logic.md) +* [Field Rules](api/field-rules.md) +* [Field Operations](api/field-operations.md) +* [Question Responses](api/question-responses.md) +* [User Management](api/user-management.md) + +## 🏗️ Architecture +* [System Overview](schema/system-overview.md) +* [Database Schema](schema/database-schema.md) +* [Domain Models](schema/domain-models.md) +* [Service Architecture](schema/service-architecture.md) + +## 📖 Guides +* [Form Builder Guide](guides/form-builder.md) +* [Skip Logic Implementation](guides/skip-logic.md) +* [Validation Rules](guides/validation-rules.md) +* [Calculation Logic](guides/calculation-logic.md) +* [Field Operations](guides/field-operations.md) +* [API Integration](guides/api-integration.md) +* [Testing](guides/testing.md) + +## 💡 Examples +* [Basic Form Creation](examples/basic-form.md) +* [Conditional Fields](examples/conditional-fields.md) +* [Skip Logic Examples](examples/skip-logic-examples.md) +* [Validation Examples](examples/validation-examples.md) +* [Calculation Examples](examples/calculation-examples.md) +* [API Integration Examples](examples/api-integration-examples.md) + +## 🔍 Advanced Topics +* [Performance Optimization](guides/performance.md) +* [Security Best Practices](guides/security.md) +* [Deployment](guides/deployment.md) +* [Troubleshooting](guides/troubleshooting.md) +* [Migration Guide](guides/migration.md) + +## 📋 Reference +* [Error Codes](api/error-codes.md) +* [Data Types](schema/data-types.md) +* [Glossary](about/glossary.md) +* [Changelog](about/changelog.md) \ No newline at end of file diff --git a/docs/about/configuration.md b/docs/about/configuration.md new file mode 100644 index 0000000..9f238b9 --- /dev/null +++ b/docs/about/configuration.md @@ -0,0 +1,578 @@ +# Configuration Guide + +> Complete configuration guide for Form Service + +## Overview + +Form Service uses a flexible configuration system that supports multiple environments and configuration sources. The configuration is loaded from JSON files and environment variables, with environment variables taking precedence. + +## Configuration Sources + +### 1. Configuration Files + +The service loads configuration from two main files: + +- **`config.json`** - Default configuration (version controlled) +- **`config.local.json`** - Local overrides (not version controlled) + +### 2. Environment Variables + +Environment variables override file-based configuration and are used for sensitive data like passwords and secrets. + +## Configuration Structure + +### System Configuration + +```json +{ + "SystemIdentifier": "Form Service", + "Auth": { + "Authentication": "Custom", + "Authorization": "Custom", + "UseRefreshToken": true, + "AccessTokenExpiresInSeconds": 2592000, + "RefreshTokenExpiresInSeconds": 2592000 + }, + "Database": { + "Type": "SQL", + "ORM": "TypeORM" + }, + "FileStorage": { + "Provider": "Custom" + }, + "Communication": { + "Email": { + "Provider": "Mock" + } + }, + "TemporaryFolders": { + "Upload": "./tmp/resources/uploads/", + "Download": "./tmp/resources/downloads/", + "CleanupFolderBeforeMinutes": 10 + } +} +``` + +### Environment Variables + +#### Database Configuration +```bash +# Database Type and ORM +DB_TYPE=postgres +DB_ORM=typeorm + +# Database Connection +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=form_service +DB_USER=postgres +DB_PASSWORD=your_secure_password +DB_DIALECT=postgres + +# Database Pool Settings +DB_POOL_MIN=5 +DB_POOL_MAX=20 +DB_POOL_ACQUIRE=30000 +DB_POOL_IDLE=10000 +``` + +#### Server Configuration +```bash +# Server Settings +PORT=3000 +HOST=localhost +NODE_ENV=development + +# CORS Settings +CORS_ORIGIN=http://localhost:3000 +CORS_METHODS=GET,POST,PUT,DELETE,PATCH +CORS_ALLOWED_HEADERS=Content-Type,Authorization + +# Rate Limiting +RATE_LIMIT_WINDOW=15m +RATE_LIMIT_MAX=100 +RATE_LIMIT_SKIP_SUCCESSFUL_REQUESTS=false +``` + +#### Authentication Configuration +```bash +# JWT Settings +JWT_SECRET=your-super-secret-jwt-key-here +JWT_EXPIRES_IN=24h +JWT_REFRESH_EXPIRES_IN=7d + +# Session Settings +SESSION_SECRET=your-session-secret +SESSION_MAX_AGE=86400000 +``` + +#### File Storage Configuration +```bash +# Storage Provider +STORAGE_PROVIDER=local +STORAGE_PATH=./uploads + +# AWS S3 (if using S3) +AWS_ACCESS_KEY_ID=your-access-key +AWS_SECRET_ACCESS_KEY=your-secret-key +AWS_REGION=us-east-1 +AWS_S3_BUCKET=your-bucket-name +``` + +#### Logging Configuration +```bash +# Logging Level +LOG_LEVEL=info +LOG_FILE=./logs/app.log +LOG_MAX_SIZE=10m +LOG_MAX_FILES=5 + +# Logging Format +LOG_FORMAT=json +LOG_COLORIZE=false +``` + +## Environment-Specific Configuration + +### Development Environment + +```json +{ + "SystemIdentifier": "Form Service - Development", + "Database": { + "Type": "SQL", + "ORM": "TypeORM" + }, + "FileStorage": { + "Provider": "Custom" + }, + "Communication": { + "Email": { + "Provider": "Mock" + } + }, + "Logging": { + "Level": "debug", + "File": "./logs/dev.log" + } +} +``` + +### Production Environment + +```json +{ + "SystemIdentifier": "Form Service - Production", + "Database": { + "Type": "SQL", + "ORM": "TypeORM" + }, + "FileStorage": { + "Provider": "AWS-S3" + }, + "Communication": { + "Email": { + "Provider": "SendGrid" + } + }, + "Logging": { + "Level": "warn", + "File": "./logs/prod.log" + } +} +``` + +## Database Configuration + +### PostgreSQL Configuration + +```json +{ + "Database": { + "Type": "SQL", + "ORM": "TypeORM", + "PostgreSQL": { + "Host": "localhost", + "Port": 5432, + "Database": "form_service", + "Username": "postgres", + "Password": "password", + "SSL": false, + "Pool": { + "Min": 5, + "Max": 20, + "Acquire": 30000, + "Idle": 10000 + } + } + } +} +``` + +### MySQL Configuration + +```json +{ + "Database": { + "Type": "SQL", + "ORM": "TypeORM", + "MySQL": { + "Host": "localhost", + "Port": 3306, + "Database": "form_service", + "Username": "root", + "Password": "password", + "Charset": "utf8mb4", + "Pool": { + "Min": 5, + "Max": 20, + "Acquire": 30000, + "Idle": 10000 + } + } + } +} +``` + +### SQLite Configuration + +```json +{ + "Database": { + "Type": "SQL", + "ORM": "TypeORM", + "SQLite": { + "Database": "./data/form_service.db", + "Logging": false + } + } +} +``` + +## File Storage Configuration + +### Local Storage + +```json +{ + "FileStorage": { + "Provider": "Custom", + "Local": { + "Path": "./uploads", + "MaxFileSize": "10MB", + "AllowedTypes": ["image/*", "application/pdf", "text/*"] + } + } +} +``` + +### AWS S3 Storage + +```json +{ + "FileStorage": { + "Provider": "AWS-S3", + "S3": { + "Bucket": "your-bucket-name", + "Region": "us-east-1", + "ACL": "private", + "MaxFileSize": "10MB" + } + } +} +``` + +## Authentication Configuration + +### JWT Configuration + +```json +{ + "Auth": { + "Authentication": "Custom", + "Authorization": "Custom", + "JWT": { + "Secret": "your-jwt-secret", + "ExpiresIn": "24h", + "RefreshExpiresIn": "7d", + "Issuer": "form-service", + "Audience": "form-service-users" + }, + "UseRefreshToken": true, + "AccessTokenExpiresInSeconds": 2592000, + "RefreshTokenExpiresInSeconds": 6048000 + } +} +``` + +### Session Configuration + +```json +{ + "Auth": { + "Session": { + "Secret": "your-session-secret", + "MaxAge": 86400000, + "Secure": false, + "HttpOnly": true, + "SameSite": "lax" + } + } +} +``` + +## Communication Configuration + +### Email Configuration + +```json +{ + "Communication": { + "Email": { + "Provider": "SendGrid", + "SendGrid": { + "ApiKey": "your-sendgrid-api-key", + "FromEmail": "noreply@yourservice.com", + "FromName": "Form Service" + } + } + } +} +``` + +### SMS Configuration + +```json +{ + "Communication": { + "SMS": { + "Provider": "Twilio", + "Twilio": { + "AccountSid": "your-account-sid", + "AuthToken": "your-auth-token", + "FromNumber": "+1234567890" + } + } + } +} +``` + +## Security Configuration + +### CORS Configuration + +```json +{ + "Security": { + "CORS": { + "Origin": ["http://localhost:3000", "https://yourservice.com"], + "Methods": ["GET", "POST", "PUT", "DELETE", "PATCH"], + "AllowedHeaders": ["Content-Type", "Authorization"], + "Credentials": true, + "MaxAge": 86400 + } + } +} +``` + +### Rate Limiting + +```json +{ + "Security": { + "RateLimit": { + "WindowMs": 900000, + "Max": 100, + "SkipSuccessfulRequests": false, + "SkipFailedRequests": false + } + } +} +``` + +## Monitoring Configuration + +### Health Check + +```json +{ + "Monitoring": { + "HealthCheck": { + "Enabled": true, + "Path": "/health", + "Interval": 30000, + "Timeout": 5000 + } + } +} +``` + +### Metrics + +```json +{ + "Monitoring": { + "Metrics": { + "Enabled": true, + "Path": "/metrics", + "CollectDefault": true + } + } +} +``` + +## Configuration Validation + +### Schema Validation + +The configuration is validated against a schema to ensure all required fields are present and have correct types. + +```typescript +interface ConfigurationSchema { + SystemIdentifier: string; + Auth: { + Authentication: string; + Authorization: string; + UseRefreshToken: boolean; + AccessTokenExpiresInSeconds: number; + RefreshTokenExpiresInSeconds: number; + }; + Database: { + Type: string; + ORM: string; + }; + FileStorage: { + Provider: string; + }; + Communication: { + Email: { + Provider: string; + }; + }; + TemporaryFolders: { + Upload: string; + Download: string; + CleanupFolderBeforeMinutes: number; + }; +} +``` + +### Environment Variable Validation + +```bash +# Validate required environment variables +npm run validate:env + +# Check configuration +npm run validate:config +``` + +## Configuration Management + +### Configuration Loading Order + +1. **Default Configuration** (`config.json`) +2. **Environment-Specific Configuration** (`config.local.json`) +3. **Environment Variables** (override file settings) +4. **Runtime Configuration** (command line arguments) + +### Configuration Reloading + +```typescript +// Reload configuration at runtime +import { ConfigurationManager } from './config/configuration.manager'; + +ConfigurationManager.reloadConfigurations(); +``` + +### Configuration Export + +```bash +# Export current configuration +npm run config:export + +# Import configuration +npm run config:import +``` + +## Best Practices + +### Security Best Practices + +1. **Never commit sensitive data** to version control +2. **Use environment variables** for secrets and passwords +3. **Rotate secrets regularly** in production +4. **Use strong passwords** and API keys +5. **Enable SSL/TLS** in production + +### Configuration Best Practices + +1. **Use descriptive names** for configuration keys +2. **Group related settings** logically +3. **Provide default values** for optional settings +4. **Validate configuration** at startup +5. **Document all configuration options** + +### Environment Best Practices + +1. **Separate configurations** by environment +2. **Use consistent naming** conventions +3. **Limit environment variables** to what's necessary +4. **Use configuration files** for complex settings +5. **Test configuration** in all environments + +## Troubleshooting + +### Common Configuration Issues + +#### 1. Configuration Not Loading +```bash +# Check configuration file syntax +node -e "console.log(require('./config.json'))" + +# Verify environment variables +env | grep DB_ +``` + +#### 2. Database Connection Issues +```bash +# Test database connection +psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME + +# Check database status +sudo systemctl status postgresql +``` + +#### 3. File Storage Issues +```bash +# Check file permissions +ls -la uploads/ + +# Test file upload +curl -X POST -F "file=@test.txt" http://localhost:3000/api/upload +``` + +### Configuration Debugging + +```bash +# Enable debug logging +LOG_LEVEL=debug npm start + +# Show configuration at startup +DEBUG=config npm start +``` + +## Next Steps + +After configuring your service: + +1. **Test Configuration**: Run validation checks +2. **Start Service**: Verify all settings work correctly +3. **Monitor Logs**: Check for configuration-related errors +4. **Backup Configuration**: Store secure copies of production configs +5. **Document Changes**: Keep track of configuration modifications + +For additional help: +- 📖 [Installation Guide](installation.md) - Setup instructions +- 🔧 [API Reference](api/overview.md) - API configuration +- 🛠️ [Development Guide](guides/development.md) - Development setup \ No newline at end of file diff --git a/docs/about/installation.md b/docs/about/installation.md new file mode 100644 index 0000000..550bc65 --- /dev/null +++ b/docs/about/installation.md @@ -0,0 +1,355 @@ +# Installation Guide + +> Complete installation instructions for Form Service + +## System Requirements + +### Minimum Requirements +- **Node.js**: 18.0.0 or higher +- **npm**: 8.0.0 or higher (or **yarn** 1.22.0 or higher) +- **RAM**: 2GB minimum, 4GB recommended +- **Disk Space**: 1GB minimum +- **Operating System**: Windows 10+, macOS 10.15+, or Linux (Ubuntu 18.04+) + +### Database Requirements +- **PostgreSQL**: 12.0 or higher (recommended) +- **MySQL**: 8.0 or higher +- **SQLite**: 3.0 or higher (development only) + +### Optional Dependencies +- **Redis**: 6.0 or higher (for caching) +- **Docker**: 20.10 or higher (for containerized deployment) +- **PM2**: For production process management + +## Installation Methods + +### Method 1: Direct Installation + +#### Step 1: Clone Repository +```bash +# Clone the repository +git clone https://github.com/your-org/form-service.git +cd form-service + +# Checkout the latest release (optional) +git checkout v1.0.0 +``` + +#### Step 2: Install Dependencies +```bash +# Install Node.js dependencies +npm install + +# Install global dependencies (optional) +npm install -g pm2 typescript ts-node +``` + +#### Step 3: Environment Configuration +```bash +# Copy configuration files +cp config.local.json.example config.local.json +cp .env.example .env + +# Edit configuration files +nano config.local.json +nano .env +``` + +#### Step 4: Database Setup +```bash +# PostgreSQL setup +sudo -u postgres createdb form_service +sudo -u postgres psql -c "CREATE USER form_user WITH PASSWORD 'your_password';" +sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE form_service TO form_user;" + +# MySQL setup +mysql -u root -p +CREATE DATABASE form_service; +CREATE USER 'form_user'@'localhost' IDENTIFIED BY 'your_password'; +GRANT ALL PRIVILEGES ON form_service.* TO 'form_user'@'localhost'; +FLUSH PRIVILEGES; +EXIT; +``` + +#### Step 5: Run Migrations +```bash +# Generate Prisma client +npx prisma generate + +# Run database migrations +npx prisma migrate deploy + +# Seed database (optional) +npm run seed +``` + +#### Step 6: Start the Service +```bash +# Development mode +npm run dev + +# Production mode +npm start + +# Using PM2 (recommended for production) +pm2 start dist/index.js --name "form-service" +pm2 save +pm2 startup +``` + +### Method 2: Docker Installation + +#### Step 1: Clone Repository +```bash +git clone https://github.com/your-org/form-service.git +cd form-service +``` + +#### Step 2: Environment Configuration +```bash +# Copy environment file +cp .env.example .env + +# Edit environment variables +nano .env +``` + +#### Step 3: Docker Compose Setup +```bash +# Start all services +docker-compose up -d + +# View logs +docker-compose logs -f form-service + +# Stop services +docker-compose down +``` + +#### Docker Compose Configuration +```yaml +version: '3.8' +services: + form-service: + build: . + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - DB_HOST=postgres + - DB_PORT=5432 + - DB_NAME=form_service + - DB_USER=postgres + - DB_PASSWORD=password + depends_on: + - postgres + - redis + volumes: + - ./logs:/app/logs + - ./uploads:/app/uploads + + postgres: + image: postgres:14 + environment: + - POSTGRES_DB=form_service + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + +volumes: + postgres_data: + redis_data: +``` + +## Configuration + +### Environment Variables + +#### Required Variables +```bash +# Database Configuration +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=form_service +DB_USER=postgres +DB_PASSWORD=your_password +DB_DIALECT=postgres + +# Server Configuration +PORT=3000 +NODE_ENV=development + +# JWT Configuration +JWT_SECRET=your-super-secret-jwt-key +JWT_EXPIRES_IN=24h +``` + +#### Optional Variables +```bash +# Redis Configuration +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD= + +# File Storage +STORAGE_PROVIDER=local +STORAGE_PATH=./uploads + +# Logging +LOG_LEVEL=info +LOG_FILE=./logs/app.log + +# CORS +CORS_ORIGIN=http://localhost:3000 + +# Rate Limiting +RATE_LIMIT_WINDOW=15m +RATE_LIMIT_MAX=100 +``` + +## Verification + +### Health Check +```bash +# Test the service +curl http://localhost:3000/api/health + +# Expected response +{ + "status": "ok", + "timestamp": "2024-01-15T10:30:00.000Z", + "version": "1.0.0" +} +``` + +### API Test +```bash +# Test form templates endpoint +curl http://localhost:3000/api/forms/templates + +# Expected response +{ + "success": true, + "data": { + "items": [], + "pagination": { + "page": 1, + "limit": 10, + "total": 0, + "totalPages": 0 + } + } +} +``` + +### Documentation Access +```bash +# Access documentation +curl http://localhost:3000/api/docs + +# Should return the docsify documentation +``` + +## Troubleshooting + +### Common Issues + +#### 1. Database Connection Failed +```bash +# Check database status +sudo systemctl status postgresql + +# Check connection +psql -h localhost -U form_user -d form_service + +# Verify environment variables +echo $DB_HOST $DB_PORT $DB_NAME +``` + +#### 2. Port Already in Use +```bash +# Find process using port +lsof -i :3000 + +# Kill process +kill -9 + +# Or change port in configuration +``` + +#### 3. Permission Denied +```bash +# Fix file permissions +chmod +x entrypoint.sh +chmod -R 755 uploads/ +chmod -R 755 logs/ +``` + +#### 4. Node.js Version Issues +```bash +# Check Node.js version +node --version + +# Use nvm to install correct version +nvm install 18 +nvm use 18 +``` + +### Log Files +```bash +# Application logs +tail -f logs/app.log + +# PM2 logs +pm2 logs form-service + +# Docker logs +docker-compose logs -f form-service +``` + +## Security Considerations + +### Production Security +1. **Change Default Passwords**: Update all default passwords +2. **Use HTTPS**: Configure SSL/TLS certificates +3. **Firewall Configuration**: Restrict access to necessary ports +4. **Regular Updates**: Keep dependencies updated +5. **Backup Strategy**: Implement regular database backups +6. **Monitoring**: Set up monitoring and alerting + +### Environment Security +```bash +# Secure environment variables +export NODE_ENV=production +export JWT_SECRET=$(openssl rand -hex 32) +export DB_PASSWORD=$(openssl rand -base64 32) + +# Use .env file for sensitive data +echo "JWT_SECRET=$JWT_SECRET" >> .env +echo "DB_PASSWORD=$DB_PASSWORD" >> .env +``` + +## Next Steps + +After successful installation: + +1. **Configure Authentication**: Set up user authentication +2. **Create First Form**: Follow the [Quick Start Guide](quickstart.md) +3. **Set Up Monitoring**: Configure logging and monitoring +4. **Test API**: Use the [Bruno Collection](/) for API testing +5. **Deploy to Production**: Follow production deployment guidelines + +For additional help: +- 📖 [Documentation](/) - Complete guide +- 🐛 [GitHub Issues](https://github.com/your-org/form-service/issues) - Report problems +- 💬 [Discussions](https://github.com/your-org/form-service/discussions) - Ask questions \ No newline at end of file diff --git a/docs/about/quickstart.md b/docs/about/quickstart.md new file mode 100644 index 0000000..ca5b9e9 --- /dev/null +++ b/docs/about/quickstart.md @@ -0,0 +1,271 @@ +# Quick Start Guide + +> Get up and running with Form Service in under 5 minutes + +## Prerequisites + +Before you begin, ensure you have the following installed: + +- **Node.js** 18.0.0 or higher +- **npm** 8.0.0 or higher (or **yarn** 1.22.0 or higher) +- **Database**: PostgreSQL 12+, MySQL 8.0+, or SQLite 3.0+ + +## Step 1: Clone and Install + +```bash +# Clone the repository +git clone https://github.com/your-org/form-service.git +cd form-service + +# Install dependencies +npm install +``` + +## Step 2: Configuration + +Copy the example configuration file and customize it: + +```bash +# Copy configuration template +cp config.local.json.example config.local.json + +# Edit the configuration file +nano config.local.json +``` + +### Basic Configuration Example + +```json +{ + "database": { + "host": "localhost", + "port": 5432, + "database": "form_service", + "username": "postgres", + "password": "your_password", + "dialect": "postgres" + }, + "server": { + "port": 3000, + "host": "localhost" + }, + "jwt": { + "secret": "your-super-secret-jwt-key", + "expiresIn": "24h" + }, + "logging": { + "level": "info" + } +} +``` + +## Step 3: Database Setup + +### Option A: PostgreSQL (Recommended) + +```bash +# Create database +createdb form_service + +# Run migrations (if using TypeORM) +npm run migration:run +``` + +### Option B: MySQL + +```sql +CREATE DATABASE form_service; +``` + +### Option C: SQLite + +```bash +# SQLite database will be created automatically +# No additional setup required +``` + +## Step 4: Start the Service + +```bash +# Development mode with hot reload +npm run dev + +# Production mode +npm start +``` + +The service will be available at `http://localhost:3000` + +## Step 5: Verify Installation + +Test the health endpoint: + +```bash +curl http://localhost:3000/api/health +``` + +Expected response: +```json +{ + "status": "ok", + "timestamp": "2024-01-15T10:30:00.000Z", + "version": "1.0.0" +} +``` + +## Step 6: Create Your First Form + +### Using the API + +```bash +# Create a form template +curl -X POST http://localhost:3000/api/forms/templates \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Contact Form", + "description": "Simple contact form", + "sections": [ + { + "name": "Personal Information", + "fields": [ + { + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true + }, + { + "name": "email", + "label": "Email Address", + "type": "email", + "required": true + } + ] + } + ] + }' +``` + +### Using Bruno (API Client) + +1. Open the `bruno/form-service` folder in Bruno +2. Import the collection from `Postman/form-service.postman_collection.json` +3. Set up your environment variables +4. Run the "Create a form template" request + +## Step 7: Submit Form Data + +```bash +# Submit form data +curl -X POST http://localhost:3000/api/forms/submissions \ + -H "Content-Type: application/json" \ + -d '{ + "templateId": "your-template-id", + "responses": [ + { + "fieldName": "fullName", + "value": "John Doe" + }, + { + "fieldName": "email", + "value": "john@example.com" + } + ] + }' +``` + +## 🎉 Congratulations! + +You've successfully set up Form Service! Here's what you can do next: + +### Next Steps + +1. **Explore the API**: Check out the [API Documentation](api/overview.md) +2. **Learn Skip Logic**: See [Skip Logic Guide](guides/skip-logic.md) +3. **Build Complex Forms**: Follow the [Form Builder Guide](guides/form-builder.md) +4. **Add Validation**: Learn about [Validation Rules](guides/validation-rules.md) + +### Common Commands + +```bash +# Development +npm run dev # Start development server +npm run build # Build for production +npm test # Run tests +npm run test:watch # Run tests in watch mode + +# Database +npm run migration:run # Run database migrations +npm run migration:revert # Revert last migration +npm run seed # Seed database with sample data + +# Linting and Formatting +npm run lint # Run ESLint +npm run lint:fix # Fix linting issues +npm run format # Format code with Prettier +``` + +### Troubleshooting + +
+ Common Issues: + + - **Port already in use**: Change the port in `config.local.json` + - **Database connection failed**: Check your database credentials + - **JWT errors**: Ensure your JWT secret is properly configured +
+ +### Getting Help + +- 📖 [Full Documentation](/) - Complete guide +- 🐛 [GitHub Issues](https://github.com/your-org/form-service/issues) - Report bugs +- 💬 [Discussions](https://github.com/your-org/form-service/discussions) - Ask questions +- 📧 [Email Support](mailto:support@yourservice.com) - Direct support + +## 🔧 Documentation Troubleshooting + +### If Documentation Shows "Loading..." + +The documentation requires a web server to run properly. Here are the solutions: + +#### Solution 1: Use the Form Service (Recommended) +```bash +npm run dev +# Then visit: http://localhost:3000/api/docs +``` + +#### Solution 2: Use Standalone Documentation Server +```bash +npm run docs +# Then visit: http://localhost:8080 +``` + +#### Solution 3: Use Python (if available) +```bash +cd docs +python -m http.server 8080 +# Then visit: http://localhost:8080 +``` + +#### Solution 4: Use Node.js http-server +```bash +npm install -g http-server +cd docs +http-server -p 8080 +# Then visit: http://localhost:8080 +``` + +### Common Issues + +1. **"Loading..." message**: You're trying to open the file directly. Use a web server instead. +2. **CORS errors**: The browser is blocking local file access. Use a web server. +3. **Missing styles**: Static files aren't being served. Use a web server. +4. **Broken links**: Relative paths don't work with file:// protocol. Use a web server. + +**Remember**: Always use `http://` URLs, never `file://` URLs for the documentation. + +--- + +
+ Pro Tip: Use the Bruno API client included in the project for easier API testing and development. +
\ No newline at end of file diff --git a/docs/api/authentication.md b/docs/api/authentication.md new file mode 100644 index 0000000..665cc03 --- /dev/null +++ b/docs/api/authentication.md @@ -0,0 +1,519 @@ +# Authentication + +> Authentication and authorization for Form Service API + +## Overview + +Form Service uses JWT (JSON Web Tokens) for authentication and implements role-based access control (RBAC) for authorization. All API endpoints (except public ones) require valid authentication. + +## Authentication Flow + +```mermaid +sequenceDiagram + participant Client + participant API + participant Auth Service + participant Database + + Client->>API: POST /auth/login + API->>Auth Service: Validate credentials + Auth Service->>Database: Check user credentials + Database-->>Auth Service: User data + Auth Service-->>API: JWT token + API-->>Client: Access token + Refresh token + + Client->>API: API request with Bearer token + API->>Auth Service: Validate JWT + Auth Service-->>API: User context + API-->>Client: Response +``` + +## Authentication Endpoints + +### Login + +POST `/api/auth/login` + +Authenticate a user and receive access tokens. + +**Request Body:** +```json +{ + "email": "user@example.com", + "password": "your_password" +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "expiresIn": 86400, + "user": { + "id": "user-uuid", + "email": "user@example.com", + "firstName": "John", + "lastName": "Doe", + "role": "admin", + "permissions": ["forms:read", "forms:write", "users:read"] + } + } +} +``` + +### Refresh Token + +POST `/api/auth/refresh` + +Refresh an expired access token using a refresh token. + +**Request Body:** +```json +{ + "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "expiresIn": 86400 + } +} +``` + +### Logout + +POST `/api/auth/logout` + +Invalidate the current session and refresh token. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Response:** +```json +{ + "success": true, + "message": "Successfully logged out" +} +``` + +### Register + +POST `/api/auth/register` + +Register a new user account. + +**Request Body:** +```json +{ + "email": "newuser@example.com", + "password": "secure_password", + "firstName": "Jane", + "lastName": "Smith", + "role": "user" +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "user": { + "id": "user-uuid", + "email": "newuser@example.com", + "firstName": "Jane", + "lastName": "Smith", + "role": "user", + "createdAt": "2024-01-15T10:30:00.000Z" + } + } +} +``` + +## Using Authentication + +### Including Tokens in Requests + +All authenticated requests must include the JWT token in the Authorization header: + +```http +Authorization: Bearer +``` + +### Example API Request + +```bash +curl -X GET http://localhost:3000/api/forms/templates \ + -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \ + -H "Content-Type: application/json" +``` + +### JavaScript Example + +```javascript +const token = 'your-jwt-token'; + +fetch('/api/forms/templates', { + method: 'GET', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } +}) +.then(response => response.json()) +.then(data => console.log(data)); +``` + +## User Roles and Permissions + +### Available Roles + +| Role | Description | Permissions | +|------|-------------|-------------| +| `admin` | System administrator | All permissions | +| `manager` | Form manager | Forms, submissions, users | +| `user` | Regular user | Own forms and submissions | +| `viewer` | Read-only access | View forms and submissions | + +### Permission System + +Permissions are granular and follow the format: `resource:action` + +#### Form Permissions +- `forms:read` - View form templates +- `forms:write` - Create and edit form templates +- `forms:delete` - Delete form templates +- `forms:publish` - Publish forms + +#### Submission Permissions +- `submissions:read` - View form submissions +- `submissions:write` - Create form submissions +- `submissions:delete` - Delete form submissions +- `submissions:export` - Export submission data + +#### User Permissions +- `users:read` - View user information +- `users:write` - Create and edit users +- `users:delete` - Delete users + +#### System Permissions +- `system:config` - Modify system configuration +- `system:logs` - View system logs +- `system:backup` - Create system backups + +### Checking Permissions + +```typescript +// Check if user has specific permission +if (user.permissions.includes('forms:write')) { + // Allow form creation +} + +// Check if user has role +if (user.role === 'admin') { + // Allow admin actions +} +``` + +## Error Responses + +### Authentication Errors + +#### 401 Unauthorized +```json +{ + "success": false, + "error": { + "code": "AUTHENTICATION_ERROR", + "message": "Invalid or missing authentication token", + "details": "Token is required for this endpoint" + } +} +``` + +#### 403 Forbidden +```json +{ + "success": false, + "error": { + "code": "AUTHORIZATION_ERROR", + "message": "Insufficient permissions", + "details": "User does not have 'forms:write' permission" + } +} +``` + +#### 422 Validation Error +```json +{ + "success": false, + "error": { + "code": "VALIDATION_ERROR", + "message": "Invalid credentials", + "details": [ + { + "field": "email", + "message": "Invalid email format" + }, + { + "field": "password", + "message": "Password must be at least 8 characters" + } + ] + } +} +``` + +## Security Best Practices + +### Token Management + +1. **Store tokens securely**: Use secure storage (not localStorage) +2. **Rotate tokens regularly**: Refresh tokens before expiration +3. **Handle token expiration**: Implement automatic refresh logic +4. **Logout properly**: Invalidate tokens on logout + +### Password Security + +1. **Strong passwords**: Minimum 8 characters with complexity +2. **Password hashing**: Passwords are hashed using bcrypt +3. **Rate limiting**: Login attempts are rate-limited +4. **Account lockout**: Temporary lockout after failed attempts + +### API Security + +1. **HTTPS only**: All API communication should use HTTPS +2. **CORS configuration**: Proper CORS settings for web clients +3. **Rate limiting**: API endpoints are rate-limited +4. **Input validation**: All inputs are validated and sanitized + +## Token Configuration + +### JWT Settings + +```json +{ + "jwt": { + "secret": "your-super-secret-jwt-key", + "expiresIn": "24h", + "refreshExpiresIn": "7d", + "issuer": "form-service", + "audience": "form-service-users" + } +} +``` + +### Token Payload + +```json +{ + "sub": "user-uuid", + "email": "user@example.com", + "role": "admin", + "permissions": ["forms:read", "forms:write"], + "iat": 1642233600, + "exp": 1642320000, + "iss": "form-service", + "aud": "form-service-users" +} +``` + +## Session Management + +### Session Configuration + +```json +{ + "session": { + "secret": "your-session-secret", + "maxAge": 86400000, + "secure": false, + "httpOnly": true, + "sameSite": "lax" + } +} +``` + +### Session Storage + +Sessions can be stored in: +- **Memory** (development only) +- **Redis** (recommended for production) +- **Database** (PostgreSQL, MySQL) + +## Multi-factor Authentication (MFA) + +### TOTP Setup + +POST `/api/auth/mfa/setup` + +Setup TOTP-based two-factor authentication. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Response:** +```json +{ + "success": true, + "data": { + "qrCode": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...", + "secret": "JBSWY3DPEHPK3PXP", + "backupCodes": ["12345678", "87654321", "11223344"] + } +} +``` + +### MFA Verification + +POST `/api/auth/mfa/verify` + +Verify TOTP code during login. + +**Request Body:** +```json +{ + "email": "user@example.com", + "password": "password", + "totpCode": "123456" +} +``` + +## OAuth Integration + +### Google OAuth + +GET `/api/auth/google` + +Initiate Google OAuth flow. + +**Response:** +```json +{ + "success": true, + "data": { + "authUrl": "https://accounts.google.com/oauth/authorize?..." + } +} +``` + +### OAuth Callback + +GET `/api/auth/google/callback` + +Handle OAuth callback and create user session. + +**Query Parameters:** +- `code` - Authorization code from OAuth provider +- `state` - CSRF protection state + +## API Key Authentication + +### Generate API Key + +POST `/api/auth/api-keys` + +Generate a new API key for service-to-service communication. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Request Body:** +```json +{ + "name": "Integration API Key", + "permissions": ["forms:read", "submissions:read"] +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "apiKey": "fs_live_1234567890abcdef...", + "name": "Integration API Key", + "permissions": ["forms:read", "submissions:read"], + "createdAt": "2024-01-15T10:30:00.000Z" + } +} +``` + +### Using API Keys + +```http +Authorization: Bearer fs_live_1234567890abcdef... +``` + +## Troubleshooting + +### Common Issues + +#### 1. Token Expired +```json +{ + "success": false, + "error": { + "code": "TOKEN_EXPIRED", + "message": "Access token has expired" + } +} +``` + +**Solution**: Use refresh token to get new access token. + +#### 2. Invalid Token Format +```json +{ + "success": false, + "error": { + "code": "INVALID_TOKEN", + "message": "Invalid token format" + } +} +``` + +**Solution**: Ensure token is properly formatted as `Bearer `. + +#### 3. Insufficient Permissions +```json +{ + "success": false, + "error": { + "code": "INSUFFICIENT_PERMISSIONS", + "message": "User does not have required permissions" + } +} +``` + +**Solution**: Check user role and permissions, contact administrator. + +### Debug Mode + +Enable debug logging for authentication: + +```bash +LOG_LEVEL=debug AUTH_DEBUG=true npm start +``` + +## Next Steps + +- 🔐 [Security Best Practices](guides/security.md) - Security guidelines +- 👥 [User Management](api/user-management.md) - User administration +- 🔑 [API Keys](guides/api-keys.md) - Service-to-service authentication +- 🛡️ [Rate Limiting](guides/rate-limiting.md) - API protection \ No newline at end of file diff --git a/docs/api/form-templates.md b/docs/api/form-templates.md new file mode 100644 index 0000000..8d9769c --- /dev/null +++ b/docs/api/form-templates.md @@ -0,0 +1,696 @@ +# Form Templates + +> Complete API reference for form template management + +## Overview + +Form templates define the structure, fields, and logic of forms. They serve as reusable blueprints for creating forms with consistent layouts and behavior. + +## Form Template Structure + +```json +{ + "id": "template-uuid", + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "version": "1.0.0", + "status": "published", + "sections": [ + { + "id": "section-uuid", + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "id": "field-uuid", + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "validation": { + "minLength": 2, + "maxLength": 100 + } + } + ] + } + ], + "createdBy": "user-uuid", + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T10:30:00.000Z" +} +``` + +## API Endpoints + +### Create Form Template + +POST `/api/forms/templates` + +Create a new form template. + +**Request Headers:** +```http +Authorization: Bearer +Content-Type: application/json +``` + +**Request Body:** +```json +{ + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "sections": [ + { + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your full name", + "validation": { + "minLength": 2, + "maxLength": 100 + } + }, + { + "name": "email", + "label": "Email Address", + "type": "email", + "required": true, + "order": 2, + "placeholder": "Enter your email address", + "validation": { + "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" + } + } + ] + } + ] +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "version": "1.0.0", + "status": "draft", + "sections": [ + { + "id": "section-uuid-1", + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "id": "field-uuid-1", + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your full name", + "validation": { + "minLength": 2, + "maxLength": 100 + } + } + ] + } + ], + "createdBy": "user-uuid", + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T10:30:00.000Z" + } +} +``` + +### Get Form Templates + +GET `/api/forms/templates` + +List all form templates with pagination and filtering. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Query Parameters:** +- `page` (number): Page number (default: 1) +- `limit` (number): Items per page (default: 10, max: 100) +- `search` (string): Search by name or description +- `status` (string): Filter by status (draft, published, archived) +- `createdBy` (string): Filter by creator +- `sortBy` (string): Sort field (name, createdAt, updatedAt) +- `sortOrder` (string): Sort order (asc, desc) + +**Example Request:** +```bash +curl -X GET "http://localhost:3000/api/forms/templates?page=1&limit=10&status=published" \ + -H "Authorization: Bearer " +``` + +**Response:** +```json +{ + "success": true, + "data": { + "items": [ + { + "id": "template-uuid-123", + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "version": "1.0.0", + "status": "published", + "createdBy": "user-uuid", + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T10:30:00.000Z" + } + ], + "pagination": { + "page": 1, + "limit": 10, + "total": 25, + "totalPages": 3, + "hasNext": true, + "hasPrev": false + } + } +} +``` + +### Get Form Template by ID + +GET `/api/forms/templates/:id` + +Get a specific form template by ID. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "version": "1.0.0", + "status": "published", + "sections": [ + { + "id": "section-uuid-1", + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "id": "field-uuid-1", + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your full name", + "validation": { + "minLength": 2, + "maxLength": 100 + } + } + ] + } + ], + "createdBy": "user-uuid", + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T10:30:00.000Z" + } +} +``` + +### Update Form Template + +PUT `/api/forms/templates/:id` + +Update an existing form template. + +**Request Headers:** +```http +Authorization: Bearer +Content-Type: application/json +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Request Body:** +```json +{ + "name": "Updated Contact Form", + "description": "An updated contact form with additional fields", + "sections": [ + { + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1 + }, + { + "name": "phone", + "label": "Phone Number", + "type": "tel", + "required": false, + "order": 2 + } + ] + } + ] +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "name": "Updated Contact Form", + "description": "An updated contact form with additional fields", + "version": "1.1.0", + "status": "draft", + "sections": [...], + "updatedAt": "2024-01-15T11:30:00.000Z" + } +} +``` + +### Delete Form Template + +DELETE `/api/forms/templates/:id` + +Delete a form template. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Response:** +```json +{ + "success": true, + "message": "Form template deleted successfully" +} +``` + +### Publish Form Template + +POST `/api/forms/templates/:id/publish` + +Publish a draft form template. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "status": "published", + "publishedAt": "2024-01-15T12:00:00.000Z" + } +} +``` + +### Archive Form Template + +POST `/api/forms/templates/:id/archive` + +Archive a published form template. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "status": "archived", + "archivedAt": "2024-01-15T12:00:00.000Z" + } +} +``` + +### Duplicate Form Template + +POST `/api/forms/templates/:id/duplicate` + +Create a copy of an existing form template. + +**Request Headers:** +```http +Authorization: Bearer +Content-Type: application/json +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Request Body:** +```json +{ + "name": "Contact Form - Copy", + "description": "A copy of the original contact form" +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-456", + "name": "Contact Form - Copy", + "description": "A copy of the original contact form", + "version": "1.0.0", + "status": "draft", + "sections": [...], + "createdAt": "2024-01-15T12:30:00.000Z" + } +} +``` + +## Field Types + +### Supported Field Types + +| Type | Description | Validation Options | +|------|-------------|-------------------| +| `text` | Single line text input | minLength, maxLength, pattern | +| `textarea` | Multi-line text input | minLength, maxLength | +| `email` | Email address input | pattern (email validation) | +| `tel` | Telephone number input | pattern | +| `number` | Numeric input | min, max, step | +| `date` | Date picker | min, max | +| `datetime` | Date and time picker | min, max | +| `select` | Dropdown selection | options (array) | +| `radio` | Radio button group | options (array) | +| `checkbox` | Checkbox group | options (array) | +| `boolean` | Single checkbox | - | +| `file` | File upload | maxSize, allowedTypes | +| `hidden` | Hidden field | - | + +### Field Configuration + +```json +{ + "name": "fieldName", + "label": "Field Label", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter value", + "defaultValue": "", + "helpText": "Additional help text", + "validation": { + "minLength": 2, + "maxLength": 100, + "pattern": "^[a-zA-Z]+$", + "customMessage": "Custom validation message" + }, + "options": [ + {"value": "option1", "label": "Option 1"}, + {"value": "option2", "label": "Option 2"} + ], + "conditional": { + "show": { + "field": "otherField", + "operator": "equals", + "value": "show" + } + } +} +``` + +## Template Status + +### Status Types + +| Status | Description | +|--------|-------------| +| `draft` | Template is being edited | +| `published` | Template is live and can be used | +| `archived` | Template is no longer active | +| `deleted` | Template is marked for deletion | + +### Status Transitions + +```mermaid +stateDiagram-v2 + [*] --> draft + draft --> published : publish + published --> draft : edit + published --> archived : archive + archived --> published : restore + archived --> deleted : delete + deleted --> [*] +``` + +## Template Versioning + +### Version Management + +- **Major Version**: Breaking changes (1.0.0 → 2.0.0) +- **Minor Version**: New features (1.0.0 → 1.1.0) +- **Patch Version**: Bug fixes (1.0.0 → 1.0.1) + +### Version History + +```json +{ + "versions": [ + { + "version": "1.0.0", + "status": "published", + "createdAt": "2024-01-15T10:30:00.000Z", + "createdBy": "user-uuid" + }, + { + "version": "1.1.0", + "status": "draft", + "createdAt": "2024-01-15T11:30:00.000Z", + "createdBy": "user-uuid" + } + ] +} +``` + +## Template Categories + +### Category Management + +```json +{ + "categories": [ + { + "id": "category-uuid", + "name": "Contact Forms", + "description": "Forms for collecting contact information" + }, + { + "id": "category-uuid-2", + "name": "Surveys", + "description": "Survey and feedback forms" + } + ] +} +``` + +### Assign Categories + +POST `/api/forms/templates/:id/categories` + +**Request Body:** +```json +{ + "categoryIds": ["category-uuid", "category-uuid-2"] +} +``` + +## Template Sharing + +### Share Template + +POST `/api/forms/templates/:id/share` + +Share a template with other users or teams. + +**Request Body:** +```json +{ + "users": ["user-uuid-1", "user-uuid-2"], + "teams": ["team-uuid-1"], + "permissions": ["read", "write"] +} +``` + +### Get Shared Templates + +GET `/api/forms/templates/shared` + +Get templates shared with the current user. + +## Template Analytics + +### Get Template Analytics + +GET `/api/forms/templates/:id/analytics` + +Get analytics for a specific template. + +**Query Parameters:** +- `startDate` (string): Start date (ISO format) +- `endDate` (string): End date (ISO format) +- `metrics` (string): Comma-separated metrics + +**Response:** +```json +{ + "success": true, + "data": { + "submissions": { + "total": 150, + "thisWeek": 25, + "thisMonth": 100 + }, + "completionRate": 85.5, + "averageTime": 180, + "topFields": [ + {"field": "email", "completionRate": 95.2}, + {"field": "fullName", "completionRate": 92.1} + ] + } +} +``` + +## Error Responses + +### Validation Errors + +```json +{ + "success": false, + "error": { + "code": "VALIDATION_ERROR", + "message": "Validation failed", + "details": [ + { + "field": "name", + "message": "Name is required" + }, + { + "field": "sections", + "message": "At least one section is required" + } + ] + } +} +``` + +### Not Found Error + +```json +{ + "success": false, + "error": { + "code": "NOT_FOUND", + "message": "Form template not found", + "details": "Template with ID 'template-uuid' does not exist" + } +} +``` + +### Permission Error + +```json +{ + "success": false, + "error": { + "code": "AUTHORIZATION_ERROR", + "message": "Insufficient permissions", + "details": "User does not have permission to update this template" + } +} +``` + +## Best Practices + +### Template Design + +1. **Clear Structure**: Use logical sections and field ordering +2. **Descriptive Labels**: Use clear, user-friendly field labels +3. **Helpful Placeholders**: Provide examples in placeholders +4. **Appropriate Validation**: Set reasonable validation rules +5. **Mobile-Friendly**: Consider mobile device usage + +### Performance + +1. **Limit Field Count**: Keep forms under 50 fields for best performance +2. **Optimize Images**: Compress images in file upload fields +3. **Use Conditional Logic**: Show/hide fields based on user input +4. **Cache Templates**: Cache frequently accessed templates + +### Security + +1. **Validate Input**: Always validate user input +2. **Sanitize Data**: Clean data before storage +3. **Access Control**: Implement proper permissions +4. **Audit Trail**: Log template changes + +## Next Steps + +- 📝 [Form Fields](api/form-fields.md) - Field configuration details +- 🔗 [Skip Logic](guides/skip-logic.md) - Conditional field visibility +- ✅ [Validation Rules](guides/validation-rules.md) - Field validation +- 📊 [Form Submissions](api/form-submissions.md) - Data collection \ No newline at end of file diff --git a/docs/api/overview.md b/docs/api/overview.md new file mode 100644 index 0000000..d617807 --- /dev/null +++ b/docs/api/overview.md @@ -0,0 +1,632 @@ +# API Overview + +> Complete API reference for Form Service + +## Introduction + +Form Service provides a comprehensive RESTful API for managing forms, templates, submissions, and user data. All API endpoints return JSON responses and follow consistent error handling patterns. + +## Base URL + +``` +Development: http://localhost:3000/api +Production: https://your-domain.com/api +``` + +## Authentication + +Most API endpoints require authentication using JWT tokens. Include the token in the Authorization header: + +```http +Authorization: Bearer +``` + +## Response Format + +All API responses follow a consistent format: + +### Success Response + +```json +{ + "success": true, + "data": { + // Response data here + }, + "message": "Optional success message" +} +``` + +### Error Response + +```json +{ + "success": false, + "error": { + "code": "ERROR_CODE", + "message": "Human-readable error message", + "details": "Additional error details" + } +} +``` + +## HTTP Status Codes + +| Code | Description | +|------|-------------| +| 200 | Success | +| 201 | Created | +| 400 | Bad Request | +| 401 | Unauthorized | +| 403 | Forbidden | +| 404 | Not Found | +| 422 | Validation Error | +| 429 | Rate Limited | +| 500 | Internal Server Error | + +## API Endpoints + +### Authentication + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/auth/login` | User login | +| POST | `/auth/register` | User registration | +| POST | `/auth/refresh` | Refresh access token | +| POST | `/auth/logout` | User logout | +| GET | `/auth/profile` | Get user profile | + +### Form Templates + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/forms/templates` | List form templates | +| POST | `/forms/templates` | Create form template | +| GET | `/forms/templates/:id` | Get template by ID | +| PUT | `/forms/templates/:id` | Update template | +| DELETE | `/forms/templates/:id` | Delete template | +| POST | `/forms/templates/:id/publish` | Publish template | +| POST | `/forms/templates/:id/archive` | Archive template | +| POST | `/forms/templates/:id/duplicate` | Duplicate template | + +### Form Fields + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/forms/fields` | List form fields | +| POST | `/forms/fields` | Create form field | +| GET | `/forms/fields/:id` | Get field by ID | +| PUT | `/forms/fields/:id` | Update field | +| DELETE | `/forms/fields/:id` | Delete field | + +### Form Sections + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/forms/sections` | List form sections | +| POST | `/forms/sections` | Create form section | +| GET | `/forms/sections/:id` | Get section by ID | +| PUT | `/forms/sections/:id` | Update section | +| DELETE | `/forms/sections/:id` | Delete section | + +### Form Submissions + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/submissions` | List form submissions | +| POST | `/submissions` | Create form submission | +| GET | `/submissions/:id` | Get submission by ID | +| PUT | `/submissions/:id` | Update submission | +| DELETE | `/submissions/:id` | Delete submission | +| GET | `/submissions/:id/export` | Export submission data | + +### Question Responses + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/responses` | List question responses | +| POST | `/responses` | Create question response | +| GET | `/responses/:id` | Get response by ID | +| PUT | `/responses/:id` | Update response | +| DELETE | `/responses/:id` | Delete response | + +### Field Logic + +#### Skip Logic + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-logic/skip-logic` | List skip logic rules | +| POST | `/field-logic/skip-logic` | Create skip logic rule | +| GET | `/field-logic/skip-logic/:id` | Get skip logic by ID | +| PUT | `/field-logic/skip-logic/:id` | Update skip logic | +| DELETE | `/field-logic/skip-logic/:id` | Delete skip logic | + +#### Validation Logic + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-logic/validation-logic` | List validation logic | +| POST | `/field-logic/validation-logic` | Create validation logic | +| GET | `/field-logic/validation-logic/:id` | Get validation logic by ID | +| PUT | `/field-logic/validation-logic/:id` | Update validation logic | +| DELETE | `/field-logic/validation-logic/:id` | Delete validation logic | + +#### Calculation Logic + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-logic/calculation-logic` | List calculation logic | +| POST | `/field-logic/calculation-logic` | Create calculation logic | +| GET | `/field-logic/calculation-logic/:id` | Get calculation logic by ID | +| PUT | `/field-logic/calculation-logic/:id` | Update calculation logic | +| DELETE | `/field-logic/calculation-logic/:id` | Delete calculation logic | + +### Field Operations + +#### Logical Operations + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-operations/logical` | List logical operations | +| POST | `/field-operations/logical` | Create logical operation | +| GET | `/field-operations/logical/:id` | Get logical operation by ID | +| PUT | `/field-operations/logical/:id` | Update logical operation | +| DELETE | `/field-operations/logical/:id` | Delete logical operation | + +#### Mathematical Operations + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-operations/mathematical` | List mathematical operations | +| POST | `/field-operations/mathematical` | Create mathematical operation | +| GET | `/field-operations/mathematical/:id` | Get mathematical operation by ID | +| PUT | `/field-operations/mathematical/:id` | Update mathematical operation | +| DELETE | `/field-operations/mathematical/:id` | Delete mathematical operation | + +#### Composition Operations + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-operations/composition` | List composition operations | +| POST | `/field-operations/composition` | Create composition operation | +| GET | `/field-operations/composition/:id` | Get composition operation by ID | +| PUT | `/field-operations/composition/:id` | Update composition operation | +| DELETE | `/field-operations/composition/:id` | Delete composition operation | + +### Field Rules + +#### Skip Rules + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-rules/skip-rules` | List skip rules | +| POST | `/field-rules/skip-rules` | Create skip rule | +| GET | `/field-rules/skip-rules/:id` | Get skip rule by ID | +| PUT | `/field-rules/skip-rules/:id` | Update skip rule | +| DELETE | `/field-rules/skip-rules/:id` | Delete skip rule | + +#### Validation Rules + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-rules/validation-rules` | List validation rules | +| POST | `/field-rules/validation-rules` | Create validation rule | +| GET | `/field-rules/validation-rules/:id` | Get validation rule by ID | +| PUT | `/field-rules/validation-rules/:id` | Update validation rule | +| DELETE | `/field-rules/validation-rules/:id` | Delete validation rule | + +#### Calculation Rules + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-rules/calculation-rules` | List calculation rules | +| POST | `/field-rules/calculation-rules` | Create calculation rule | +| GET | `/field-rules/calculation-rules/:id` | Get calculation rule by ID | +| PUT | `/field-rules/calculation-rules/:id` | Update calculation rule | +| DELETE | `/field-rules/calculation-rules/:id` | Delete calculation rule | + +### Users + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/users` | List users | +| POST | `/users` | Create user | +| GET | `/users/:id` | Get user by ID | +| PUT | `/users/:id` | Update user | +| DELETE | `/users/:id` | Delete user | +| GET | `/users/search` | Search users | + +### User Sessions + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/user-sessions` | List user sessions | +| POST | `/user-sessions` | Create user session | +| GET | `/user-sessions/:id` | Get session by ID | +| DELETE | `/user-sessions/:id` | Delete session | + +### Form Template Approvals + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/form-template-approvals` | List template approvals | +| POST | `/form-template-approvals` | Create template approval | +| GET | `/form-template-approvals/:id` | Get approval by ID | +| PUT | `/form-template-approvals/:id` | Update approval | +| DELETE | `/form-template-approvals/:id` | Delete approval | + +### Template Folders + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/template-folders` | List template folders | +| POST | `/template-folders` | Create template folder | +| GET | `/template-folders/:id` | Get folder by ID | +| PUT | `/template-folders/:id` | Update folder | +| DELETE | `/template-folders/:id` | Delete folder | + +### Favorite Templates + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/favorite-templates` | List favorite templates | +| POST | `/favorite-templates` | Create favorite template | +| GET | `/favorite-templates/:id` | Get favorite by ID | +| DELETE | `/favorite-templates/:id` | Delete favorite | + +### Input Unit Lists + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/input-unit-lists` | List input unit lists | +| POST | `/input-unit-lists` | Create input unit list | +| GET | `/input-unit-lists/:id` | Get unit list by ID | +| PUT | `/input-unit-lists/:id` | Update unit list | +| DELETE | `/input-unit-lists/:id` | Delete unit list | + +### System + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/health` | Health check | +| GET | `/docs` | API documentation | + +## Query Parameters + +### Pagination + +Most list endpoints support pagination: + +```http +GET /api/forms/templates?page=1&limit=10 +``` + +**Parameters:** +- `page` (number): Page number (default: 1) +- `limit` (number): Items per page (default: 10, max: 100) + +### Filtering + +Many endpoints support filtering: + +```http +GET /api/forms/templates?status=published&createdBy=user-uuid +``` + +### Sorting + +Sort results by specific fields: + +```http +GET /api/forms/templates?sortBy=createdAt&sortOrder=desc +``` + +**Parameters:** +- `sortBy` (string): Field to sort by +- `sortOrder` (string): Sort order (`asc` or `desc`) + +### Search + +Search across text fields: + +```http +GET /api/forms/templates?search=contact form +``` + +## Request Headers + +### Common Headers + +```http +Content-Type: application/json +Authorization: Bearer +Accept: application/json +``` + +### Optional Headers + +```http +X-Request-ID: unique-request-id +X-Client-Version: 1.0.0 +X-User-Agent: custom-user-agent +``` + +## Rate Limiting + +API endpoints are rate-limited to prevent abuse: + +- **Authentication endpoints**: 5 requests per minute +- **General endpoints**: 100 requests per minute +- **File upload endpoints**: 10 requests per minute + +Rate limit headers are included in responses: + +```http +X-RateLimit-Limit: 100 +X-RateLimit-Remaining: 95 +X-RateLimit-Reset: 1642233600 +``` + +## Error Handling + +### Common Error Codes + +| Code | Description | +|------|-------------| +| `VALIDATION_ERROR` | Request data validation failed | +| `AUTHENTICATION_ERROR` | Invalid or missing authentication | +| `AUTHORIZATION_ERROR` | Insufficient permissions | +| `NOT_FOUND` | Resource not found | +| `DUPLICATE_ERROR` | Resource already exists | +| `RATE_LIMIT_ERROR` | Rate limit exceeded | +| `INTERNAL_ERROR` | Internal server error | + +### Error Response Example + +```json +{ + "success": false, + "error": { + "code": "VALIDATION_ERROR", + "message": "Validation failed", + "details": [ + { + "field": "email", + "message": "Invalid email format" + }, + { + "field": "password", + "message": "Password must be at least 8 characters" + } + ] + } +} +``` + +## Data Types + +### UUID + +All IDs are UUIDs (version 4): + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +### Timestamps + +All timestamps are in ISO 8601 format: + +```json +{ + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T11:30:00.000Z" +} +``` + +### JSONB + +Complex data is stored as JSONB: + +```json +{ + "validation_rules": { + "minLength": 2, + "maxLength": 100, + "pattern": "^[a-zA-Z]+$" + } +} +``` + +## File Upload + +### Upload Endpoint + +```http +POST /api/upload +Content-Type: multipart/form-data +``` + +### File Limits + +- **Max file size**: 10MB +- **Allowed types**: Images, PDFs, documents +- **Max files per request**: 5 + +### Upload Response + +```json +{ + "success": true, + "data": { + "files": [ + { + "id": "file-uuid", + "filename": "document.pdf", + "size": 1024000, + "url": "https://storage.example.com/files/document.pdf", + "mimeType": "application/pdf" + } + ] + } +} +``` + +## Webhooks + +### Webhook Configuration + +```json +{ + "url": "https://your-app.com/webhooks/form-submission", + "events": ["form.submitted", "form.updated"], + "secret": "webhook-secret-key" +} +``` + +### Webhook Events + +| Event | Description | +|-------|-------------| +| `form.submitted` | Form submission created | +| `form.updated` | Form submission updated | +| `form.deleted` | Form submission deleted | +| `template.published` | Form template published | +| `template.archived` | Form template archived | + +### Webhook Payload + +```json +{ + "event": "form.submitted", + "timestamp": "2024-01-15T10:30:00.000Z", + "data": { + "submissionId": "submission-uuid", + "templateId": "template-uuid", + "submittedBy": "user-uuid" + } +} +``` + +## SDKs and Libraries + +### JavaScript/TypeScript + +```bash +npm install @form-service/sdk +``` + +```javascript +import { FormService } from '@form-service/sdk'; + +const client = new FormService({ + baseUrl: 'https://api.example.com', + token: 'your-jwt-token' +}); + +// Create form template +const template = await client.templates.create({ + name: 'Contact Form', + description: 'A simple contact form' +}); +``` + +### Python + +```bash +pip install form-service-sdk +``` + +```python +from form_service import FormService + +client = FormService( + base_url='https://api.example.com', + token='your-jwt-token' +) + +# Create form template +template = client.templates.create({ + 'name': 'Contact Form', + 'description': 'A simple contact form' +}) +``` + +## Testing + +### Using Bruno Collection + +The project includes a Bruno collection for API testing: + +1. **Install Bruno**: Download from [bruno.com](https://www.bruno.com) +2. **Import Collection**: Import the `bruno/form-service/collection.bru` file +3. **Configure Environment**: Set up environment variables +4. **Run Tests**: Execute requests and test scenarios + +### Example Test Request + +```http +POST {{baseUrl}}/api/forms/templates +Authorization: Bearer {{authToken}} +Content-Type: application/json + +{ + "name": "Test Form", + "description": "A test form for API testing", + "sections": [ + { + "name": "Basic Information", + "fields": [ + { + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true + } + ] + } + ] +} +``` + +## Versioning + +API versioning is handled through URL paths: + +``` +/api/v1/forms/templates # Version 1 +/api/v2/forms/templates # Version 2 +``` + +Current version: `v1` + +## Deprecation Policy + +- **Deprecation notice**: 6 months advance notice +- **Breaking changes**: Only in major versions +- **Backward compatibility**: Maintained within major versions + +## Support + +### Documentation + +- **API Docs**: Available at `/api/docs` +- **Examples**: See the [examples](examples/) directory +- **SDK Docs**: Available in each SDK package + +### Getting Help + +- **GitHub Issues**: Report bugs and request features +- **Discussions**: Ask questions and share ideas +- **Email Support**: Contact support@example.com + +## Next Steps + +- 🔐 [Authentication](api/authentication.md) - Authentication and authorization +- 📝 [Form Templates](api/form-templates.md) - Form template management +- 📊 [Form Submissions](api/form-submissions.md) - Data collection +- 🔗 [Skip Logic](guides/skip-logic.md) - Conditional field visibility +- 🗄️ [Database Schema](schema/database-schema.md) - Database design \ No newline at end of file diff --git a/docs/examples/basic-form.md b/docs/examples/basic-form.md new file mode 100644 index 0000000..9a81dae --- /dev/null +++ b/docs/examples/basic-form.md @@ -0,0 +1,744 @@ +# Basic Form Creation Example + +> Learn how to create a simple contact form using the Form Service API + +## Overview + +This example demonstrates how to create a basic contact form with multiple field types, sections, and simple validation. We'll walk through the entire process from form template creation to form submission. + +## Form Structure + +Our contact form will have the following structure: + +``` +Contact Form +├── Personal Information +│ ├── Full Name (text, required) +│ ├── Email Address (email, required) +│ └── Phone Number (tel, optional) +├── Address Information +│ ├── Street Address (text, required) +│ ├── City (text, required) +│ ├── State (select, required) +│ └── ZIP Code (text, required) +└── Additional Information + ├── Subject (text, required) + └── Message (textarea, required) +``` + +## Step 1: Create Form Template + +### API Request + +POST `/api/forms/templates` + +```json +{ + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "sections": [ + { + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your full name", + "validation": { + "minLength": 2, + "maxLength": 100 + } + }, + { + "name": "email", + "label": "Email Address", + "type": "email", + "required": true, + "order": 2, + "placeholder": "Enter your email address", + "validation": { + "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" + } + }, + { + "name": "phone", + "label": "Phone Number", + "type": "tel", + "required": false, + "order": 3, + "placeholder": "Enter your phone number", + "validation": { + "pattern": "^[+]?[1-9]\\d{1,14}$" + } + } + ] + }, + { + "name": "Address Information", + "description": "Mailing address details", + "order": 2, + "fields": [ + { + "name": "streetAddress", + "label": "Street Address", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your street address" + }, + { + "name": "city", + "label": "City", + "type": "text", + "required": true, + "order": 2, + "placeholder": "Enter your city" + }, + { + "name": "state", + "label": "State", + "type": "select", + "required": true, + "order": 3, + "options": [ + {"value": "AL", "label": "Alabama"}, + {"value": "AK", "label": "Alaska"}, + {"value": "AZ", "label": "Arizona"}, + {"value": "AR", "label": "Arkansas"}, + {"value": "CA", "label": "California"}, + {"value": "CO", "label": "Colorado"}, + {"value": "CT", "label": "Connecticut"}, + {"value": "DE", "label": "Delaware"}, + {"value": "FL", "label": "Florida"}, + {"value": "GA", "label": "Georgia"}, + {"value": "HI", "label": "Hawaii"}, + {"value": "ID", "label": "Idaho"}, + {"value": "IL", "label": "Illinois"}, + {"value": "IN", "label": "Indiana"}, + {"value": "IA", "label": "Iowa"}, + {"value": "KS", "label": "Kansas"}, + {"value": "KY", "label": "Kentucky"}, + {"value": "LA", "label": "Louisiana"}, + {"value": "ME", "label": "Maine"}, + {"value": "MD", "label": "Maryland"}, + {"value": "MA", "label": "Massachusetts"}, + {"value": "MI", "label": "Michigan"}, + {"value": "MN", "label": "Minnesota"}, + {"value": "MS", "label": "Mississippi"}, + {"value": "MO", "label": "Missouri"}, + {"value": "MT", "label": "Montana"}, + {"value": "NE", "label": "Nebraska"}, + {"value": "NV", "label": "Nevada"}, + {"value": "NH", "label": "New Hampshire"}, + {"value": "NJ", "label": "New Jersey"}, + {"value": "NM", "label": "New Mexico"}, + {"value": "NY", "label": "New York"}, + {"value": "NC", "label": "North Carolina"}, + {"value": "ND", "label": "North Dakota"}, + {"value": "OH", "label": "Ohio"}, + {"value": "OK", "label": "Oklahoma"}, + {"value": "OR", "label": "Oregon"}, + {"value": "PA", "label": "Pennsylvania"}, + {"value": "RI", "label": "Rhode Island"}, + {"value": "SC", "label": "South Carolina"}, + {"value": "SD", "label": "South Dakota"}, + {"value": "TN", "label": "Tennessee"}, + {"value": "TX", "label": "Texas"}, + {"value": "UT", "label": "Utah"}, + {"value": "VT", "label": "Vermont"}, + {"value": "VA", "label": "Virginia"}, + {"value": "WA", "label": "Washington"}, + {"value": "WV", "label": "West Virginia"}, + {"value": "WI", "label": "Wisconsin"}, + {"value": "WY", "label": "Wyoming"} + ] + }, + { + "name": "zipCode", + "label": "ZIP Code", + "type": "text", + "required": true, + "order": 4, + "placeholder": "Enter your ZIP code", + "validation": { + "pattern": "^\\d{5}(-\\d{4})?$" + } + } + ] + }, + { + "name": "Additional Information", + "description": "Inquiry details", + "order": 3, + "fields": [ + { + "name": "subject", + "label": "Subject", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter the subject of your inquiry", + "validation": { + "minLength": 5, + "maxLength": 200 + } + }, + { + "name": "message", + "label": "Message", + "type": "textarea", + "required": true, + "order": 2, + "placeholder": "Enter your message", + "rows": 5, + "validation": { + "minLength": 10, + "maxLength": 2000 + } + } + ] + } + ] +} +``` + +### Response + +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "sections": [ + { + "id": "section-uuid-1", + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "id": "field-uuid-1", + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your full name", + "validation": { + "minLength": 2, + "maxLength": 100 + } + }, + { + "id": "field-uuid-2", + "name": "email", + "label": "Email Address", + "type": "email", + "required": true, + "order": 2, + "placeholder": "Enter your email address", + "validation": { + "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" + } + }, + { + "id": "field-uuid-3", + "name": "phone", + "label": "Phone Number", + "type": "tel", + "required": false, + "order": 3, + "placeholder": "Enter your phone number", + "validation": { + "pattern": "^[+]?[1-9]\\d{1,14}$" + } + } + ] + } + ], + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T10:30:00.000Z" + } +} +``` + +## Step 2: Submit Form Data + +### API Request + +POST `/api/forms/submissions` + +```json +{ + "templateId": "template-uuid-123", + "responses": [ + { + "fieldName": "fullName", + "value": "John Doe" + }, + { + "fieldName": "email", + "value": "john.doe@example.com" + }, + { + "fieldName": "phone", + "value": "+1-555-123-4567" + }, + { + "fieldName": "streetAddress", + "value": "123 Main Street" + }, + { + "fieldName": "city", + "value": "New York" + }, + { + "fieldName": "state", + "value": "NY" + }, + { + "fieldName": "zipCode", + "value": "10001" + }, + { + "fieldName": "subject", + "value": "Product Inquiry" + }, + { + "fieldName": "message", + "value": "I would like to know more about your products and pricing. Please send me additional information." + } + ] +} +``` + +### Response + +```json +{ + "success": true, + "data": { + "id": "submission-uuid-456", + "templateId": "template-uuid-123", + "responses": [ + { + "id": "response-uuid-1", + "fieldName": "fullName", + "value": "John Doe", + "fieldId": "field-uuid-1" + }, + { + "id": "response-uuid-2", + "fieldName": "email", + "value": "john.doe@example.com", + "fieldId": "field-uuid-2" + } + ], + "status": "submitted", + "submittedAt": "2024-01-15T10:35:00.000Z", + "createdAt": "2024-01-15T10:35:00.000Z" + } +} +``` + +## Step 3: Retrieve Form Submissions + +### API Request + +GET `/api/forms/submissions?templateId=template-uuid-123` + +### Response + +```json +{ + "success": true, + "data": { + "items": [ + { + "id": "submission-uuid-456", + "templateId": "template-uuid-123", + "responses": [ + { + "fieldName": "fullName", + "value": "John Doe" + }, + { + "fieldName": "email", + "value": "john.doe@example.com" + } + ], + "status": "submitted", + "submittedAt": "2024-01-15T10:35:00.000Z" + } + ], + "pagination": { + "page": 1, + "limit": 10, + "total": 1, + "totalPages": 1, + "hasNext": false, + "hasPrev": false + } + } +} +``` + +## Using Bruno Collection + +### 1. Import the Collection + +1. Open Bruno +2. Import the collection from `bruno/form-service/collection.bru` +3. Set up environment variables in `bruno/form-service/environments/forms-service.bru` + +### 2. Environment Variables + +```json +{ + "baseUrl": "http://localhost:3000/api", + "templateId": "", + "submissionId": "" +} +``` + +### 3. Run the Requests + +1. **Create Form Template**: Run "Create a form template" request +2. **Copy Template ID**: Copy the returned template ID to environment variable +3. **Submit Form**: Run "Create a new form submission" request +4. **View Submissions**: Run "Get form submissions" request + +## Frontend Integration Example + +### React Component + +```jsx +import React, { useState } from 'react'; + +const ContactForm = () => { + const [formData, setFormData] = useState({ + fullName: '', + email: '', + phone: '', + streetAddress: '', + city: '', + state: '', + zipCode: '', + subject: '', + message: '' + }); + + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const handleSubmit = async (e) => { + e.preventDefault(); + setLoading(true); + setError(null); + + try { + const response = await fetch('/api/forms/submissions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + templateId: 'template-uuid-123', + responses: Object.entries(formData).map(([fieldName, value]) => ({ + fieldName, + value + })) + }) + }); + + const result = await response.json(); + + if (result.success) { + alert('Form submitted successfully!'); + setFormData({ + fullName: '', + email: '', + phone: '', + streetAddress: '', + city: '', + state: '', + zipCode: '', + subject: '', + message: '' + }); + } else { + setError(result.error.message); + } + } catch (err) { + setError('Failed to submit form. Please try again.'); + } finally { + setLoading(false); + } + }; + + const handleChange = (field, value) => { + setFormData(prev => ({ + ...prev, + [field]: value + })); + }; + + return ( +
+

Contact Us

+ + {error &&
{error}
} + +
+

Personal Information

+ +
+ + handleChange('fullName', e.target.value)} + placeholder="Enter your full name" + required + /> +
+ +
+ + handleChange('email', e.target.value)} + placeholder="Enter your email address" + required + /> +
+ +
+ + handleChange('phone', e.target.value)} + placeholder="Enter your phone number" + /> +
+
+ +
+

Address Information

+ +
+ + handleChange('streetAddress', e.target.value)} + placeholder="Enter your street address" + required + /> +
+ +
+
+ + handleChange('city', e.target.value)} + placeholder="Enter your city" + required + /> +
+ +
+ + +
+ +
+ + handleChange('zipCode', e.target.value)} + placeholder="Enter your ZIP code" + required + /> +
+
+
+ +
+

Additional Information

+ +
+ + handleChange('subject', e.target.value)} + placeholder="Enter the subject of your inquiry" + required + /> +
+ +
+ +