From f51483973549b3f581a48c220f57c8162494add0 Mon Sep 17 00:00:00 2001 From: Brett Choquet <brett.choquet@inra.fr> Date: Thu, 16 Jan 2025 23:13:42 +0100 Subject: [PATCH] Authentication and backend fusion update --- .dockerignore | 8 +- .env.development | 33 + Dockerfile | 33 +- __test__/models/group.test.js | 31 - __test__/models/policy.test.js | 21 - __test__/models/policyField.test.js | 13 - __test__/models/role.test.js | 24 - __test__/models/searchHistory.test.js | 44 -- __test__/models/source.test.js | 13 - __test__/models/stdField.test.js | 21 - __tests__/api/groups/get.test.js | 88 +++ __tests__/api/groups/post.test.js | 67 ++ __tests__/api/groups/update.test.js | 227 +++++++ __tests__/api/policies/delete.test.js | 51 ++ __tests__/api/policies/get.test.js | 88 +++ __tests__/api/policies/post.test.js | 66 ++ __tests__/api/policies/update.test.js | 271 ++++++++ __tests__/api/roles/delete.test.js | 49 ++ __tests__/api/roles/get.test.js | 132 ++++ __tests__/api/roles/post.test.js | 195 ++++++ __tests__/api/roles/update.test.js | 88 +++ __tests__/api/search/post.test.js | 67 ++ __tests__/api/sources/delete.test.js | 50 ++ __tests__/api/sources/get.test.js | 208 ++++++ __tests__/api/sources/post.test.js | 69 ++ __tests__/api/std_fields/get.test.js | 90 +++ __tests__/api/std_fields/post.test.js | 46 ++ __tests__/api/std_fields/update.test.js | 55 ++ __tests__/api/user-display-fields/get.test.js | 55 ++ __tests__/api/user-requests/delete.test.js | 51 ++ __tests__/api/user-requests/get.test.js | 165 +++++ __tests__/api/user-requests/post.test.js | 137 ++++ .../api/user-search-history/delete.test.js | 94 +++ __tests__/api/user-search-history/get.test.js | 58 ++ .../api/user-search-history/post.test.js | 75 +++ __tests__/api/users/delete.test.js | 52 ++ __tests__/api/users/get.test.js | 78 +++ __tests__/api/users/post.test.js | 96 +++ app/api/group/delete.js | 42 -- app/api/group/get.js | 42 -- app/api/group/index.js | 23 - app/api/group/post.js | 42 -- app/api/group/update.js | 42 -- app/api/groups/delete.js | 25 + app/api/groups/get.js | 34 + app/api/groups/post.js | 29 + app/api/groups/update.js | 110 +++ app/api/index.js | 281 +++++--- app/api/policies/delete.js | 25 + app/api/policies/get.js | 35 + app/api/policies/post.js | 28 + app/api/policies/update.js | 110 +++ app/api/policy/delete.js | 31 - app/api/policy/get.js | 64 -- app/api/policy/index.js | 25 - app/api/policy/post.js | 64 -- app/api/policy/update.js | 31 - app/api/realm/delete.js | 24 - app/api/realm/get.js | 38 -- app/api/realm/index.js | 1 - app/api/realm/post.js | 24 - app/api/realm/put.js | 24 - app/api/role-user/delete.js | 21 - app/api/role-user/get.js | 21 - app/api/role-user/index.js | 15 - app/api/role-user/post.js | 21 - app/api/role-user/put.js | 21 - app/api/roles/delete.js | 34 +- app/api/roles/get.js | 68 +- app/api/roles/index.js | 17 - app/api/roles/post.js | 86 ++- app/api/roles/put.js | 45 +- app/api/search/post.js | 63 ++ app/api/sources/delete.js | 25 + app/api/sources/get.js | 81 +++ app/api/sources/post.js | 38 ++ app/api/std_fields/delete.js | 17 + app/api/std_fields/get.js | 49 ++ app/api/std_fields/post.js | 17 + app/api/std_fields/update.js | 27 + app/api/user-display-fields/get.js | 25 + app/api/user-display-fields/post.js | 27 + .../user-fields-display-settings/delete.js | 21 - app/api/user-fields-display-settings/get.js | 32 - app/api/user-fields-display-settings/index.js | 16 - app/api/user-fields-display-settings/post.js | 21 - app/api/user-fields-display-settings/put.js | 21 - app/api/user-profile/index.js | 1 - app/api/user-requests/delete.js | 35 +- app/api/user-requests/get.js | 82 ++- app/api/user-requests/index.js | 16 - app/api/user-requests/post.js | 94 ++- app/api/user-search-history/delete.js | 53 +- app/api/user-search-history/get.js | 34 +- app/api/user-search-history/index.js | 13 - app/api/user-search-history/post.js | 57 +- app/api/users/delete.js | 34 +- app/api/users/get.js | 102 +-- app/api/users/index.js | 24 - app/api/users/post.js | 90 +-- app/api/users/put.js | 21 - app/config/db.js | 54 -- app/config/elk.js | 17 + app/config/mongo.js | 25 + app/config/postgres.js | 12 + app/dal/authService.js | 31 + app/dal/elasticService.js | 153 +++++ app/dal/groupService.js | 513 ++++++++------ app/dal/keycloakService.js | 52 ++ app/dal/mongoService.js | 32 + app/dal/policyService.js | 632 +++++++++++------- app/dal/realmService.js | 37 - app/dal/roleAllocationService.js | 77 --- app/dal/roleService.js | 282 ++++++-- app/dal/searchHistoryService.js | 168 +++-- app/dal/sourceService.js | 295 ++++++++ app/dal/stdFieldService.js | 166 +++++ app/dal/userDisplayFieldsService.js | 66 ++ app/dal/userFieldsDisplaySettingsService.js | 117 ---- app/dal/userRequestService.js | 207 +++--- app/dal/userService.js | 472 +++---------- app/middlewares/errorHandler.js | 20 - app/models/Group.js | 80 --- app/models/Policy.js | 97 --- app/models/Role.js | 47 -- app/models/Source.js | 78 --- app/models/StdField.js | 34 - app/models/User.js | 37 - app/models/UserFieldsDisplaySettings.js | 48 -- app/models/UserHistory.js | 30 - app/models/UserRequest.js | 33 - app/utils/cast.js | 18 + app/utils/errorHandler.js | 31 + app/utils/{logger.js => format.js} | 8 +- db.js | 6 - docker-compose.yml | 14 + entrypoint.sh | 15 + gitlab-ci.yml | 25 + index.js | 24 +- init-keycloak.js | 13 - jest.config.js | 8 + jest.setup.js | 17 + jsconfig.json | 9 + keycloak.js | 15 - knexfile.js | 9 - migrate.js | 8 - package.json | 32 +- prisma/client.js | 5 + .../20241211224745_0_init/migration.sql | 186 ++++++ .../migration.sql | 5 + .../migration.sql | 17 + .../migration.sql | 12 + .../migration.sql | 4 + .../migration.sql | 13 + .../migration.sql | 13 + .../migration.sql | 3 + prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 200 ++++++ prisma/seed.js | 49 ++ scripts/build.sh | 11 - scripts/migrate.sh | 14 - scripts/up.sh | 21 - scripts/util.sh | 5 - server.js | 85 ++- web.js | 40 +- 165 files changed, 7107 insertions(+), 3431 deletions(-) create mode 100644 .env.development delete mode 100644 __test__/models/group.test.js delete mode 100644 __test__/models/policy.test.js delete mode 100644 __test__/models/policyField.test.js delete mode 100644 __test__/models/role.test.js delete mode 100644 __test__/models/searchHistory.test.js delete mode 100644 __test__/models/source.test.js delete mode 100644 __test__/models/stdField.test.js create mode 100644 __tests__/api/groups/get.test.js create mode 100644 __tests__/api/groups/post.test.js create mode 100644 __tests__/api/groups/update.test.js create mode 100644 __tests__/api/policies/delete.test.js create mode 100644 __tests__/api/policies/get.test.js create mode 100644 __tests__/api/policies/post.test.js create mode 100644 __tests__/api/policies/update.test.js create mode 100644 __tests__/api/roles/delete.test.js create mode 100644 __tests__/api/roles/get.test.js create mode 100644 __tests__/api/roles/post.test.js create mode 100644 __tests__/api/roles/update.test.js create mode 100644 __tests__/api/search/post.test.js create mode 100644 __tests__/api/sources/delete.test.js create mode 100644 __tests__/api/sources/get.test.js create mode 100644 __tests__/api/sources/post.test.js create mode 100644 __tests__/api/std_fields/get.test.js create mode 100644 __tests__/api/std_fields/post.test.js create mode 100644 __tests__/api/std_fields/update.test.js create mode 100644 __tests__/api/user-display-fields/get.test.js create mode 100644 __tests__/api/user-requests/delete.test.js create mode 100644 __tests__/api/user-requests/get.test.js create mode 100644 __tests__/api/user-requests/post.test.js create mode 100644 __tests__/api/user-search-history/delete.test.js create mode 100644 __tests__/api/user-search-history/get.test.js create mode 100644 __tests__/api/user-search-history/post.test.js create mode 100644 __tests__/api/users/delete.test.js create mode 100644 __tests__/api/users/get.test.js create mode 100644 __tests__/api/users/post.test.js delete mode 100644 app/api/group/delete.js delete mode 100644 app/api/group/get.js delete mode 100644 app/api/group/index.js delete mode 100644 app/api/group/post.js delete mode 100644 app/api/group/update.js create mode 100644 app/api/groups/delete.js create mode 100644 app/api/groups/get.js create mode 100644 app/api/groups/post.js create mode 100644 app/api/groups/update.js create mode 100644 app/api/policies/delete.js create mode 100644 app/api/policies/get.js create mode 100644 app/api/policies/post.js create mode 100644 app/api/policies/update.js delete mode 100644 app/api/policy/delete.js delete mode 100644 app/api/policy/get.js delete mode 100644 app/api/policy/index.js delete mode 100644 app/api/policy/post.js delete mode 100644 app/api/policy/update.js delete mode 100644 app/api/realm/delete.js delete mode 100644 app/api/realm/get.js delete mode 100644 app/api/realm/index.js delete mode 100644 app/api/realm/post.js delete mode 100644 app/api/realm/put.js delete mode 100644 app/api/role-user/delete.js delete mode 100644 app/api/role-user/get.js delete mode 100644 app/api/role-user/index.js delete mode 100644 app/api/role-user/post.js delete mode 100644 app/api/role-user/put.js delete mode 100644 app/api/roles/index.js create mode 100644 app/api/search/post.js create mode 100644 app/api/sources/delete.js create mode 100644 app/api/sources/get.js create mode 100644 app/api/sources/post.js create mode 100644 app/api/std_fields/delete.js create mode 100644 app/api/std_fields/get.js create mode 100644 app/api/std_fields/post.js create mode 100644 app/api/std_fields/update.js create mode 100644 app/api/user-display-fields/get.js create mode 100644 app/api/user-display-fields/post.js delete mode 100644 app/api/user-fields-display-settings/delete.js delete mode 100644 app/api/user-fields-display-settings/get.js delete mode 100644 app/api/user-fields-display-settings/index.js delete mode 100644 app/api/user-fields-display-settings/post.js delete mode 100644 app/api/user-fields-display-settings/put.js delete mode 100644 app/api/user-profile/index.js delete mode 100644 app/api/user-requests/index.js delete mode 100644 app/api/user-search-history/index.js delete mode 100644 app/api/users/index.js delete mode 100644 app/api/users/put.js delete mode 100644 app/config/db.js create mode 100644 app/config/elk.js create mode 100644 app/config/mongo.js create mode 100644 app/config/postgres.js create mode 100644 app/dal/authService.js create mode 100644 app/dal/elasticService.js create mode 100644 app/dal/keycloakService.js create mode 100644 app/dal/mongoService.js delete mode 100644 app/dal/realmService.js delete mode 100644 app/dal/roleAllocationService.js create mode 100644 app/dal/sourceService.js create mode 100644 app/dal/stdFieldService.js create mode 100644 app/dal/userDisplayFieldsService.js delete mode 100644 app/dal/userFieldsDisplaySettingsService.js delete mode 100644 app/middlewares/errorHandler.js delete mode 100644 app/models/Group.js delete mode 100644 app/models/Policy.js delete mode 100644 app/models/Role.js delete mode 100644 app/models/Source.js delete mode 100644 app/models/StdField.js delete mode 100644 app/models/User.js delete mode 100644 app/models/UserFieldsDisplaySettings.js delete mode 100644 app/models/UserHistory.js delete mode 100644 app/models/UserRequest.js create mode 100644 app/utils/cast.js create mode 100644 app/utils/errorHandler.js rename app/utils/{logger.js => format.js} (90%) delete mode 100644 db.js create mode 100644 docker-compose.yml create mode 100755 entrypoint.sh create mode 100644 gitlab-ci.yml delete mode 100644 init-keycloak.js create mode 100644 jest.config.js create mode 100644 jest.setup.js create mode 100644 jsconfig.json delete mode 100644 keycloak.js delete mode 100644 knexfile.js delete mode 100644 migrate.js create mode 100644 prisma/client.js create mode 100644 prisma/migrations/20241211224745_0_init/migration.sql create mode 100644 prisma/migrations/20241211225353_1_cascade_delete_group_when_admin_deleted/migration.sql create mode 100644 prisma/migrations/20241211235543_2_cascade_delete/migration.sql create mode 100644 prisma/migrations/20241212020153_3_std_field_unique_name/migration.sql create mode 100644 prisma/migrations/20241212233751_4_nullable_and_default_source_indices/migration.sql create mode 100644 prisma/migrations/20241212234126_5_source_providers/migration.sql create mode 100644 prisma/migrations/20241214231527_6_display_fields/migration.sql create mode 100644 prisma/migrations/20250116142811_7_list_url_and_default_fields/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/schema.prisma create mode 100644 prisma/seed.js delete mode 100644 scripts/build.sh delete mode 100644 scripts/migrate.sh delete mode 100644 scripts/up.sh delete mode 100644 scripts/util.sh diff --git a/.dockerignore b/.dockerignore index 40b878d..dea15c8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,7 @@ -node_modules/ \ No newline at end of file +node_modules/ +__tests__/ +jest* +*.log +*.env* +.dockerignore +.gitignore diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..4e95269 --- /dev/null +++ b/.env.development @@ -0,0 +1,33 @@ +NODE_ENV=development +PROCESS_TYPE=web +PORT=4000 +LOG_LEVEL=debug + +KEYCLOAK_HOST= +KEYCLOAK_PORT= +KEYCLOAK_USER= +KEYCLOAK_PASSWORD= +KEYCLOAK_GRANT_TYPE= +KEYCLOAK_REALM= +KEYCLOAK_SERVER_URL= +KEYCLOAK_CLIENT_ID= +KEYCLOAK_CREDENTIALS_SECRET= +KEYCLOAK_SERVER_PUBLIC_KEY= + +DB_HOST= +DB_USER= +DB_PASSWORD= +DB_DATABASE= +DB_PORT= + +IN_SYLVA_EMAIL= +IN_SYLVA_EMAIL_PASSWORD= +IN_SYLVA_SMTP_HOST= +IN_SYLVA_SMTP_PORT= +IN_SYLVA_EMAIL_FROM= +IN_SYLVA_EMAIL_TO= +IN_SYLVA_ADMIN_USERNAME= +IN_SYLVA_ADMIN_PASSWORD= + +BOT_SERVICE_TOKEN= +BOT_SERVICE_CHANNEL= \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index fcfc329..ef33cbd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,28 +1,25 @@ -FROM node:14-buster-slim +FROM node:20.16.0 -RUN apt update && apt install -y git openssh-client +# Update the package list and install packages +RUN wget -qO- https://www.mongodb.org/static/pgp/server-8.0.asc | tee /etc/apt/trusted.gpg.d/server-8.0.asc +RUN echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/8.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-8.0.list -RUN npm install --global pm2 +RUN apt-get update && apt-get install -y git mongodb-mongosh && rm -rf /var/lib/apt/lists/* -EXPOSE 22 -ARG SSH_KEY -ARG SSH_KEY_PASSPHRASE -RUN chmod go-w /root +# Install PM2 +RUN yarn global add pm2 -RUN mkdir -p /root/.ssh && \ - chmod 600 /root/.ssh +# Set up the app +WORKDIR /app +COPY package.json ./ -RUN echo "$SSH_KEY" >> /root/.ssh/id_rsa && \ - echo "$SSH_KEY_PASSPHRASE" >> /root/.ssh/id_rsa.pub +# Install dependencies +RUN yarn install -RUN chmod -R 600 /root/.ssh/id_rsa && \ - chmod -R 600 /root/.ssh/id_rsa.pub +# Copy the rest of the application code +COPY . . -RUN ssh-keyscan -Ht rsa forgemia.inra.fr,147.100.164.13 >> ~/.ssh/known_hosts - -COPY . /app - -RUN cd /app && yarn +ENTRYPOINT [ "/bin/bash", "./entrypoint.sh"] CMD [ "pm2-runtime","--name","in-sylva.gatekeeper", "yarn start:prod"] diff --git a/__test__/models/group.test.js b/__test__/models/group.test.js deleted file mode 100644 index d5fbb11..0000000 --- a/__test__/models/group.test.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' - -const { Group, GroupsPolicy, GroupUser } = require('../../app/models/Group') - -describe("Testing 1Group entitiy", () => { - it('Insert new Group', async () => { - await Group.create({ - name: "Test name for the Group 2.", - name: "Test description for the Group 2.", - user_id: 1 - }) - }) -}) - -describe("Testing 2GroupsPolicy entity", () => { - it("Insert new GroupsPolicy", async () => { - await GroupsPolicy.create({ - group_id: 1, - policy_id: 1 - }) - }) -}) - -describe("Testing 3GroupUser entity", () => { - it("Insert new GroupUser", async () => { - await GroupUser.create({ - group_id: 1, - user_id: 1 - }) - }) -}) \ No newline at end of file diff --git a/__test__/models/policy.test.js b/__test__/models/policy.test.js deleted file mode 100644 index 2a2b7ac..0000000 --- a/__test__/models/policy.test.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const { Policy } = require('../../app/models/Policy') - -describe("Testing Policy entity", () => { - it("Insert new Policy", async () => { - await Policy.create({ - name: "New Policy", - source_id: 1, - is_default: false, - user_id: 1 - }) - }) -}) - - -describe("Testing FPolicy entity", () => { - it("Fetch new FPolicies", async () => { - - }) -}) \ No newline at end of file diff --git a/__test__/models/policyField.test.js b/__test__/models/policyField.test.js deleted file mode 100644 index 7d08aea..0000000 --- a/__test__/models/policyField.test.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -const { PolicyField } = require('../../app/models/Policy') - -describe('Testing CPolicyField entity', () => { - it("Insert new PolicyField", async () => { - - await PolicyField.create({ - policy_id: 1, - std_field_id: 1 - }) - }) -}) \ No newline at end of file diff --git a/__test__/models/role.test.js b/__test__/models/role.test.js deleted file mode 100644 index 5909d0f..0000000 --- a/__test__/models/role.test.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict' -const { Role, RoleUser } = require('../../app/models/Role') - -describe("Testing Role entity", () => { - - it("Delete roles", async () => { - await Role.destroy() - }), - - it("Insert new Role", async () => { - await Role.create({ - name: "admin", - description: "admin description" - }) - }) - - it("Insert new CRoleUser", async () => { - await RoleUser.create({ - role_id: 1, - kc_id: "3026ee0c-e1d3-4ce8-9289-2507ef797523" - }) - }) - -}) \ No newline at end of file diff --git a/__test__/models/searchHistory.test.js b/__test__/models/searchHistory.test.js deleted file mode 100644 index 6246787..0000000 --- a/__test__/models/searchHistory.test.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict' - -const { SearchHistory } = require('../../app/models/UserHistory') - -describe("Testing searchHistory entity with Sequelize orm", () => { - - it("Insert new searchHistory", async () => { - await SearchHistory.create({ - kc_id: "fe3b88e4-a231-47a2-9d9e-18bd9be0d637", - query: "query test", - name: "name test", - ui_structure: "ui_structure", - description: "description test" - }).then(c => { - return c - }) - }) - - - /*it("test findAll searchHistories", async () => { - - const sHistories = await SearchHistory.findAll({ - where: { - kc_id: "fe3b88e4-a231-47a2-9d9e-18bd9be0d637" - } - }) - - console.log(typeof sHistories) - expect(sHistories.length > 0) - }) */ - /* - it("test delete searchHistory", async () => { - const result = await SearchHistory.destroy({ - where: { - id:'1' - } - }) - - console.log(result) - }) */ - -}) - - diff --git a/__test__/models/source.test.js b/__test__/models/source.test.js deleted file mode 100644 index 692e863..0000000 --- a/__test__/models/source.test.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -const { Source } = require('../../app/models/Source') - -describe("Testing Source entity", () => { - it('insert new Source', async () => { - await Source.create({ - name: "test source name 2", - description : "test source description" - }) - }) -}) - diff --git a/__test__/models/stdField.test.js b/__test__/models/stdField.test.js deleted file mode 100644 index 135fc55..0000000 --- a/__test__/models/stdField.test.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const {StdField } = require('../../app/models/StdField') - -describe('Testing StdField entity', () => { - it("insert StdField entity", async () => { - await StdField.create({ - category: "category", - field_name: "field_name", - definition_and_comment: "definition_and_comment", - obligation_or_condition: "obligation_or_condition", - cardinality: "cardinality", - field_type:"field_type", - values:"values", - ispublic: true, - isoptional: false - }).then(c => { - return c - }) - }) -}) \ No newline at end of file diff --git a/__tests__/api/groups/get.test.js b/__tests__/api/groups/get.test.js new file mode 100644 index 0000000..f38c991 --- /dev/null +++ b/__tests__/api/groups/get.test.js @@ -0,0 +1,88 @@ +const { + getGroupHandler, + getAllGroupsHandler, +} = require("@/app/api/groups/get"); +const { getAllGroups, getGroup } = require("@/app/dal/groupService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/groupService"); +jest.mock("@/app/utils/errorHandler"); + +describe("getGroupHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + await getGroupHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should get group and set status to 200", async () => { + ctx.params.id = "group123"; + const mockResponse = { id: "group123", name: "Test Group" }; + getGroup.mockResolvedValue(mockResponse); + + await getGroupHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "group123"; + getGroup.mockImplementation(() => { + throw error; + }); + + await getGroupHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getAllGroupsHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + body: null, + status: null, + }; + }); + + it("should get all groups and set status to 200", async () => { + const mockResponse = [ + { id: 1, name: "Group 1" }, + { id: 2, name: "Group 2" }, + ]; + getAllGroups.mockResolvedValue(mockResponse); + + await getAllGroupsHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + getAllGroups.mockImplementation(() => { + throw error; + }); + + await getAllGroupsHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/groups/post.test.js b/__tests__/api/groups/post.test.js new file mode 100644 index 0000000..d1ee165 --- /dev/null +++ b/__tests__/api/groups/post.test.js @@ -0,0 +1,67 @@ +const { createGroupHandler } = require("@/app/api/groups/post"); +const { createGroup } = require("@/app/dal/groupService"); +const { + handleMissingParameters, + handleInternalError, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/groupService"); +jest.mock("@/app/utils/errorHandler"); + +describe("createGroupHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when name, description, or kc_id is missing", async () => { + await createGroupHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["name", "description", "kc_id"], + ctx + ); + }); + + it("should create group and set status to 201", async () => { + ctx.request.body = { + name: "Test Group", + description: "Test Description", + kc_id: "kc123", + }; + const mockResponse = { + id: 1, + name: "Test Group", + description: "Test Description", + }; + createGroup.mockResolvedValue(mockResponse); + + await createGroupHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.request.body = { + name: "Test Group", + description: "Test Description", + kc_id: "kc123", + }; + createGroup.mockImplementation(() => { + throw error; + }); + + await createGroupHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/groups/update.test.js b/__tests__/api/groups/update.test.js new file mode 100644 index 0000000..672c030 --- /dev/null +++ b/__tests__/api/groups/update.test.js @@ -0,0 +1,227 @@ +// update.test.js + +const { + updateGroupHandler, + addUserToGroupHandler, + removeUserFromGroupHandler, + addPolicyToGroupHandler, +} = require("@/app/api/groups/update"); +const { + createGroup, + addUserToGroup, + removeUserFromGroup, + addPolicyToGroup, + updateGroup, +} = require("@/app/dal/groupService"); +const { + handleMissingParameters, + handleInternalError, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/groupService"); +jest.mock("@/app/utils/errorHandler"); + +describe("updateGroupHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when name, description, or id is missing", async () => { + await updateGroupHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["name", "description", "id"], + ctx + ); + }); + + it("should update group and set status to 201", async () => { + ctx.params.id = "group123"; + ctx.request.body = { + name: "Updated Group", + description: "Updated Description", + }; + const mockResponse = { + id: "group123", + name: "Updated Group", + description: "Updated Description", + }; + updateGroup.mockResolvedValue(mockResponse); + + await updateGroupHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "group123"; + ctx.request.body = { + name: "Updated Group", + description: "Updated Description", + }; + updateGroup.mockImplementation(() => { + throw error; + }); + + await updateGroupHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("addUserToGroupHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when kc_id or groupId is missing", async () => { + await addUserToGroupHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id", "id"], ctx); + }); + + it("should add user to group and set status to 201", async () => { + ctx.params.id = "group123"; + ctx.request.body = { + kc_id: "kc123", + }; + const mockResponse = { kc_id: "kc123", groupId: "group123" }; + addUserToGroup.mockResolvedValue(mockResponse); + + await addUserToGroupHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "group123"; + ctx.request.body = { + kc_id: "kc123", + }; + addUserToGroup.mockImplementation(() => { + throw error; + }); + + await addUserToGroupHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("removeUserFromGroupHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when kc_id or id is missing", async () => { + await removeUserFromGroupHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id", "id"], ctx); + }); + + it("should remove user from group and set status to 204", async () => { + ctx.params = { kc_id: "kc123", id: "group123" }; + removeUserFromGroup.mockResolvedValue({}); + + await removeUserFromGroupHandler(ctx); + + expect(ctx.body).toEqual(null); + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params = { kc_id: "kc123", id: "group123" }; + removeUserFromGroup.mockImplementation(() => { + throw error; + }); + + await removeUserFromGroupHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("addPolicyToGroupHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when policy_id or id is missing", async () => { + await addPolicyToGroupHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["policy_id", "id"], + ctx + ); + }); + + it("should add policy to group and set status to 201", async () => { + ctx.params.id = "group123"; + ctx.request.body = { + policy_id: "policy123", + }; + const mockResponse = { policy_id: "policy123", group_id: "group123" }; + addPolicyToGroup.mockResolvedValue(mockResponse); + + await addPolicyToGroupHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "group123"; + ctx.request.body = { + policy_id: "policy123", + }; + addPolicyToGroup.mockImplementation(() => { + throw error; + }); + + await addPolicyToGroupHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/policies/delete.test.js b/__tests__/api/policies/delete.test.js new file mode 100644 index 0000000..9017388 --- /dev/null +++ b/__tests__/api/policies/delete.test.js @@ -0,0 +1,51 @@ +// delete.test.js + +const { deletePolicyHandler } = require("@/app/api/policies/delete"); +const { deletePolicy } = require("@/app/dal/policyService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/policyService"); +jest.mock("@/app/utils/errorHandler"); + +describe("deletePolicyHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + await deletePolicyHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should delete policy and set status to 204", async () => { + ctx.params.id = "policy123"; + deletePolicy.mockResolvedValue(null); + + await deletePolicyHandler(ctx); + + expect(ctx.body).toBeNull(); + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "policy123"; + deletePolicy.mockImplementation(() => { + throw error; + }); + + await deletePolicyHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/policies/get.test.js b/__tests__/api/policies/get.test.js new file mode 100644 index 0000000..12553e5 --- /dev/null +++ b/__tests__/api/policies/get.test.js @@ -0,0 +1,88 @@ +const { + getAllPoliciesHandler, + getPolicyHandler, +} = require("@/app/api/policies/get"); +const { getAllPolicies, getPolicy } = require("@/app/dal/policyService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/policyService"); +jest.mock("@/app/utils/errorHandler"); + +describe("getAllPoliciesHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + body: null, + status: null, + }; + }); + + it("should get all policies and set status to 200", async () => { + const mockResponse = [ + { id: 1, name: "Policy 1" }, + { id: 2, name: "Policy 2" }, + ]; + getAllPolicies.mockResolvedValue(mockResponse); + + await getAllPoliciesHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + getAllPolicies.mockImplementation(() => { + throw error; + }); + + await getAllPoliciesHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getPolicyHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + await getPolicyHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should get policy and set status to 200", async () => { + ctx.params.id = "policy123"; + const mockResponse = { id: "policy123", name: "Test Policy" }; + getPolicy.mockResolvedValue(mockResponse); + + await getPolicyHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "policy123"; + getPolicy.mockImplementation(() => { + throw error; + }); + + await getPolicyHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/policies/post.test.js b/__tests__/api/policies/post.test.js new file mode 100644 index 0000000..72dc9d2 --- /dev/null +++ b/__tests__/api/policies/post.test.js @@ -0,0 +1,66 @@ +const { createPolicyHandler } = require("@/app/api/policies/post"); +const { createPolicy } = require("@/app/dal/policyService"); +const { + handleMissingParameters, + handleInternalError, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/policyService"); +jest.mock("@/app/utils/errorHandler"); + +describe("createPolicyHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when name, source_id, or kc_id is missing", async () => { + await createPolicyHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["name", "kc_id"], + ctx + ); + }); + + it("should create policy and set status to 201", async () => { + ctx.request.body = { + name: "Test Policy", + kc_id: "kc123", + }; + const mockResponse = { + id: 1, + name: "Test Policy", + kc_id: "kc123", + }; + createPolicy.mockResolvedValue(mockResponse); + + await createPolicyHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.request.body = { + name: "Test Policy", + source_id: "source123", + kc_id: "kc123", + }; + createPolicy.mockImplementation(() => { + throw error; + }); + + await createPolicyHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/policies/update.test.js b/__tests__/api/policies/update.test.js new file mode 100644 index 0000000..455782f --- /dev/null +++ b/__tests__/api/policies/update.test.js @@ -0,0 +1,271 @@ +// update.test.js + +const { + updatePolicyHandler, + addFieldToPolicyHandler, + removeFieldFromPolicyHandler, + addSourceToPolicyHandler, + removeSourceFromPolicyHandler, +} = require("@/app/api/policies/update"); +const { + updatePolicy, + addFieldToPolicy, + removeFieldFromPolicy, + addSourceToPolicy, + removeSourceFromPolicy, +} = require("@/app/dal/policyService"); +const { + handleMissingParameters, + handleInternalError, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/policyService"); +jest.mock("@/app/utils/errorHandler"); + +describe("updatePolicyHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + request: { + body: {}, + }, + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id, name, or isDefault is missing", async () => { + await updatePolicyHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["id", "name", "isDefault"], + ctx + ); + }); + + it("should update policy and set status to 204", async () => { + ctx.params.id = "policy123"; + ctx.request.body = { + name: "Updated Policy", + isDefault: true, + }; + const mockResponse = { + id: "policy123", + name: "Updated Policy", + isDefault: true, + }; + updatePolicy.mockResolvedValue(mockResponse); + + await updatePolicyHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "policy123"; + ctx.request.body = { + name: "Updated Policy", + isDefault: true, + }; + updatePolicy.mockImplementation(() => { + throw error; + }); + + await updatePolicyHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("addFieldToPolicyHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id or field_id is missing", async () => { + await addFieldToPolicyHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["id", "field_id"], + ctx + ); + }); + + it("should add field to policy and set status to 201", async () => { + ctx.params.id = "policy123"; + ctx.request.body.field_id = "field123"; + const mockResponse = { success: true }; + addFieldToPolicy.mockResolvedValue(mockResponse); + + await addFieldToPolicyHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "policy123"; + ctx.request.body.field_id = "field123"; + addFieldToPolicy.mockImplementation(() => { + throw error; + }); + + await addFieldToPolicyHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("removeFieldFromPolicyHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id or policy_id is missing", async () => { + await removeFieldFromPolicyHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["id", "field_id"], + ctx + ); + }); + + it("should remove field from policy and set status to 204", async () => { + ctx.params.id = "policy123"; + ctx.params.field_id = "field123"; + const mockResponse = { success: true }; + removeFieldFromPolicy.mockResolvedValue(mockResponse); + + await removeFieldFromPolicyHandler(ctx); + + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "policy123"; + ctx.params.field_id = "field123"; + removeFieldFromPolicy.mockImplementation(() => { + throw error; + }); + + await removeFieldFromPolicyHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("addSourceToPolicyHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id or source_id is missing", async () => { + await addSourceToPolicyHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["id", "source_id"], + ctx + ); + }); + + it("should add source to policy and set status to 201", async () => { + ctx.params.id = "policy123"; + ctx.request.body.source_id = "source123"; + const mockResponse = { success: true }; + addSourceToPolicy.mockResolvedValue(mockResponse); + + await addSourceToPolicyHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "policy123"; + ctx.request.body.source_id = "source123"; + addSourceToPolicy.mockImplementation(() => { + throw error; + }); + + await addSourceToPolicyHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("removeSourceFromPolicyHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id or source_id is missing", async () => { + await removeSourceFromPolicyHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["id", "source_id"], + ctx + ); + }); + + it("should add source to policy and set status to 201", async () => { + ctx.params = { id: "policy123", source_id: "source123" }; + removeSourceFromPolicy.mockResolvedValue(null); + + await removeSourceFromPolicyHandler(ctx); + + expect(ctx.body).toEqual(null); + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params = { id: "policy123", source_id: "source123" }; + removeSourceFromPolicy.mockImplementation(() => { + throw error; + }); + + await removeSourceFromPolicyHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/roles/delete.test.js b/__tests__/api/roles/delete.test.js new file mode 100644 index 0000000..be4c9aa --- /dev/null +++ b/__tests__/api/roles/delete.test.js @@ -0,0 +1,49 @@ +const { deleteRoleHandler } = require("@/app/api/roles/delete"); +const { deleteRole } = require("@/app/dal/roleService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/roleService"); +jest.mock("@/app/utils/errorHandler"); + +describe("deleteRoleHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + await deleteRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should delete role and set status to 204", async () => { + ctx.params.id = "role123"; + deleteRole.mockResolvedValue(null); + + await deleteRoleHandler(ctx); + + expect(ctx.body).toBeNull(); + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "role123"; + deleteRole.mockImplementation(() => { + throw error; + }); + + await deleteRoleHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/roles/get.test.js b/__tests__/api/roles/get.test.js new file mode 100644 index 0000000..f31ac4c --- /dev/null +++ b/__tests__/api/roles/get.test.js @@ -0,0 +1,132 @@ +const { + getRolesHandler, + getRoleHandler, + getUserRolesHandler, +} = require("@/app/api/roles/get"); +const { getRoles, getRole, getUserRoles } = require("@/app/dal/roleService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/roleService"); +jest.mock("@/app/utils/errorHandler"); + +describe("getRolesHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + body: null, + status: null, + }; + }); + + it("should get roles and set status to 200", async () => { + const mockResponse = [{ id: "role1" }, { id: "role2" }]; + getRoles.mockResolvedValue(mockResponse); + + await getRolesHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + getRoles.mockImplementation(() => { + throw error; + }); + + await getRolesHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getRoleHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + await getRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should get role by id and set status to 200", async () => { + ctx.params.id = "role123"; + const mockResponse = { id: "role123", name: "Role Name" }; + getRole.mockResolvedValue(mockResponse); + + await getRoleHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "role123"; + getRole.mockImplementation(() => { + throw error; + }); + + await getRoleHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getUserRolesHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when kc_id is missing", async () => { + await getUserRolesHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should get user roles by kc_id and set status to 200", async () => { + ctx.request.body.kc_id = "kc123"; + const mockResponse = [{ id: "role1" }, { id: "role2" }]; + getUserRoles.mockResolvedValue(mockResponse); + + await getUserRolesHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.request.body.kc_id = "kc123"; + getUserRoles.mockImplementation(() => { + throw error; + }); + + await getUserRolesHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/roles/post.test.js b/__tests__/api/roles/post.test.js new file mode 100644 index 0000000..e08ff6f --- /dev/null +++ b/__tests__/api/roles/post.test.js @@ -0,0 +1,195 @@ +const { + createRoleHandler, + addUserToRoleHandler, + removeUserFromRoleHandler, +} = require("@/app/api/roles/post"); +const { + createRole, + addUserToRole, + removeUserFromRole, +} = require("@/app/dal/roleService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/roleService"); +jest.mock("@/app/utils/errorHandler"); + +describe("createRoleHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when name and description are missing", async () => { + await createRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["name", "description"], + ctx + ); + }); + + it("should handle missing parameters when name is missing", async () => { + ctx.request.body.description = "A description"; + + await createRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["name"], ctx); + }); + + it("should handle missing parameters when description is missing", async () => { + ctx.request.body.name = "A name"; + + await createRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["description"], ctx); + }); + + it("should create role and set status to 201", async () => { + ctx.request.body = { name: "A name", description: "A description" }; + const mockResponse = { + id: "role123", + name: "A name", + description: "A description", + }; + createRole.mockResolvedValue(mockResponse); + + await createRoleHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.request.body = { name: "A name", description: "A description" }; + createRole.mockImplementation(() => { + throw error; + }); + + await createRoleHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("addUserToRoleHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + ctx.request.body.kc_id = "kc123"; + + await addUserToRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should handle missing parameters when kc_id is missing", async () => { + ctx.params.id = "role123"; + + await addUserToRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should add user to role and set status to 201", async () => { + ctx.params.id = "role123"; + ctx.request.body.kc_id = "kc123"; + const mockResponse = { + id: "role123", + kc_id: "kc123", + }; + addUserToRole.mockResolvedValue(mockResponse); + + await addUserToRoleHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "role123"; + ctx.request.body.kc_id = "kc123"; + addUserToRole.mockImplementation(() => { + throw error; + }); + + await addUserToRoleHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("removeUserFromRoleHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + ctx.params.kc_id = "kc123"; + + await removeUserFromRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should handle missing parameters when kc_id is missing", async () => { + ctx.params.id = "role123"; + + await removeUserFromRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should remove user from role and set status to 204", async () => { + ctx.params.id = "role123"; + ctx.params.kc_id = "kc123"; + + await removeUserFromRoleHandler(ctx); + + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "role123"; + ctx.params.kc_id = "kc123"; + removeUserFromRole.mockImplementation(() => { + throw error; + }); + + await removeUserFromRoleHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/roles/update.test.js b/__tests__/api/roles/update.test.js new file mode 100644 index 0000000..5850a43 --- /dev/null +++ b/__tests__/api/roles/update.test.js @@ -0,0 +1,88 @@ +const { updateRoleHandler } = require("@/app/api/roles/put"); +const { updateRole } = require("@/app/dal/roleService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/roleService"); +jest.mock("@/app/utils/errorHandler"); + +describe("updateRoleHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id, name, and description are missing", async () => { + await updateRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["id", "name", "description"], + ctx + ); + }); + + it("should handle missing parameters when id is missing", async () => { + ctx.request.body = { name: "Role Name", description: "Role Description" }; + + await updateRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should handle missing parameters when name is missing", async () => { + ctx.params.id = "role123"; + ctx.request.body.description = "Role Description"; + + await updateRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["name"], ctx); + }); + + it("should handle missing parameters when description is missing", async () => { + ctx.params.id = "role123"; + ctx.request.body.name = "Role Name"; + + await updateRoleHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["description"], ctx); + }); + + it("should update role and set status to 204", async () => { + ctx.params.id = "role123"; + ctx.request.body = { name: "Role Name", description: "Role Description" }; + const mockResponse = { + id: "role123", + name: "Role Name", + description: "Role Description", + }; + updateRole.mockResolvedValue(mockResponse); + + await updateRoleHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "role123"; + ctx.request.body = { name: "Role Name", description: "Role Description" }; + updateRole.mockImplementation(() => { + throw error; + }); + + await updateRoleHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/search/post.test.js b/__tests__/api/search/post.test.js new file mode 100644 index 0000000..d844c8b --- /dev/null +++ b/__tests__/api/search/post.test.js @@ -0,0 +1,67 @@ +const { getQueryResultsHandler } = require("@/app/api/search/post"); +const { search } = require("@/app/dal/elasticService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/elasticService"); +jest.mock("@/app/utils/errorHandler"); + +describe("getQueryResultsHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when query is missing", async () => { + await getQueryResultsHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["query", "sourcesId", "fieldsId"], + ctx + ); + }); + + it("should get query results and set status to 200", async () => { + ctx.request.body = { + query: "test query", + sourcesId: [1, 2, 3], + fieldsId: [1, 2, 3], + index: "test_index", + scroll_id: "test_scroll_id", + }; + const mockResponse = { hits: { hits: [] } }; + search.mockResolvedValue(mockResponse); + + await getQueryResultsHandler(ctx); + + expect(ctx.body).toEqual([]); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.request.body = { + query: "test query", + sourcesId: [1, 2, 3], + fieldsId: [1, 2, 3], + index: "test_index", + scroll_id: "test_scroll_id", + }; + search.mockImplementation(() => { + throw error; + }); + + await getQueryResultsHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/sources/delete.test.js b/__tests__/api/sources/delete.test.js new file mode 100644 index 0000000..113ec10 --- /dev/null +++ b/__tests__/api/sources/delete.test.js @@ -0,0 +1,50 @@ +const { deleteSourceHandler } = require("@/app/api/sources/delete"); +const { deleteSource } = require("@/app/dal/sourceService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/sourceService"); +jest.mock("@/app/utils/errorHandler"); + +describe("deleteSourceHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + await deleteSourceHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should delete source and set status to 200", async () => { + ctx.params.id = "source123"; + const mockResponse = { success: true }; + deleteSource.mockResolvedValue(mockResponse); + + await deleteSourceHandler(ctx); + + expect(ctx.body).toEqual(null); + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "source123"; + deleteSource.mockImplementation(() => { + throw error; + }); + + await deleteSourceHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/sources/get.test.js b/__tests__/api/sources/get.test.js new file mode 100644 index 0000000..f186abe --- /dev/null +++ b/__tests__/api/sources/get.test.js @@ -0,0 +1,208 @@ +const { + getSourcesHandler, + getSourceHandler, + getSourcesByProviderHandler, + getIndexedSourcesHandler, + getIndexedSourcesByProviderHandler, +} = require("@/app/api/sources/get"); +const { + getSources, + getSource, + getSourcesByProvider, + getIndexedSources, + getIndexedSourcesByProvider, +} = require("@/app/dal/sourceService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/sourceService"); +jest.mock("@/app/utils/errorHandler"); + +describe("getSourcesHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + body: null, + status: null, + }; + }); + + it("should get sources and set status to 200", async () => { + const mockResponse = [{ id: "source1" }, { id: "source2" }]; + getSources.mockResolvedValue(mockResponse); + + await getSourcesHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + getSources.mockImplementation(() => { + throw error; + }); + + await getSourcesHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getSourceHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + await getSourceHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should get source by id and set status to 200", async () => { + ctx.params.id = "source123"; + const mockResponse = { id: "source123", name: "Source Name" }; + getSource.mockResolvedValue(mockResponse); + + await getSourceHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "source123"; + getSource.mockImplementation(() => { + throw error; + }); + + await getSourceHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getSourcesByProviderHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when kc_id is missing", async () => { + await getSourcesByProviderHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should get sources by provider and set status to 200", async () => { + ctx.params.kc_id = "kc123"; + const mockResponse = [{ id: "source1" }, { id: "source2" }]; + getSourcesByProvider.mockResolvedValue(mockResponse); + + await getSourcesByProviderHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.kc_id = "kc123"; + getSourcesByProvider.mockImplementation(() => { + throw error; + }); + + await getSourcesByProviderHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getIndexedSourcesHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + body: null, + status: null, + }; + }); + + it("should get indexed sources and set status to 200", async () => { + const mockResponse = [{ id: "source1" }, { id: "source2" }]; + getIndexedSources.mockResolvedValue(mockResponse); + + await getIndexedSourcesHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + getIndexedSources.mockImplementation(() => { + throw error; + }); + + await getIndexedSourcesHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getIndexedSourcesByProviderHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when kc_id is missing", async () => { + await getIndexedSourcesByProviderHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should get indexed sources by provider and set status to 200", async () => { + ctx.params.kc_id = "kc123"; + const mockResponse = [{ id: "source1" }, { id: "source2" }]; + getIndexedSourcesByProvider.mockResolvedValue(mockResponse); + + await getIndexedSourcesByProviderHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.kc_id = "kc123"; + getIndexedSourcesByProvider.mockImplementation(() => { + throw error; + }); + + await getIndexedSourcesByProviderHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/sources/post.test.js b/__tests__/api/sources/post.test.js new file mode 100644 index 0000000..7dbd15a --- /dev/null +++ b/__tests__/api/sources/post.test.js @@ -0,0 +1,69 @@ +const { createSourceHandler } = require("@/app/api/sources/post"); +const { createSource } = require("@/app/dal/sourceService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/sourceService"); +jest.mock("@/app/utils/errorHandler"); + +describe("createSourceHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when name, description, metaUrfms and kc_id are missing", async () => { + await createSourceHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["name", "description", "kc_id", "metaUrfms"], + ctx + ); + }); + + it("should create source and set status to 201", async () => { + ctx.request.body = { + name: "A name", + description: "A description", + metaUrfms: "metaUrfms", + kc_id: "kc_id", + }; + const mockResponse = { + id: "source123", + name: "A name", + description: "A description", + }; + createSource.mockResolvedValue(mockResponse); + + await createSourceHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.request.body = { + name: "A name", + description: "A description", + metaUrfms: "metaUrfms", + kc_id: "kc_id", + }; + createSource.mockImplementation(() => { + throw error; + }); + + await createSourceHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/std_fields/get.test.js b/__tests__/api/std_fields/get.test.js new file mode 100644 index 0000000..19908a6 --- /dev/null +++ b/__tests__/api/std_fields/get.test.js @@ -0,0 +1,90 @@ +// get.test.js + +const { + getStdFieldsHandler, + getStdFieldHandler, +} = require("@/app/api/std_fields/get"); +const { getStdFields, getStdField } = require("@/app/dal/stdFieldService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/stdFieldService"); +jest.mock("@/app/utils/errorHandler"); + +describe("getStdFieldsHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + body: null, + status: null, + }; + }); + + it("should get standard fields and set status to 200", async () => { + const mockResponse = [{ id: "field1" }, { id: "field2" }]; + getStdFields.mockResolvedValue(mockResponse); + + await getStdFieldsHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + getStdFields.mockImplementation(() => { + throw error; + }); + + await getStdFieldsHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getStdFieldHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + await getStdFieldHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should get standard field by id and set status to 200", async () => { + ctx.params.id = "field123"; + const mockResponse = { name: "Field Name" }; + getStdField.mockResolvedValue(mockResponse); + + await getStdFieldHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "field123"; + getStdField.mockImplementation(() => { + throw error; + }); + + await getStdFieldHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/std_fields/post.test.js b/__tests__/api/std_fields/post.test.js new file mode 100644 index 0000000..3e12d77 --- /dev/null +++ b/__tests__/api/std_fields/post.test.js @@ -0,0 +1,46 @@ +const { createOrUpdateStdFieldHandler } = require("@/app/api/std_fields/post"); +const { createOrUpdateStdField } = require("@/app/dal/stdFieldService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/stdFieldService"); +jest.mock("@/app/utils/errorHandler"); + +describe("createOrUpdateStdFieldHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should create standard field and set status to 201", async () => { + ctx.request.body.name = "New Field"; + const mockResponse = { id: "field123", name: "New Field" }; + createOrUpdateStdField.mockResolvedValue(mockResponse); + + await createOrUpdateStdFieldHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.request.body.name = "New Field"; + createOrUpdateStdField.mockImplementation(() => { + throw error; + }); + + await createOrUpdateStdFieldHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/std_fields/update.test.js b/__tests__/api/std_fields/update.test.js new file mode 100644 index 0000000..e90bfc7 --- /dev/null +++ b/__tests__/api/std_fields/update.test.js @@ -0,0 +1,55 @@ +const { updateStdFieldHandler } = require("@/app/api/std_fields/update"); +const { createOrUpdateStdField } = require("@/app/dal/stdFieldService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/stdFieldService"); +jest.mock("@/app/utils/errorHandler"); + +describe("updateStdFieldHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + await updateStdFieldHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should update standard field and set status to 204", async () => { + ctx.params.id = "field123"; + ctx.request.body = { name: "Updated Field" }; + const mockResponse = { id: "field123", name: "Updated Field" }; + createOrUpdateStdField.mockResolvedValue(mockResponse); + + await updateStdFieldHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "field123"; + ctx.request.body = { name: "Updated Field" }; + createOrUpdateStdField.mockImplementation(() => { + throw error; + }); + + await updateStdFieldHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/user-display-fields/get.test.js b/__tests__/api/user-display-fields/get.test.js new file mode 100644 index 0000000..94ecf8d --- /dev/null +++ b/__tests__/api/user-display-fields/get.test.js @@ -0,0 +1,55 @@ +const { + handleInternalError, + handleMissingParameters, +} = require("../../../app/utils/errorHandler"); +const { + getUserDisplayFields, +} = require("../../../app/dal/userDisplayFieldsService"); +const { + getUserDisplayFieldsHandler, +} = require("../../../app/api/user-display-fields/get"); + +jest.mock("@/app/dal/userService"); +jest.mock("@/app/dal/userDisplayFieldsService"); +jest.mock("@/app/utils/errorHandler"); + +describe("getUserDisplayFieldsHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when kc_id is missing", async () => { + await getUserDisplayFieldsHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should get user display fields and set status to 200", async () => { + ctx.params.kc_id = "user123"; + const mockResponse = { field1: "value1", field2: "value2" }; + getUserDisplayFields.mockResolvedValue(mockResponse); + + await getUserDisplayFieldsHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.kc_id = "user123"; + getUserDisplayFields.mockImplementation(() => { + throw error; + }); + + await getUserDisplayFieldsHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/user-requests/delete.test.js b/__tests__/api/user-requests/delete.test.js new file mode 100644 index 0000000..dec90c1 --- /dev/null +++ b/__tests__/api/user-requests/delete.test.js @@ -0,0 +1,51 @@ +// delete.test.js + +const { deleteRequestHandler } = require("@/app/api/user-requests/delete"); +const { deleteRequest } = require("@/app/dal/userRequestService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/userRequestService"); +jest.mock("@/app/utils/errorHandler"); + +describe("deleteRequestHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + await deleteRequestHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should delete request and set status to 204", async () => { + ctx.params.id = "req123"; + deleteRequest.mockResolvedValue({}); + + await deleteRequestHandler(ctx); + + expect(ctx.body).toEqual(null); + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "req123"; + deleteRequest.mockImplementation(() => { + throw error; + }); + + await deleteRequestHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/user-requests/get.test.js b/__tests__/api/user-requests/get.test.js new file mode 100644 index 0000000..56723de --- /dev/null +++ b/__tests__/api/user-requests/get.test.js @@ -0,0 +1,165 @@ +// get.test.js + +const { + getAllRequestsHandler, + getAllPendingRequestsHandler, + getAllUserRequestsHandler, + getAllPendingUserRequestsHandler, +} = require("@/app/api/user-requests/get"); +const { + getAllRequests, + getAllPendingRequests, +} = require("@/app/dal/userRequestService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/userRequestService"); +jest.mock("@/app/utils/errorHandler"); + +describe("getAllRequestsHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + body: null, + status: null, + }; + }); + + it("should get all requests and set status to 200", async () => { + const mockResponse = [{ id: 1, request: "test request" }]; + getAllRequests.mockResolvedValue(mockResponse); + + await getAllRequestsHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + getAllRequests.mockImplementation(() => { + throw error; + }); + + await getAllRequestsHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getAllPendingRequestsHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + body: null, + status: null, + }; + }); + + it("should get all pending requests and set status to 200", async () => { + const mockResponse = [{ id: 1, request: "pending request" }]; + getAllPendingRequests.mockResolvedValue(mockResponse); + + await getAllPendingRequestsHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + getAllPendingRequests.mockImplementation(() => { + throw error; + }); + + await getAllPendingRequestsHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getAllUserRequestsHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + body: null, + status: null, + params: { kc_id: "test-id" }, + }; + }); + + it("should get all requests for a user and set status to 200", async () => { + const mockResponse = [{ id: 1, request: "test request" }]; + getAllRequests.mockResolvedValue(mockResponse); + + await getAllUserRequestsHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle missing parameters", async () => { + ctx.params.kc_id = null; + + await getAllUserRequestsHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + getAllRequests.mockImplementation(() => { + throw error; + }); + + await getAllUserRequestsHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("getAllPendingUserRequestsHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + body: null, + status: null, + params: { kc_id: "test-id" }, + }; + }); + + it("should get all pending requests for a user and set status to 200", async () => { + const mockResponse = [{ id: 1, request: "pending request" }]; + getAllPendingRequests.mockResolvedValue(mockResponse); + + await getAllPendingUserRequestsHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(200); + }); + + it("should handle missing parameters", async () => { + ctx.params.kc_id = null; + + await getAllPendingUserRequestsHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + getAllPendingRequests.mockImplementation(() => { + throw error; + }); + + await getAllPendingUserRequestsHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/user-requests/post.test.js b/__tests__/api/user-requests/post.test.js new file mode 100644 index 0000000..0e574b0 --- /dev/null +++ b/__tests__/api/user-requests/post.test.js @@ -0,0 +1,137 @@ +// post.test.js + +const { + createRequestHandler, + updateRequestHandler, +} = require("@/app/api/user-requests/post"); +const { + createRequest, + updateRequest, +} = require("@/app/dal/userRequestService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/userRequestService"); +jest.mock("@/app/utils/errorHandler"); + +describe("createRequestHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when kc_id or message is missing", async () => { + await createRequestHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["kc_id", "message"], + ctx + ); + }); + + it("should create request and set status to 201", async () => { + ctx.params.kc_id = "kc123"; + ctx.request.body = { message: "test message" }; + const mockResponse = { id: 1, kc_id: "kc123", message: "test message" }; + createRequest.mockResolvedValue(mockResponse); + + await createRequestHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.kc_id = "kc123"; + ctx.request.body = { message: "test message" }; + createRequest.mockImplementation(() => { + throw error; + }); + + await createRequestHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("updateRequestHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id or is_processed is missing", async () => { + await updateRequestHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["id", "is_processed"], + ctx + ); + }); + + it("should return 400 if is_processed is not a boolean", async () => { + ctx.params.id = { + id: "req123", + }; + ctx.request.body.is_processed = "not_boolean"; + + jest.mock("@/app/utils/errorHandler", () => { + const originalModule = jest.requireActual("@/app/utils/errorHandler"); + return { + ...originalModule, + handleInternalError: jest.fn(), + }; + }); + + await updateRequestHandler(ctx); + expect(handleMissingParameters.mock.calls).toHaveLength(1); + expect(handleMissingParameters).toHaveBeenCalledWith( + ["is_processed must be a boolean"], + ctx + ); + }); + + it("should update request and set status to 201", async () => { + ctx.params.id = "req123"; + ctx.request.body.is_processed = true; + const mockResponse = { id: "req123", is_processed: "true" }; + updateRequest.mockResolvedValue(mockResponse); + + await updateRequestHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "req123"; + ctx.request.body.is_processed = true; + updateRequest.mockImplementation(() => { + throw error; + }); + + await updateRequestHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/user-search-history/delete.test.js b/__tests__/api/user-search-history/delete.test.js new file mode 100644 index 0000000..1ed4c00 --- /dev/null +++ b/__tests__/api/user-search-history/delete.test.js @@ -0,0 +1,94 @@ +// delete.test.js + +const { + deleteHistoryHandler, + deleteAllHistoryByUserHandler, +} = require("@/app/api/user-search-history/delete"); +const { + deleteHistory, + deleteAllHistoryByUser: deleteAllHistoryByUserService, +} = require("@/app/dal/searchHistoryService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/searchHistoryService"); +jest.mock("@/app/utils/errorHandler"); + +describe("deleteHistoryHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when id is missing", async () => { + await deleteHistoryHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["id"], ctx); + }); + + it("should delete history and set status to 204", async () => { + ctx.params.id = "history123"; + + await deleteHistoryHandler(ctx); + + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.id = "history123"; + deleteHistory.mockImplementation(() => { + throw error; + }); + + await deleteHistoryHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); + +describe("deleteAllHistoryByUserHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when kc_id is missing", async () => { + await deleteAllHistoryByUserHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should delete all history by user and set status to 204", async () => { + ctx.params.kc_id = "kc123"; + deleteAllHistoryByUserService.mockResolvedValue([]); + + await deleteAllHistoryByUserHandler(ctx); + + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.kc_id = "kc123"; + deleteAllHistoryByUserService.mockImplementation(() => { + throw error; + }); + + await deleteAllHistoryByUserHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/user-search-history/get.test.js b/__tests__/api/user-search-history/get.test.js new file mode 100644 index 0000000..2bf46b7 --- /dev/null +++ b/__tests__/api/user-search-history/get.test.js @@ -0,0 +1,58 @@ +// get.test.js + +const { getHistoryHandler } = require("@/app/api/user-search-history/get"); +const { + handleMissingParameters, + handleInternalError, +} = require("@/app/utils/errorHandler"); +const { getSearchHistoryByUser } = require("@/app/dal/searchHistoryService"); + +jest.mock("@/app/utils/errorHandler"); +jest.mock("@/app/dal/searchHistoryService"); + +describe("getHistoryHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + request: { + body: {}, + }, + body: null, + status: null, + params: {}, + }; + }); + + it("should handle missing parameters", async () => { + await getHistoryHandler(ctx); + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should return search history and set status to 200", async () => { + const mockHistory = [{ id: 1, search: "test search" }]; + ctx.params = { + kc_id: "kc123", + }; + getSearchHistoryByUser.mockResolvedValue(mockHistory); + + await getHistoryHandler(ctx); + + expect(ctx.status).toBe(200); + expect(ctx.body).toEqual(mockHistory); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params = { + kc_id: "kc123", + }; + getSearchHistoryByUser.mockImplementation(() => { + throw error; + }); + + await getHistoryHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/user-search-history/post.test.js b/__tests__/api/user-search-history/post.test.js new file mode 100644 index 0000000..25ee21c --- /dev/null +++ b/__tests__/api/user-search-history/post.test.js @@ -0,0 +1,75 @@ +// post.test.js + +const { + addSearchHistoryToUserHandler, +} = require("@/app/api/user-search-history/post"); +const { addSearchHistoryToUser } = require("@/app/dal/searchHistoryService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/searchHistoryService"); +jest.mock("@/app/utils/errorHandler"); + +describe("addSearchHistoryToUserHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when any required parameter is missing", async () => { + ctx.request.body = { query: "test query" }; + + await addSearchHistoryToUserHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["kc_id", "name", "ui_structure", "description"], + ctx + ); + }); + + it("should add search history and set status to 201", async () => { + const mockResponse = { id: 1, query: "test query" }; + + ctx.params = { kc_id: "test-id" }; + ctx.request.body = { + query: "test query", + name: "test name", + ui_structure: "test structure", + description: "test description", + }; + addSearchHistoryToUser.mockResolvedValue(mockResponse); + + await addSearchHistoryToUserHandler(ctx); + + expect(ctx.body).toEqual(mockResponse); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params = { kc_id: "test-id" }; + ctx.request.body = { + query: "test query", + name: "test name", + ui_structure: "test structure", + description: "test description", + }; + addSearchHistoryToUser.mockImplementation(() => { + throw error; + }); + + await addSearchHistoryToUserHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/users/delete.test.js b/__tests__/api/users/delete.test.js new file mode 100644 index 0000000..6d9a423 --- /dev/null +++ b/__tests__/api/users/delete.test.js @@ -0,0 +1,52 @@ +const { deleteUserHandler } = require("@/app/api/users/delete"); +const userService = require("@/app/dal/userService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +jest.mock("@/app/dal/userService"); +jest.mock("@/app/utils/errorHandler"); + +describe("deleteUserHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when kc_id is missing", async () => { + await deleteUserHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should delete user and set status to 204", async () => { + ctx.params = { kc_id: "kc123" }; + userService.deleteUser.mockResolvedValue({}); + + await deleteUserHandler(ctx); + + expect(ctx.body).toEqual({}); + expect(ctx.status).toBe(204); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params = { kc_id: "kc123" }; + userService.deleteUser.mockImplementation(() => { + throw error; + }); + + await deleteUserHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/__tests__/api/users/get.test.js b/__tests__/api/users/get.test.js new file mode 100644 index 0000000..484738e --- /dev/null +++ b/__tests__/api/users/get.test.js @@ -0,0 +1,78 @@ +"use strict"; +const { getUsersHandler, getUserHandler } = require("@/app/api/users/get"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { getUsers, getUser } = require("@/app/dal/userService"); + +jest.mock("@/app/utils/errorHandler"); +jest.mock("@/app/dal/userService"); + +describe("User Handlers", () => { + let ctx; + + beforeEach(() => { + ctx = { + params: {}, + body: null, + status: null, + }; + }); + + describe("getUsersHandler", () => { + it("should return all users and set status to 200", async () => { + const mockUsers = [ + { id: 1, name: "User 1" }, + { id: 2, name: "User 2" }, + ]; + getUsers.mockResolvedValue(mockUsers); + + await getUsersHandler(ctx); + + expect(ctx.body).toEqual(mockUsers); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + getUsers.mockImplementation(() => { + throw error; + }); + + await getUsersHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); + }); + + describe("getUserHandler", () => { + it("should handle missing parameters", async () => { + await getUserHandler(ctx); + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should return user data and set status to 200", async () => { + const mockUser = { id: 1, name: "Test User" }; + ctx.params.kc_id = 1; + getUser.mockResolvedValue(mockUser); + + await getUserHandler(ctx); + + expect(ctx.body).toEqual(mockUser); + expect(ctx.status).toBe(200); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.params.kc_id = 1; + getUser.mockImplementation(() => { + throw error; + }); + + await getUserHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); + }); +}); diff --git a/__tests__/api/users/post.test.js b/__tests__/api/users/post.test.js new file mode 100644 index 0000000..ced1bdf --- /dev/null +++ b/__tests__/api/users/post.test.js @@ -0,0 +1,96 @@ +// post.test.js + +const { getOrCreateUserHandler } = require("@/app/api/users/post"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { createUser, getUser } = require("@/app/dal/userService"); +const prisma = require("@/prisma/client"); + +jest.mock("@/prisma/client", () => ({ + user: { + findFirst: jest.fn(), + create: jest.fn(), + }, +})); + +jest.mock("@/app/dal/userService"); +jest.mock("@/app/utils/errorHandler"); + +describe("getOrCreateUserHandler", () => { + let ctx; + + beforeEach(() => { + ctx = { + request: { + body: {}, + }, + body: null, + status: null, + }; + }); + + it("should handle missing parameters when kc_id is missing", async () => { + ctx.request.body = { email: "test@example.com" }; + + await getOrCreateUserHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["kc_id"], ctx); + }); + + it("should handle missing parameters when email is missing", async () => { + ctx.request.body = { kc_id: "kc123" }; + + await getOrCreateUserHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith(["email"], ctx); + }); + + it("should handle missing parameters when both kc_id and email are missing", async () => { + ctx.request.body = {}; + + await getOrCreateUserHandler(ctx); + + expect(handleMissingParameters).toHaveBeenCalledWith( + ["kc_id", "email"], + ctx + ); + }); + + it("should return user data and set status to 200 if user exists", async () => { + const mockUser = { id: 1, kc_id: "kc123", email: "test@example.com" }; + ctx.request.body = { kc_id: "kc123", email: "test@example.com" }; + getUser.mockResolvedValue(mockUser); + + await getOrCreateUserHandler(ctx); + + expect(ctx.body).toEqual(mockUser); + expect(ctx.status).toBe(200); + }); + + it("should create a new user and set status to 201 if user does not exist", async () => { + const mockUser = { id: 1, kc_id: "kc123", email: "test@example.com" }; + ctx.request.body = { kc_id: "kc123", email: "test@example.com" }; + getUser.mockResolvedValue(null); + createUser.mockResolvedValue(mockUser); + + prisma.user.findFirst.mockResolvedValue(null); + await getOrCreateUserHandler(ctx); + + expect(ctx.body).toEqual(mockUser); + expect(ctx.status).toBe(201); + }); + + it("should handle internal error", async () => { + const error = new Error("Internal Error"); + ctx.request.body = { kc_id: "kc123", email: "test@example.com" }; + createUser.mockImplementation(() => { + throw error; + }); + + await getOrCreateUserHandler(ctx); + + expect(handleInternalError).toHaveBeenCalledWith(error, ctx); + }); +}); diff --git a/app/api/group/delete.js b/app/api/group/delete.js deleted file mode 100644 index 5a8c7cd..0000000 --- a/app/api/group/delete.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict' - -const groupService = require('../../dal/groupService'); -const logger = require('../../utils/logger'); - -const Group = { - async deleteGroup(ctx) { - try { - ctx.body = await groupService.deleteGroup(ctx.request.body); - ctx.status = 201; - logger.info('Group deleted.'); - } catch (error) { - throw error; - } - }, - - async deleteGroupPolicy(ctx) { - try { - ctx.body = await groupService.deleteGroupPolicy(ctx.request.body); - ctx.status = 201; - logger.info('Group policy deleted.'); - } catch (error) { - throw error; - } - }, - - async deleteGroupUser(ctx) { - try { - ctx.body = await groupService.deleteGroupUser(ctx.request.body); - ctx.status = 201; - logger.info('Group user deleted.'); - } catch (error) { - throw error; - } - }, -}; - -module.exports = { - deleteGroup : Group.deleteGroup, - deleteGroupPolicy: Group.deleteGroupPolicy, - deleteGroupUser: Group.deleteGroupUser -}; diff --git a/app/api/group/get.js b/app/api/group/get.js deleted file mode 100644 index 8a99ad0..0000000 --- a/app/api/group/get.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict' - -const groupService = require('../../dal/groupService'); -const logger = require('../../utils/logger'); - -const Group = { - async groups(ctx) { - try { - ctx.body = await groupService.groups(); - ctx.status = 201; - logger.info('Groups fetched.'); - } catch (error) { - throw error; - } - }, - - async groupPolicies(ctx) { - try { - ctx.body = await groupService.groupPolicies(); - ctx.status = 201; - logger.info('Group policies fetched.'); - } catch (error) { - throw error; - } - }, - - async groupUsers(ctx) { - try { - ctx.body = await groupService.groupUsers(ctx.request.body); - ctx.status = 201; - logger.info('Group users fetched.'); - } catch (error) { - throw error; - } - } -}; - -module.exports = { - groups: Group.groups, - groupPolicies: Group.groupPolicies, - groupUsers: Group.groupUsers, -}; diff --git a/app/api/group/index.js b/app/api/group/index.js deleted file mode 100644 index 32fb066..0000000 --- a/app/api/group/index.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict' - -const { groups, groupUsers, groupPolicies } = require('./get'); -const { createGroup, createGroupPolicy, createGroupUser } = require('./post'); -const { deleteGroup, deleteGroupPolicy, deleteGroupUser } = require('./delete'); -const { updateGroup, updateGroupPolicy, updateGroupUser } = require('./update'); - -const GroupHandler = { - groups, - groupUsers, - groupPolicies, - createGroup, - createGroupPolicy, - createGroupUser, - deleteGroup, - deleteGroupPolicy, - deleteGroupUser, - updateGroup, - updateGroupPolicy, - updateGroupUser -}; - -module.exports = GroupHandler; diff --git a/app/api/group/post.js b/app/api/group/post.js deleted file mode 100644 index dd0a5b5..0000000 --- a/app/api/group/post.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict' - -const groupService = require('../../dal/groupService'); -const logger = require('../../utils/logger'); - -const Group = { - async createGroup(ctx) { - try { - ctx.body = await groupService.createGroup(ctx.request.body); - ctx.status = 201; - logger.info('Group created.'); - } catch (error) { - throw error; - } - }, - - async createGroupPolicy(ctx) { - try { - ctx.body = await groupService.createGroupPolicy(ctx.request.body); - ctx.status = 201; - logger.info('Assigned policy to group.'); - } catch (error) { - throw error; - } - }, - - async createGroupUser(ctx) { - try { - ctx.body = await groupService.createGroupUser(ctx.request.body); - ctx.status = 201; - logger.info('Assigned user to group.'); - } catch (error) { - throw error; - } - } -}; - -module.exports = { - createGroup: Group.createGroup, - createGroupPolicy: Group.createGroupPolicy, - createGroupUser: Group.createGroupUser, -}; diff --git a/app/api/group/update.js b/app/api/group/update.js deleted file mode 100644 index 6241040..0000000 --- a/app/api/group/update.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict' - -const groupService = require('../../dal/groupService'); -const logger = require('../../utils/logger'); - -const Group = { - async updateGroup(ctx) { - try { - ctx.body = await groupService.updateGroup(ctx.request.body); - ctx.status = 201; - logger.info('Group updated.'); - } catch (error) { - throw error; - } - }, - - async updateGroupPolicy(ctx) { - try { - ctx.body = await groupService.updateGroupPolicy(ctx.request.body); - ctx.status = 201; - logger.info('Group policy updated.'); - } catch (error) { - throw error; - } - }, - - async updateGroupUser(ctx) { - try { - ctx.body = await groupService.updateGroupUser(ctx.request.body); - ctx.status = 201; - logger.info('Group user updated.'); - } catch (error) { - throw error; - } - }, -}; - -module.exports = { - updateGroup: Group.updateGroup, - updateGroupPolicy: Group.updateGroupPolicy, - updateGroupUser: Group.updateGroupUser, -}; diff --git a/app/api/groups/delete.js b/app/api/groups/delete.js new file mode 100644 index 0000000..7d0afb2 --- /dev/null +++ b/app/api/groups/delete.js @@ -0,0 +1,25 @@ +"use strict"; + +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { deleteGroup } = require("@/app/dal/groupService"); + +async function deleteGroupHandler(ctx) { + const { id } = ctx.params; + if (!id) { + handleMissingParameters(["id"], ctx); + } else { + try { + await deleteGroup(id); + ctx.status = 204; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +module.exports = { + deleteGroupHandler, +}; diff --git a/app/api/groups/get.js b/app/api/groups/get.js new file mode 100644 index 0000000..e4695bc --- /dev/null +++ b/app/api/groups/get.js @@ -0,0 +1,34 @@ +"use strict"; + +const { + handleMissingParameters, + handleInternalError, +} = require("@/app/utils/errorHandler"); +const { getAllGroups, getGroup } = require("@/app/dal/groupService"); + +async function getGroupHandler(ctx) { + const { id } = ctx.params; + if (!id) { + handleMissingParameters(["id"], ctx); + } + try { + ctx.body = await getGroup(id); + ctx.status = 200; + } catch (error) { + handleInternalError(error, ctx); + } +} + +async function getAllGroupsHandler(ctx) { + try { + ctx.body = await getAllGroups(); + ctx.status = 200; + } catch (error) { + handleInternalError(error, ctx); + } +} + +module.exports = { + getAllGroupsHandler, + getGroupHandler, +}; diff --git a/app/api/groups/post.js b/app/api/groups/post.js new file mode 100644 index 0000000..461735f --- /dev/null +++ b/app/api/groups/post.js @@ -0,0 +1,29 @@ +"use strict"; + +const { + handleMissingParameters, + handleInternalError, +} = require("@/app/utils/errorHandler"); +const { createGroup } = require("@/app/dal/groupService"); + +async function createGroupHandler(ctx) { + const { name, description, kc_id } = ctx.request.body; + if (!name || !description || !kc_id) { + const missingParameters = []; + if (!name) missingParameters.push("name"); + if (!description) missingParameters.push("description"); + if (!kc_id) missingParameters.push("kc_id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await createGroup(name, description, kc_id); + ctx.status = 201; + } catch (error) { + handleInternalError(error, ctx); + } + } +} + +module.exports = { + createGroupHandler, +}; diff --git a/app/api/groups/update.js b/app/api/groups/update.js new file mode 100644 index 0000000..6f9d821 --- /dev/null +++ b/app/api/groups/update.js @@ -0,0 +1,110 @@ +"use strict"; + +const { + handleMissingParameters, + handleInternalError, +} = require("@/app/utils/errorHandler"); +const { + addUserToGroup, + addPolicyToGroup, + removePolicyFromGroup, + removeUserFromGroup, + updateGroup, +} = require("@/app/dal/groupService"); + +async function updateGroupHandler(ctx) { + const { id } = ctx.params; + const { name, description } = ctx.request.body; + if (!name || !description || !id) { + const missingParameters = []; + if (!name) missingParameters.push("name"); + if (!description) missingParameters.push("description"); + if (!id) missingParameters.push("id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await updateGroup(id, name, description); + ctx.status = 200; + } catch (error) { + handleInternalError(error, ctx); + } + } +} + +async function addUserToGroupHandler(ctx) { + const { id } = ctx.params; + const { kc_id } = ctx.request.body; + if (!kc_id || !id) { + const missingParameters = []; + if (!kc_id) missingParameters.push("kc_id"); + if (!id) missingParameters.push("id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await addUserToGroup(kc_id, id); + ctx.status = 201; + } catch (error) { + handleInternalError(error, ctx); + } + } +} + +async function removeUserFromGroupHandler(ctx) { + const { id, kc_id } = ctx.params; + if (!kc_id || !id) { + const missingParameters = []; + if (!kc_id) missingParameters.push("kc_id"); + if (!id) missingParameters.push("id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + await removeUserFromGroup(kc_id, id); + ctx.status = 204; + } catch (error) { + handleInternalError(error, ctx); + } + } +} + +async function addPolicyToGroupHandler(ctx) { + const { id } = ctx.params; + const { policy_id } = ctx.request.body; + if (!policy_id || !id) { + const missingParameters = []; + if (!policy_id) missingParameters.push("policy_id"); + if (!id) missingParameters.push("id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await addPolicyToGroup(policy_id, id); + ctx.status = 201; + } catch (error) { + handleInternalError(error, ctx); + } + } +} + +async function removePolicyFromGroupHandler(ctx) { + const { policy_id, id } = ctx.params; + if (!policy_id || !id) { + const missingParameters = []; + if (!policy_id) missingParameters.push("policy_id"); + if (!id) missingParameters.push("id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await removePolicyFromGroup(policy_id, id); + ctx.status = 204; + } catch (error) { + handleInternalError(error, ctx); + } + } +} + +module.exports = { + updateGroupHandler, + addUserToGroupHandler, + addPolicyToGroupHandler, + removeUserFromGroupHandler, + removePolicyFromGroupHandler, +}; diff --git a/app/api/index.js b/app/api/index.js index a69d35f..52944e5 100644 --- a/app/api/index.js +++ b/app/api/index.js @@ -1,106 +1,189 @@ -'use strict' +"use strict"; const Router = require("koa-router"); const bodyParser = require("koa-bodyparser"); -/* const { keycloak } = require('../../init-keycloak') */ - -// Handlers -const userHandler = require('./users'); -const roleHandler = require('./roles'); -const allocationRoleHandler = require('./role-user'); -const searchHistoryHandler = require('./user-search-history'); -const userRequestHandler = require('./user-requests'); -const groupHandler = require('./group'); -const policyHandler = require('./policy'); -const UserFieldsDisplaySettingsHandler = require("./user-fields-display-settings"); - const routers = new Router(); -routers.use(bodyParser()); - -routers.get('/healthcheck', (ctx) => { - ctx.res.statusCode = 200; - ctx.res.end('OK'); -}); - -// Users -routers.post('/user', userHandler.create); -routers.post('/user/findOne',/* keycloak.protect(),*/ userHandler.user); -routers.get('/user/find',/* keycloak.protect(),*/ userHandler.users); -routers.delete('/user/delete',/* keycloak.protect(),*/ userHandler.deleteUser); -routers.put('/user/update',/* keycloak.protect(),*/ userHandler.update); -routers.post('/user/kcid', /* keycloak.protect(),*/ userHandler.kcId); -routers.post('/user/detail', /* keycloak.protect(),*/ userHandler.detail); -routers.post('/user/assigned-by-role', userHandler.getAssignedUserByRole); -routers.post('/user/send-mail', userHandler.sendMail); -routers.post('/user/reset-password', userHandler.resetPassword); - -// System admin user -routers.post('/user/create-system-user', userHandler.createSystemUser); -routers.get('/user/with-groups-and-roles', userHandler.usersWithGroupAndRole); -routers.post('/user/one-with-groups-and-roles', userHandler.userWithGroupAndRole); - -// Search history -routers.post('/user/add-history', searchHistoryHandler.create); -routers.post('/user/fetch-history', searchHistoryHandler.fetch); -routers.delete('/user/delete-history', searchHistoryHandler.deleteUserSearchHistory); - -// User fields display settings -routers.post('/user/fields-display-settings/', /* keycloak.protect(),*/ UserFieldsDisplaySettingsHandler.createUserFieldsDisplaySettings); -routers.get('/user/fields-display-settings/:userId',/* keycloak.protect(),*/ UserFieldsDisplaySettingsHandler.findUserFieldsDisplaySettings); -routers.get('/user/fields-display-settings/',/* keycloak.protect(),*/ UserFieldsDisplaySettingsHandler.findUsersFieldsDisplaySettings); -routers.put('/user/fields-display-settings/',/* keycloak.protect(),*/ UserFieldsDisplaySettingsHandler.updateUserFieldsDisplaySettings); -routers.delete('/user/fields-display-settings/:userId',/* keycloak.protect(),*/ UserFieldsDisplaySettingsHandler.deleteUserFieldsDisplaySettings); - -// User requests -routers.post('/user/create-request', userRequestHandler.createRequest); -routers.delete('/user/delete-request', userRequestHandler.deleteRequest); -routers.post('/user/process-request', userRequestHandler.processRequest); -routers.get('/user/list-requests', userRequestHandler.fetchRequests); -routers.post('/user/list-requests-by-user', userRequestHandler.fetchRequestsByUser); -routers.get('/user/list-pending-requests', userRequestHandler.fetchPendingRequests); - -// Groups -routers.post('/user/groups', groupHandler.groups); -routers.post('/user/group-policies', groupHandler.groupPolicies); -routers.post('/user/group-users', groupHandler.groupUsers); -routers.post('/user/add-group', groupHandler.createGroup); -routers.post('/user/add-group-policy', groupHandler.createGroupPolicy); -routers.post('/user/add-group-user', groupHandler.createGroupUser); -routers.put('/user/update-group', groupHandler.updateGroup); -routers.put('/user/update-group-policy', groupHandler.updateGroupPolicy); -routers.put('/user/update-group-user', groupHandler.updateGroupUser); -routers.delete('/user/delete-group', groupHandler.deleteGroup); -routers.delete('/user/delete-group-policy', groupHandler.deleteGroupPolicy); -routers.delete('/user/delete-group-user', groupHandler.deleteGroupUser); - -// Policies -routers.post('/policy/add', policyHandler.createPolicy); -routers.delete('/policy/delete', policyHandler.deletePolicy); -routers.put('/policy/update', policyHandler.updatePolicy); -routers.get('/policy/list', policyHandler.policies); -routers.post('/policy/list-by-user', policyHandler.policiesByUser); -routers.post('/policy/assigned-fields', policyHandler.assignedPolicies); -routers.get('/policy/policies-with-sources', policyHandler.getPoliciesWithSources); -routers.post('/policy/policies-with-sources-by-user', policyHandler.getPoliciesWithSourcesByUser); -routers.post('/policy/policies-with-groups', policyHandler.getGroupDetailsByPolicy); -routers.post('/policyField/add', policyHandler.createPolicyField); -routers.delete('/policyField/delete', policyHandler.deletePolicyField); -routers.put('/policyField/update', policyHandler.updatePolicyField); -routers.get('/policyField/list', policyHandler.policyFields); -routers.post('/policySource/add', policyHandler.createPolicySource); - -// Roles -routers.post('/role',/* keycloak.protect(),*/ roleHandler.createRole); -routers.get('/role/find', /* keycloak.protect(),*/ roleHandler.roles); -routers.get('/role/findOne', /* keycloak.protect(),*/ roleHandler.role); -routers.delete('/role/delete', /* keycloak.protect(),*/ roleHandler.deleteRole); -routers.put('/role/update', /* keycloak.protect(),*/ roleHandler.update); - -// Role allocation to users -routers.post('/allocate-role-to-user', /* keycloak.protect(),*/allocationRoleHandler.create); -routers.post('/allocatedRoles', /* keycloak.protect(),*/allocationRoleHandler.allocatedRoles); -routers.put('/allocatedRoles/update', /* keycloak.protect(),*/allocationRoleHandler.update); -routers.delete('/allocatedRoles/delete',/* keycloak.protect(),*/allocationRoleHandler.deleteAllocatedRole); +const { getUsersHandler, getUserHandler } = require("@/app/api/users/get"); +const { getOrCreateUserHandler } = require("@/app/api/users/post"); +const { deleteUserHandler } = require("@/app/api/users/delete"); + +const { getHistoryHandler } = require("@/app/api/user-search-history/get"); +const { + addSearchHistoryToUserHandler, +} = require("@/app/api/user-search-history/post"); +const { + deleteHistoryHandler, + deleteAllHistoryByUserHandler, +} = require("@/app/api/user-search-history/delete"); + +const { + getAllRequestsHandler, + getAllPendingRequestsHandler, + getAllUserRequestsHandler, +} = require("@/app/api/user-requests/get"); +const { + createRequestHandler, + updateRequestHandler, +} = require("@/app/api/user-requests/post"); +const { deleteRequestHandler } = require("@/app/api/user-requests/delete"); + +const { + getAllGroupsHandler, + getGroupHandler, +} = require("@/app/api/groups/get"); +const { + addUserToGroupHandler, + removeUserFromGroupHandler, + addPolicyToGroupHandler, + updateGroupHandler, + removePolicyFromGroupHandler, +} = require("@/app/api/groups/update"); +const { createGroupHandler } = require("@/app/api/groups/post"); +const { deleteGroupHandler } = require("@/app/api/groups/delete"); + +const { + getAllPoliciesHandler, + getPolicyHandler, +} = require("@/app/api/policies/get"); +const { createPolicyHandler } = require("@/app/api/policies/post"); +const { + updatePolicyHandler, + addFieldToPolicyHandler, + removeFieldFromPolicyHandler, + addSourceToPolicyHandler, + removeSourceFromPolicyHandler, +} = require("@/app/api/policies/update"); +const { deletePolicyHandler } = require("@/app/api/policies/delete"); + +const { + getStdFieldsHandler, + getPublicStdFieldsHandler, + getStdFieldHandler, +} = require("@/app/api/std_fields/get"); + +const { + getUserDisplayFieldsHandler, +} = require("@/app/api/user-display-fields/get"); + +const { createOrUpdateStdFieldHandler } = require("@/app/api/std_fields/post"); + +const { deleteAllStdFieldsHandler } = require("@/app/api/std_fields/delete"); + +const { + getSourcesHandler, + getSourcesByProviderHandler, + getIndexedSourcesByProviderHandler, + getIndexedSourcesHandler, +} = require("@/app/api/sources/get"); +const { createSourceHandler } = require("@/app/api/sources/post"); +const { deleteSourceHandler } = require("@/app/api/sources/delete"); + +const { + createRoleHandler, + addUserToRoleHandler, + removeUserFromRoleHandler, +} = require("@/app/api/roles/post"); +const { getRolesHandler, getRoleHandler } = require("@/app/api/roles/get"); +const { updateRoleHandler } = require("@/app/api/roles/put"); +const { deleteRoleHandler } = require("@/app/api/roles/delete"); + +const { getQueryResultsHandler } = require("@/app/api/search/post"); +const { setUserDisplayFieldsHandler } = require("./user-display-fields/post"); + +routers.use( + bodyParser({ formLimit: "700mb", jsonLimit: "700mb", textLimit: "700mb" }) +); + +// search +routers.post("/search", getQueryResultsHandler); + +// users +routers.get("/users", getUsersHandler); +routers.get("/users/:kc_id", getUserHandler); +routers.post("/users", getOrCreateUserHandler); +routers.delete("/users/:kc_id", deleteUserHandler); + +// search-history +routers.get("/users/:kc_id/history", getHistoryHandler); +routers.post("/users/:kc_id/history", addSearchHistoryToUserHandler); +routers.delete("/users/:kc_id/history", deleteAllHistoryByUserHandler); +routers.delete("/users/:kc_id/history/:id", deleteHistoryHandler); + +// user requests +routers.get("/user-requests", getAllRequestsHandler); +routers.get("/user-requests/pending", getAllPendingRequestsHandler); +routers.get("/users/:kc_id/requests", getAllRequestsHandler); +routers.get("/users/:kc_id/requests/pending", getAllUserRequestsHandler); +routers.post("/users/:kc_id/requests", createRequestHandler); +routers.post("/user-requests/:id", updateRequestHandler); +routers.delete("/user-requests/:id", deleteRequestHandler); + +// user display fields +routers.get("/users/:kc_id/fields", getUserDisplayFieldsHandler); +routers.post("/users/:kc_id/fields", setUserDisplayFieldsHandler); + +// groups +routers.get("/groups", getAllGroupsHandler); +routers.get("/groups/:id", getGroupHandler); +routers.post("/groups", createGroupHandler); +routers.put("/groups/:id", updateGroupHandler); +routers.delete("/groups/:id", deleteGroupHandler); + +// groups users +routers.post("/groups/:id/users", addUserToGroupHandler); +routers.delete("/groups/:id/users/:kc_id", removeUserFromGroupHandler); + +// groups policies +routers.post("/groups/:id/policies", addPolicyToGroupHandler); +routers.delete("/groups/:id/policies/:policy_id", removePolicyFromGroupHandler); + +// policy +routers.get("/policies", getAllPoliciesHandler); +routers.get("/policies/:id", getPolicyHandler); +routers.post("/policies", createPolicyHandler); +routers.put("/policy/update", updatePolicyHandler); +routers.delete("/policies/:id", deletePolicyHandler); + +// policy std_fields +routers.post("/policies/:id/std_fields", addFieldToPolicyHandler); +routers.delete( + "/policies/:id/std_fields/:field_id", + removeFieldFromPolicyHandler +); + +// policy sources +routers.post("/policies/:id/sources", addSourceToPolicyHandler); +routers.delete( + "/policies/:id/sources/:source_id", + removeSourceFromPolicyHandler +); + +// std_fields +routers.get("/std_fields", getStdFieldsHandler); +routers.get("/public_std_fields", getPublicStdFieldsHandler); +routers.get("/std_fields/:id", getStdFieldHandler); +routers.post("/std_fields", createOrUpdateStdFieldHandler); +routers.put("/std_fields/:id", createOrUpdateStdFieldHandler); +routers.delete("/std_fields", deleteAllStdFieldsHandler); + +// sources +routers.get("/sources", getSourcesHandler); +routers.get("/sources/:kc_id", getSourcesByProviderHandler); +routers.post("/sources", createSourceHandler); +routers.get("/indexed-sources", getIndexedSourcesHandler); +routers.get("/indexed-sources/:kc_id", getIndexedSourcesByProviderHandler); +routers.delete("/sources/:id", deleteSourceHandler); + +// roles +routers.get("/roles", getRolesHandler); +routers.get("/roles/:id", getRoleHandler); +routers.post("/roles", createRoleHandler); +routers.put("/roles/:id", updateRoleHandler); +routers.delete("/roles/:id", deleteRoleHandler); + +// role users +routers.post("/roles/:id/users", addUserToRoleHandler); +routers.delete("/roles/:id/users/:kc_id", removeUserFromRoleHandler); module.exports = routers; diff --git a/app/api/policies/delete.js b/app/api/policies/delete.js new file mode 100644 index 0000000..ab36df3 --- /dev/null +++ b/app/api/policies/delete.js @@ -0,0 +1,25 @@ +"use strict"; + +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { deletePolicy } = require("@/app/dal/policyService"); + +async function deletePolicyHandler(ctx) { + const { id } = ctx.params; + if (!id) { + handleMissingParameters(["id"], ctx); + } else { + try { + ctx.body = await deletePolicy(id); + ctx.status = 204; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +module.exports = { + deletePolicyHandler, +}; diff --git a/app/api/policies/get.js b/app/api/policies/get.js new file mode 100644 index 0000000..560e0ad --- /dev/null +++ b/app/api/policies/get.js @@ -0,0 +1,35 @@ +"use strict"; + +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { getAllPolicies, getPolicy } = require("@/app/dal/policyService"); + +async function getAllPoliciesHandler(ctx) { + try { + ctx.body = await getAllPolicies(); + ctx.status = 200; + } catch (error) { + handleInternalError(error, ctx); + } +} + +async function getPolicyHandler(ctx) { + const { id } = ctx.params; + if (!id) { + handleMissingParameters(["id"], ctx); + } else { + try { + ctx.body = await getPolicy(id); + ctx.status = 200; + } catch (error) { + handleInternalError(error, ctx); + } + } +} + +module.exports = { + getAllPoliciesHandler, + getPolicyHandler, +}; diff --git a/app/api/policies/post.js b/app/api/policies/post.js new file mode 100644 index 0000000..13bfd43 --- /dev/null +++ b/app/api/policies/post.js @@ -0,0 +1,28 @@ +"use strict"; + +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { createPolicy } = require("@/app/dal/policyService"); + +async function createPolicyHandler(ctx) { + const { name, kc_id } = ctx.request.body; + if (!name || !kc_id) { + const missingParameters = []; + if (!name) missingParameters.push("name"); + if (!kc_id) missingParameters.push("kc_id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await createPolicy(name, kc_id); + ctx.status = 201; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +module.exports = { + createPolicyHandler, +}; diff --git a/app/api/policies/update.js b/app/api/policies/update.js new file mode 100644 index 0000000..d81d803 --- /dev/null +++ b/app/api/policies/update.js @@ -0,0 +1,110 @@ +"use strict"; + +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { + updatePolicy, + addFieldToPolicy, + removeFieldFromPolicy, + addSourceToPolicy, + removeSourceFromPolicy, +} = require("@/app/dal/policyService"); + +async function updatePolicyHandler(ctx) { + const { id } = ctx.params; + const { name, isDefault } = ctx.request.body; + if (!id || !name || !isDefault) { + const missingParameters = []; + if (!id) missingParameters.push("id"); + if (!name) missingParameters.push("name"); + if (!isDefault) missingParameters.push("isDefault"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await updatePolicy(id, name, isDefault); + ctx.status = 204; + } catch (error) { + handleInternalError(error, ctx); + } + } +} + +async function addFieldToPolicyHandler(ctx) { + const { id } = ctx.params; + const { field_id } = ctx.request.body; + if (!id || !field_id) { + const missingParameters = []; + if (!id) missingParameters.push("id"); + if (!field_id) missingParameters.push("field_id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await addFieldToPolicy(field_id, id); + ctx.status = 201; + } catch (error) { + handleInternalError(error, ctx); + } + } +} + +async function removeFieldFromPolicyHandler(ctx) { + const { id, field_id } = ctx.params; + if (!id || !field_id) { + const missingParameters = []; + if (!id) missingParameters.push("id"); + if (!field_id) missingParameters.push("field_id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await removeFieldFromPolicy(field_id, id); + ctx.status = 204; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +async function addSourceToPolicyHandler(ctx) { + const { id } = ctx.params; + const { source_id } = ctx.request.body; + if (!id || !source_id) { + const missingParameters = []; + if (!id) missingParameters.push("id"); + if (!source_id) missingParameters.push("source_id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await addSourceToPolicy(source_id, id); + ctx.status = 201; + } catch (error) { + handleInternalError(error, ctx); + } + } +} + +async function removeSourceFromPolicyHandler(ctx) { + const { id, source_id } = ctx.params; + if (!id || !source_id) { + const missingParameters = []; + if (!id) missingParameters.push("id"); + if (!source_id) missingParameters.push("source_id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + await removeSourceFromPolicy(source_id, id); + ctx.status = 204; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +module.exports = { + updatePolicyHandler, + addFieldToPolicyHandler, + removeFieldFromPolicyHandler, + addSourceToPolicyHandler, + removeSourceFromPolicyHandler, +}; diff --git a/app/api/policy/delete.js b/app/api/policy/delete.js deleted file mode 100644 index 4a75dce..0000000 --- a/app/api/policy/delete.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' - -const policyService = require('../../dal/policyService'); -const logger = require('../../utils/logger'); - -const Policy = { - async deletePolicy(ctx) { - try { - ctx.body = await policyService.deletePolicy(ctx.request.body); - ctx.status = 201; - logger.info('Policy deleted.'); - } catch (error) { - throw error; - } - }, - - async deletePolicyField(ctx) { - try { - ctx.body = await policyService.deletePolicyField(ctx.request.body); - ctx.status = 201; - logger.info('PolicyField deleted.'); - } catch (error) { - throw error; - } - } -}; - -module.exports = { - deletePolicy: Policy.deletePolicy, - deletePolicyField: Policy.deletePolicyField -}; diff --git a/app/api/policy/get.js b/app/api/policy/get.js deleted file mode 100644 index 61abfad..0000000 --- a/app/api/policy/get.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict' - -const policyService = require('../../dal/policyService'); -const logger = require('../../utils/logger'); - -const Policy = { - async policies(ctx) { - try { - ctx.body = await policyService.policies(); - ctx.status = 201; - logger.info('Policies fetched.'); - } catch (error) { - throw error; - } - }, - - async policyFields(ctx) { - try { - ctx.body = await policyService.policyFields(); - ctx.status = 201; - logger.info('Policy fields fetched.'); - } catch (error) { - throw error; - } - }, - - async assignedPolicies(ctx) { - try { - ctx.body = await policyService.getAssignedPolicies(ctx.request.body); - ctx.status = 201; - logger.info('Assigned policies fetched.'); - } catch (error) { - throw error; - } - }, - - async getPoliciesWithSources(ctx) { - try { - ctx.body = await policyService.getPoliciesWithSources(); - ctx.status = 201; - logger.info('Policies with sources fetched.'); - } catch (error) { - throw error; - } - }, - - async getGroupDetailsByPolicy(ctx) { - try { - ctx.body = await policyService.getGroupDetailsByPolicy(ctx.request.body); - ctx.status = 201; - logger.info('Group details by policy fetched.'); - } catch (error) { - throw error; - } - } -}; - -module.exports = { - policies: Policy.policies, - policyFields: Policy.policyFields, - assignedPolicies: Policy.assignedPolicies, - getPoliciesWithSources: Policy.getPoliciesWithSources, - getGroupDetailsByPolicy: Policy.getGroupDetailsByPolicy -}; diff --git a/app/api/policy/index.js b/app/api/policy/index.js deleted file mode 100644 index 7eb03f9..0000000 --- a/app/api/policy/index.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict' - -const { createPolicy, createPolicyField, createPolicySource, policiesByUser, getPoliciesWithSourcesByUser } = require('./post') -const { updatePolicy, updatePolicyField } = require('./update') -const { deletePolicy, deletePolicyField } = require('./delete') -const { policies, policyFields, assignedPolicies, getPoliciesWithSources, getGroupDetailsByPolicy } = require('./get') - -const PolicyHandler = { - createPolicy, - createPolicyField, - updatePolicy, - updatePolicyField, - deletePolicy, - deletePolicyField, - policies, - policiesByUser, - policyFields, - assignedPolicies, - getPoliciesWithSources, - getPoliciesWithSourcesByUser, - getGroupDetailsByPolicy, - createPolicySource -} - -module.exports = PolicyHandler \ No newline at end of file diff --git a/app/api/policy/post.js b/app/api/policy/post.js deleted file mode 100644 index a04c6bb..0000000 --- a/app/api/policy/post.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict' - -const policyService = require('../../dal/policyService'); -const logger = require('../../utils/logger'); - -const Policy = { - async createPolicy(ctx) { - try { - ctx.body = await policyService.createPolicy(ctx.request.body); - ctx.status = 201; - logger.info('Policy created.'); - } catch (error) { - throw error; - } - }, - - async createPolicyField(ctx) { - try { - ctx.body = await policyService.createPolicyField(ctx.request.body); - ctx.status = 201; - logger.info('PolicyField created.'); - } catch (error) { - throw error; - } - }, - - async createPolicySource(ctx) { - try { - ctx.body = await policyService.createPolicySource(ctx.request.body); - ctx.status = 201; - logger.info('PolicySource created.'); - } catch (error) { - throw error; - } - }, - - async policiesByUser(ctx) { - try { - ctx.body = await policyService.policiesByUser(ctx.request.body); - ctx.status = 201; - logger.info('Policies by user listed.'); - } catch (error) { - throw error; - } - }, - - async getPoliciesWithSourcesByUser(ctx) { - try { - ctx.body = await policyService.getPoliciesWithSourcesByUser(ctx.request.body); - ctx.status = 201; - logger.info('Policies with sources by user listed.'); - } catch (error) { - throw error; - } - } -}; - -module.exports = { - policiesByUser: Policy.policiesByUser, - getPoliciesWithSourcesByUser: Policy.getPoliciesWithSourcesByUser, - createPolicy: Policy.createPolicy, - createPolicyField: Policy.createPolicyField, - createPolicySource: Policy.createPolicySource -}; diff --git a/app/api/policy/update.js b/app/api/policy/update.js deleted file mode 100644 index 9afd22e..0000000 --- a/app/api/policy/update.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' - -const policyService = require('../../dal/policyService'); -const logger = require('../../utils/logger'); - -const Policy = { - async updatePolicy(ctx) { - try { - ctx.body = await policyService.updatePolicy(ctx.request.body); - ctx.status = 201; - logger.info('Policy updated.'); - } catch (error) { - throw error; - } - }, - - async updatePolicyField(ctx) { - try { - ctx.body = await policyService.updatePolicyField(ctx.request.body); - ctx.status = 201; - logger.info('Policy field updated.'); - } catch (error) { - throw error; - } - } -}; - -module.exports = { - updatePolicy: Policy.updatePolicy, - updatePolicyField: Policy.updatePolicyField, -}; diff --git a/app/api/realm/delete.js b/app/api/realm/delete.js deleted file mode 100644 index b6b49ba..0000000 --- a/app/api/realm/delete.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict' - -const realmService = require('../../dal/realmService') -const logger = require('../../utils/logger') -const util = require('util') - -const DeleteRealm = { - async deleteRealm(ctx) { - try { - ctx.body = await realmService.delete(ctx.request.body) - ctx.status = 201 - logger.info('realm is deleted successfully!') - } catch (error) { - ctx.body = err || 'Error occurred!' - ctx.status = 500; - logger.error(`Caught error: ${JSON.stringify(util.inspect(err, { compact: false, depth: 1, breakLength: 80 }))}`) - } - } -} - -module.exports = { - deleteRealm: DeleteRealm.deleteRealm, - DeleteRealm -} diff --git a/app/api/realm/get.js b/app/api/realm/get.js deleted file mode 100644 index cdef5b9..0000000 --- a/app/api/realm/get.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' - -const realmService = require('../../dal/realmService') -const logger = require('../../utils/logger') -const util = require('util') - -const GetRealm = { - async find(ctx) { - try { - ctx.body = await realmService.realms() - ctx.status = 201 - logger.info('realms are listed successfully!') - } catch (error) { - ctx.body = err || 'Error occurred!' - ctx.status = 500; - logger.error(`Caught error: ${JSON.stringify(util.inspect(error, { compact: false, depth: 1, breakLength: 80 }))}`) - } - }, - - async findOne(ctx) { - try { - ctx.body = await realmService.realm(ctx.request.body) - ctx.status = 201 - logger.info('realm is found successfully!') - } catch (error) { - ctx.body = err || 'Error occurred!' - ctx.status = 500; - logger.error(`Caught error: ${JSON.stringify(util.inspect(error, { compact: false, depth: 1, breakLength: 80 }))}`) - } - } - -} - -module.exports = { - realms: GetRealm.find, - realm: GetRealm.findOne, - GetRealm -} diff --git a/app/api/realm/index.js b/app/api/realm/index.js deleted file mode 100644 index 923d096..0000000 --- a/app/api/realm/index.js +++ /dev/null @@ -1 +0,0 @@ -'use strict' \ No newline at end of file diff --git a/app/api/realm/post.js b/app/api/realm/post.js deleted file mode 100644 index d60ac09..0000000 --- a/app/api/realm/post.js +++ /dev/null @@ -1,24 +0,0 @@ -"use strict" - -const realmService = require("../../dal/realmService") -const logger = require("../../utils/logger") -const util = require('util') - -const AddRealm = { - async AddNewRealm(ctx) { - try { - ctx.body = await realmService.create(ctx.request.body) - ctx.status = 201 - logger.info('realm is created successfully!') - } catch (error) { - ctx.body = err.response.data || 'Error occurred!' - ctx.status = 500 - logger.error(`Caught error: ${JSON.stringify(util.inspect(error, { compact: false, depth: 1, breakLength: 80 }))}`) - } - } -} - -module.exports = { - addRealm: AddRealm.AddNewRealm, - AddRealm -} diff --git a/app/api/realm/put.js b/app/api/realm/put.js deleted file mode 100644 index adfa37c..0000000 --- a/app/api/realm/put.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict' - -const realmService = require("../../dal/realmService"); -const logger = require('../../utils/logger') -const util = require('util') - -const UpdateRealm = { - async update(ctx) { - try { - ctx.body = await realmService.update(ctx.request.body); - ctx.status = 201 - logger.info('realm is updated successfully!') - } catch (error) { - ctx.body = err || 'Error occurred!' - ctx.status = 500 - logger.error(`Caught error: ${JSON.stringify(util.inspect(err, { compact: false, depth: 1, breakLength: 80 }))}`) - } - } -} - -module.exports = { - update: UpdateRealm.update, - UpdateRealm -} diff --git a/app/api/role-user/delete.js b/app/api/role-user/delete.js deleted file mode 100644 index ef7c9d1..0000000 --- a/app/api/role-user/delete.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const roleAllocationService = require("../../dal/roleAllocationService"); -const logger = require("../../utils/logger"); - -const DeleteAllocatedRole = { - async delete(ctx) { - try { - ctx.body = await roleAllocationService.delete(ctx.request.body); - ctx.status = 201; - logger.info('Role allocated to user deleted.'); - } catch (error) { - throw error; - } - } -}; - -module.exports = { - deleteAllocatedRole: DeleteAllocatedRole.delete, - DeleteAllocatedRole -}; diff --git a/app/api/role-user/get.js b/app/api/role-user/get.js deleted file mode 100644 index 9936076..0000000 --- a/app/api/role-user/get.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const roleAllocationService = require("../../dal/roleAllocationService"); -const logger = require("../../utils/logger"); - -const GetAllocatedRole = { - async find(ctx) { - try { - ctx.body = await roleAllocationService.allocatedRoles(ctx.request.body); - ctx.status = 201; - logger.info('User allocated roles fetched.'); - } catch (error) { - throw error; - } - } -}; - -module.exports = { - allocatedRoles: GetAllocatedRole.find, - GetAllocatedRole -}; diff --git a/app/api/role-user/index.js b/app/api/role-user/index.js deleted file mode 100644 index 19a8e52..0000000 --- a/app/api/role-user/index.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -const { create } = require("./post"); -const { allocatedRoles } = require('./get'); -const { update } = require('./put'); -const { deleteAllocatedRole } = require('./delete'); - -const AllocationRoleHandler = { - create, - allocatedRoles, - update, - deleteAllocatedRole, -}; - -module.exports = AllocationRoleHandler; diff --git a/app/api/role-user/post.js b/app/api/role-user/post.js deleted file mode 100644 index 0e1c04b..0000000 --- a/app/api/role-user/post.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const roleAllocationService = require("../../dal/roleAllocationService"); -const logger = require("../../utils/logger"); - -const AllocateRoleToUser = { - async create(ctx) { - try { - ctx.body = await roleAllocationService.create(ctx.request.body); - ctx.status = 201; - logger.info('Role allocated to user.'); - } catch (error) { - throw error; - } - } -}; - -module.exports = { - create: AllocateRoleToUser.create, - AllocateRoleToUser -}; diff --git a/app/api/role-user/put.js b/app/api/role-user/put.js deleted file mode 100644 index 9cebeae..0000000 --- a/app/api/role-user/put.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const roleAllocationService = require("../../dal/roleAllocationService"); -const logger = require("../../utils/logger"); - -const UpdateAllocatedRole = { - async update(ctx) { - try { - ctx.body = await roleAllocationService.update(ctx.request.body); - ctx.status = 201; - logger.info('Role allocated to user updated.'); - } catch (error) { - throw error; - } - } -}; - -module.exports = { - update: UpdateAllocatedRole.update, - UpdateAllocatedRole -}; diff --git a/app/api/roles/delete.js b/app/api/roles/delete.js index cd63774..bdc9772 100644 --- a/app/api/roles/delete.js +++ b/app/api/roles/delete.js @@ -1,21 +1,25 @@ -'use strict' +"use strict"; -const roleService = require('../../dal/roleService'); -const logger = require('../../utils/logger'); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { deleteRole } = require("@/app/dal/roleService"); -const DeleteRole = { - async deleteRole(ctx) { - try { - ctx.body = await roleService.delete(ctx.request.body); - ctx.status = 201; - logger.info('Role deleted.'); - } catch (error) { - throw error; - } +async function deleteRoleHandler(ctx) { + const { id } = ctx.params; + if (!id) { + handleMissingParameters(["id"], ctx); + } else { + try { + ctx.body = await deleteRole(id); + ctx.status = 204; + } catch (err) { + handleInternalError(err, ctx); } -}; + } +} module.exports = { - deleteRole: DeleteRole.deleteRole, - DeleteRole, + deleteRoleHandler, }; diff --git a/app/api/roles/get.js b/app/api/roles/get.js index 9ae5a0f..ab85ed4 100644 --- a/app/api/roles/get.js +++ b/app/api/roles/get.js @@ -1,32 +1,50 @@ -'use strict' +"use strict"; -const roleService = require("../../dal/roleService"); -const logger = require('../../utils/logger'); +const { + handleInternalError, + handleMissingParameters, +} = require("../../utils/errorHandler"); +const { getRoles, getRole, getUserRoles } = require("../../dal/roleService"); -const GetRole = { - async find(ctx) { - try { - ctx.body = await roleService.roles(); - ctx.status = 201; - logger.info('Roles fetched.'); - } catch (error) { - throw error; - } - }, +async function getRolesHandler(ctx) { + try { + ctx.body = await getRoles(); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } +} - async findOne(ctx) { - try { - ctx.body = await roleService.role(ctx.request.body); - ctx.status = 201; - logger.info('Role fetched.'); - } catch (error) { - throw error; - } +async function getRoleHandler(ctx) { + const { id } = ctx.params; + if (!id) { + handleMissingParameters(["id"], ctx); + } else { + try { + ctx.body = await getRole(id); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); } -}; + } +} + +async function getUserRolesHandler(ctx) { + const { kc_id } = ctx.request.body; + if (!kc_id) { + handleMissingParameters(["kc_id"], ctx); + } else { + try { + ctx.body = await getUserRoles(kc_id); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } + } +} module.exports = { - roles: GetRole.find, - role: GetRole.findOne, - GetRole + getRolesHandler, + getRoleHandler, + getUserRolesHandler, }; diff --git a/app/api/roles/index.js b/app/api/roles/index.js deleted file mode 100644 index 0cc31da..0000000 --- a/app/api/roles/index.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict' - -const { createRole } = require('./post'); -const { roles } = require('./get'); -const { role } = require('./get'); -const { deleteRole } = require('./delete'); -const { update } = require('./put'); - -const RoleHandler = { - createRole, - roles, - role, - deleteRole, - update, -}; - -module.exports = RoleHandler; diff --git a/app/api/roles/post.js b/app/api/roles/post.js index 58c83a5..0235388 100644 --- a/app/api/roles/post.js +++ b/app/api/roles/post.js @@ -1,21 +1,77 @@ -'use strict' +"use strict"; -const roleService = require("../../dal/roleService"); -const logger = require('../../utils/logger'); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { + createRole, + addUserToRole, + removeUserFromRole, +} = require("@/app/dal/roleService"); -const AddRole = { - async create(ctx) { - try { - ctx.body = await roleService.create(ctx.request.body); - ctx.status = 201; - logger.info('Role created.'); - } catch (error) { - throw error; - } +async function createRoleHandler(ctx) { + const { name, description } = ctx.request.body; + if (!name || !description) { + const missingParameters = []; + if (!name) { + missingParameters.push("name"); } -}; + if (!description) { + missingParameters.push("description"); + } + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await createRole(name, description); + ctx.status = 201; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +async function addUserToRoleHandler(ctx) { + const { id } = ctx.params; + const { kc_id } = ctx.request.body; + if (!id || !kc_id) { + const missingParameters = []; + if (!id) { + missingParameters.push("id"); + } + if (!kc_id) { + missingParameters.push("kc_id"); + } + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await addUserToRole(kc_id, id); + ctx.status = 201; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +async function removeUserFromRoleHandler(ctx) { + const { id, kc_id } = ctx.params; + if (!kc_id || !id) { + const missingParameters = []; + if (!kc_id) missingParameters.push("kc_id"); + if (!id) missingParameters.push("id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await removeUserFromRole(kc_id, id); + ctx.status = 204; + } catch (error) { + handleInternalError(error, ctx); + } + } +} module.exports = { - createRole: AddRole.create, - AddRole + createRoleHandler, + addUserToRoleHandler, + removeUserFromRoleHandler, }; diff --git a/app/api/roles/put.js b/app/api/roles/put.js index 9ed289b..94ab26e 100644 --- a/app/api/roles/put.js +++ b/app/api/roles/put.js @@ -1,21 +1,36 @@ -'use strict' +"use strict"; -const roleService = require('../../dal/roleService'); -const logger = require('../../utils/logger'); +const { + handleInternalError, + handleMissingParameters, +} = require("../../utils/errorHandler"); +const { updateRole } = require("../../dal/roleService"); -const UpdateRole = { - async update(ctx) { - try { - ctx.body = await roleService.update(ctx.request.body); - ctx.status = 201; - logger.info('Role updated.'); - } catch (error) { - throw error; - } +async function updateRoleHandler(ctx) { + const { id } = ctx.params; + const { name, description } = ctx.request.body; + if (!id || !name || !description) { + const missingParameters = []; + if (!id) { + missingParameters.push("id"); } -}; + if (!name) { + missingParameters.push("name"); + } + if (!description) { + missingParameters.push("description"); + } + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await updateRole(id, name, description); + ctx.status = 204; + } catch (err) { + handleInternalError(err, ctx); + } + } +} module.exports = { - update: UpdateRole.update, - UpdateRole + updateRoleHandler, }; diff --git a/app/api/search/post.js b/app/api/search/post.js new file mode 100644 index 0000000..c7c9f07 --- /dev/null +++ b/app/api/search/post.js @@ -0,0 +1,63 @@ +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const {search} = require("@/app/dal/elasticService"); +const logger = require("@/app/utils/format"); + +async function getQueryResultsHandler(ctx) { + logger.debug("getQueryResultsHandler"); + const { + query, + sourcesId, + fieldsId, + scroll_id, + advancedQuery = false, + } = ctx.request.body; + if (!query || !sourcesId || !fieldsId) { + const missingParams = []; + if (!query) missingParams.push("query"); + if (!sourcesId) missingParams.push("sourcesId"); + if (!fieldsId) missingParams.push("fieldsId"); + handleMissingParameters(missingParams, ctx); + } else { + try { + logger.debug(`query: ${JSON.stringify(query)}`); + logger.debug(`sourcesId: ${sourcesId}`); + if (!Array.isArray(sourcesId)) { + handleInternalError(new Error("Invalid sourcesId"), ctx); + } + const allNumbers = sourcesId.every((id) => typeof id === "number"); + if (!allNumbers) { + handleInternalError(new Error("Invalid sourcesId"), ctx); + } else { + const result = await search({ + query, + sourcesId, + scroll_id, + advancedQuery, + }); + logger.debug(`result: ${JSON.stringify(result)}`); + let hits = []; + for (let record of result?.hits?.hits) { + const {_source, _index, _id} = record; + logger.debug(`_source: ${JSON.stringify(_source)}`); + logger.debug(`_index: ${_index}`); + logger.debug(`_id: ${_id}`); + hits.push({ + id: `${_index}_${_id}`, + ..._source, + }); + } + ctx.body = hits; + ctx.status = 200; + } + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +module.exports = { + getQueryResultsHandler, +}; diff --git a/app/api/sources/delete.js b/app/api/sources/delete.js new file mode 100644 index 0000000..758ff98 --- /dev/null +++ b/app/api/sources/delete.js @@ -0,0 +1,25 @@ +"use strict"; + +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { deleteSource } = require("@/app/dal/sourceService"); + +async function deleteSourceHandler(ctx) { + const { id } = ctx.params; + if (!id) { + handleMissingParameters(["id"], ctx); + } else { + try { + await deleteSource(id); + ctx.status = 204; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +module.exports = { + deleteSourceHandler, +}; diff --git a/app/api/sources/get.js b/app/api/sources/get.js new file mode 100644 index 0000000..4d9da46 --- /dev/null +++ b/app/api/sources/get.js @@ -0,0 +1,81 @@ +"use strict"; + +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { + getSources, + getSource, + getSourcesByProvider, + getIndexedSourcesByProvider, + getIndexedSources, +} = require("@/app/dal/sourceService"); + +async function getSourcesHandler(ctx) { + try { + ctx.body = await getSources(); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } +} + +async function getSourceHandler(ctx) { + const { id } = ctx.params; + if (!id) { + handleMissingParameters(["id"], ctx); + } else { + try { + ctx.body = await getSource(id); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +async function getSourcesByProviderHandler(ctx) { + const { kc_id } = ctx.params; + if (!kc_id) { + handleMissingParameters(["kc_id"], ctx); + } else { + try { + ctx.body = await getSourcesByProvider(kc_id); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +async function getIndexedSourcesByProviderHandler(ctx) { + const { kc_id } = ctx.params; + if (!kc_id) { + handleMissingParameters(["kc_id"], ctx); + } else { + try { + ctx.body = await getIndexedSourcesByProvider(kc_id); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +async function getIndexedSourcesHandler(ctx) { + try { + ctx.body = await getIndexedSources(); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } +} + +module.exports = { + getSourcesHandler, + getSourceHandler, + getSourcesByProviderHandler, + getIndexedSourcesByProviderHandler, + getIndexedSourcesHandler, +}; diff --git a/app/api/sources/post.js b/app/api/sources/post.js new file mode 100644 index 0000000..7f1c72c --- /dev/null +++ b/app/api/sources/post.js @@ -0,0 +1,38 @@ +"use strict"; + +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { createSource } = require("@/app/dal/sourceService"); + +async function createSourceHandler(ctx) { + const { name, description, metaUrfms, kc_id } = ctx.request.body; + if (!name || !description || !kc_id || !metaUrfms) { + const missingParams = []; + if (!name) { + missingParams.push("name"); + } + if (!description) { + missingParams.push("description"); + } + if (!kc_id) { + missingParams.push("kc_id"); + } + if (!metaUrfms) { + missingParams.push("metaUrfms"); + } + handleMissingParameters(missingParams, ctx); + } else { + try { + ctx.body = await createSource(name, description, metaUrfms, kc_id); + ctx.status = 201; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +module.exports = { + createSourceHandler, +}; diff --git a/app/api/std_fields/delete.js b/app/api/std_fields/delete.js new file mode 100644 index 0000000..ef98db6 --- /dev/null +++ b/app/api/std_fields/delete.js @@ -0,0 +1,17 @@ +"use strict"; + +const { handleInternalError } = require("@/app/utils/errorHandler"); +const { deleteAllStdFields } = require("@/app/dal/stdFieldService"); + +async function deleteAllStdFieldsHandler(ctx) { + try { + ctx.body = await deleteAllStdFields(); + ctx.status = 204; + } catch (error) { + handleInternalError(error, ctx); + } +} + +module.exports = { + deleteAllStdFieldsHandler, +}; diff --git a/app/api/std_fields/get.js b/app/api/std_fields/get.js new file mode 100644 index 0000000..35b649b --- /dev/null +++ b/app/api/std_fields/get.js @@ -0,0 +1,49 @@ +"use strict"; + +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { + getStdFields, + getStdField, + getPublicStdFields, +} = require("@/app/dal/stdFieldService"); + +async function getStdFieldsHandler(ctx) { + try { + ctx.body = await getStdFields(); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } +} + +async function getPublicStdFieldsHandler(ctx) { + try { + ctx.body = await getPublicStdFields(); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } +} + +async function getStdFieldHandler(ctx) { + const { id } = ctx.params; + if (!id) { + handleMissingParameters(["id"], ctx); + } else { + try { + ctx.body = await getStdField(id); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +module.exports = { + getStdFieldsHandler, + getPublicStdFieldsHandler, + getStdFieldHandler, +}; diff --git a/app/api/std_fields/post.js b/app/api/std_fields/post.js new file mode 100644 index 0000000..2bdaa39 --- /dev/null +++ b/app/api/std_fields/post.js @@ -0,0 +1,17 @@ +"use strict"; + +const {handleInternalError} = require("@/app/utils/errorHandler"); +const {createOrUpdateStdField} = require("@/app/dal/stdFieldService"); + +async function createOrUpdateStdFieldHandler(ctx) { + try { + ctx.body = await createOrUpdateStdField(ctx.request.body); + ctx.status = 201; + } catch (error) { + handleInternalError(error, ctx); + } +} + +module.exports = { + createOrUpdateStdFieldHandler, +}; diff --git a/app/api/std_fields/update.js b/app/api/std_fields/update.js new file mode 100644 index 0000000..295e4bc --- /dev/null +++ b/app/api/std_fields/update.js @@ -0,0 +1,27 @@ +"use strict"; + +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { createOrUpdateStdField } = require("@/app/dal/stdFieldService"); + +async function updateStdFieldHandler(ctx) { + const { id } = ctx.params; + if (!id) { + const missingParameters = []; + if (!id) missingParameters.push("id"); + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await createOrUpdateStdField(ctx.request.body); + ctx.status = 204; + } catch (error) { + handleInternalError(error, ctx); + } + } +} + +module.exports = { + updateStdFieldHandler, +}; diff --git a/app/api/user-display-fields/get.js b/app/api/user-display-fields/get.js new file mode 100644 index 0000000..7081b7b --- /dev/null +++ b/app/api/user-display-fields/get.js @@ -0,0 +1,25 @@ +const { getUserDisplayFields } = require("../../dal/userDisplayFieldsService"); +const { + handleInternalError, + handleMissingParameters, +} = require("../../utils/errorHandler"); + +async function getUserDisplayFieldsHandler(ctx) { + const { kc_id } = ctx.params; + if (!kc_id) { + const missingParams = []; + if (!kc_id) missingParams.push("kc_id"); + handleMissingParameters(missingParams, ctx); + } else { + try { + ctx.body = await getUserDisplayFields(kc_id); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +module.exports = { + getUserDisplayFieldsHandler, +}; diff --git a/app/api/user-display-fields/post.js b/app/api/user-display-fields/post.js new file mode 100644 index 0000000..b2f61cd --- /dev/null +++ b/app/api/user-display-fields/post.js @@ -0,0 +1,27 @@ +const { + handleInternalError, + handleMissingParameters, +} = require("../../utils/errorHandler"); +const { setUserDisplayFields } = require("../../dal/userDisplayFieldsService"); + +async function setUserDisplayFieldsHandler(ctx) { + const { kc_id } = ctx.params; + const { fields_id } = ctx.request.body; + if (!kc_id || !fields_id) { + const missingParams = []; + if (!kc_id) missingParams.push("kc_id"); + if (!fields_id) missingParams.push("fields_id"); + handleMissingParameters(missingParams, ctx); + } else { + try { + ctx.body = await setUserDisplayFields(kc_id, fields_id); + ctx.status = 201; + } catch (err) { + handleInternalError(err, ctx); + } + } +} + +module.exports = { + setUserDisplayFieldsHandler, +}; diff --git a/app/api/user-fields-display-settings/delete.js b/app/api/user-fields-display-settings/delete.js deleted file mode 100644 index 743ae4a..0000000 --- a/app/api/user-fields-display-settings/delete.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const userFieldsDisplaySettingsService = require("../../dal/userFieldsDisplaySettingsService"); -const logger = require('../../utils/logger'); - -const UserFieldsDisplaySettings = { - async delete(ctx) { - try { - ctx.body = await userFieldsDisplaySettingsService.delete(ctx.request.params); - ctx.status = 201; - logger.info('User fields display settings deleted.'); - } catch (error) { - throw error; - } - } -} - -module.exports = { - deleteUserFieldsDisplaySettings: UserFieldsDisplaySettings.delete, - UserFieldsDisplaySettings -}; diff --git a/app/api/user-fields-display-settings/get.js b/app/api/user-fields-display-settings/get.js deleted file mode 100644 index e8b08ef..0000000 --- a/app/api/user-fields-display-settings/get.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; - -const logger = require('../../utils/logger') -const userFieldsDisplaySettingsService = require("../../dal/userFieldsDisplaySettingsService"); - -const GetUserFieldsDisplaySettings = { - async find(ctx) { - try { - ctx.body = await userFieldsDisplaySettingsService.usersFieldsDisplaySettings(); - ctx.status = 201; - logger.info('Users fields display settings retrieved.'); - } catch (error) { - throw error; - } - }, - - async findOne(ctx) { - try { - ctx.body = await userFieldsDisplaySettingsService.userFieldsDisplaySettings(ctx.request.params); - ctx.status = 201; - logger.info('User fields display settings retrieved.'); - } catch (error) { - throw error; - } - }, -}; - -module.exports = { - findUsersFieldsDisplaySettings: GetUserFieldsDisplaySettings.find, - findUserFieldsDisplaySettings: GetUserFieldsDisplaySettings.findOne, - GetUserFieldsDisplaySettings -}; diff --git a/app/api/user-fields-display-settings/index.js b/app/api/user-fields-display-settings/index.js deleted file mode 100644 index 338b10b..0000000 --- a/app/api/user-fields-display-settings/index.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; - -const { createUserFieldsDisplaySettings } = require('./post'); -const { findUsersFieldsDisplaySettings, findUserFieldsDisplaySettings } = require('./get'); -const { updateUserFieldsDisplaySettings } = require('./put'); -const { deleteUserFieldsDisplaySettings } = require('./delete'); - -const UserFieldsDisplaySettingsHandler = { - createUserFieldsDisplaySettings, - findUsersFieldsDisplaySettings, - findUserFieldsDisplaySettings, - updateUserFieldsDisplaySettings, - deleteUserFieldsDisplaySettings -} - -module.exports = UserFieldsDisplaySettingsHandler diff --git a/app/api/user-fields-display-settings/post.js b/app/api/user-fields-display-settings/post.js deleted file mode 100644 index 2b0075b..0000000 --- a/app/api/user-fields-display-settings/post.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const userFieldsDisplaySettingsService = require("../../dal/userFieldsDisplaySettingsService"); -const logger = require('../../utils/logger') - -const CreateUserFieldsDisplaySettings = { - async create(ctx) { - try { - ctx.body = await userFieldsDisplaySettingsService.create(ctx.request.body); - ctx.status = 201; - logger.info('User fields display settings created.'); - } catch (error) { - throw error; - } - }, -} - -module.exports = { - createUserFieldsDisplaySettings: CreateUserFieldsDisplaySettings.create, - CreateUserFieldsDisplaySettings -} diff --git a/app/api/user-fields-display-settings/put.js b/app/api/user-fields-display-settings/put.js deleted file mode 100644 index 066f593..0000000 --- a/app/api/user-fields-display-settings/put.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const userFieldsDisplaySettingsService = require("../../dal/userFieldsDisplaySettingsService"); -const logger = require('../../utils/logger'); - -const UpdateUserFieldsDisplaySettings = { - async update(ctx) { - try { - ctx.body = await userFieldsDisplaySettingsService.update(ctx.request.body); - ctx.status = 201; - logger.info('User fields display settings updated.'); - } catch (error) { - throw error; - } - } -} - -module.exports = { - updateUserFieldsDisplaySettings: UpdateUserFieldsDisplaySettings.update, - UpdateUserFieldsDisplaySettings -}; diff --git a/app/api/user-profile/index.js b/app/api/user-profile/index.js deleted file mode 100644 index ccacec3..0000000 --- a/app/api/user-profile/index.js +++ /dev/null @@ -1 +0,0 @@ -'use strict' diff --git a/app/api/user-requests/delete.js b/app/api/user-requests/delete.js index d1be7f9..983c56f 100644 --- a/app/api/user-requests/delete.js +++ b/app/api/user-requests/delete.js @@ -1,21 +1,24 @@ -'use strict' +"use strict"; -const userRequestService = require('../../dal/userRequestService'); -const logger = require('../../utils/logger'); +const { + handleInternalError, + handleMissingParameters, +} = require("../../utils/errorHandler"); +const { deleteRequest } = require("../../dal/userRequestService"); -const UserRequest = { - async delete(ctx) { - try { - ctx.body = await userRequestService.delete(ctx.request.body); - ctx.status = 201; - logger.info('User request deleted.'); - } catch (error) { - throw error; - } +async function deleteRequestHandler(ctx) { + const { id } = ctx.params; + if (!id) { + handleMissingParameters(["id"], ctx); + } else { + try { + await deleteRequest(id); + ctx.status = 204; + } catch (err) { + handleInternalError(err, ctx); } -}; - + } +} module.exports = { - deleteRequest: UserRequest.delete, - UserRequest + deleteRequestHandler, }; diff --git a/app/api/user-requests/get.js b/app/api/user-requests/get.js index b62ee08..00d849f 100644 --- a/app/api/user-requests/get.js +++ b/app/api/user-requests/get.js @@ -1,32 +1,64 @@ -'use strict' +"use strict"; -const userRequestService = require('../../dal/userRequestService'); -const logger = require('../../utils/logger'); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { + getAllRequests, + getAllPendingRequests, +} = require("@/app/dal/userRequestService"); -const UserRequest = { - async list(ctx) { - try { - ctx.body = await userRequestService.fetch(); - ctx.status = 201; - logger.info('User requests fetched.'); - } catch (error) { - throw error; - } - }, +async function getAllRequestsHandler(ctx) { + try { + ctx.body = await getAllRequests(); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } +} - async listPending(ctx) { - try { - ctx.body = await userRequestService.fetchPending(); - ctx.status = 201; - logger.info('Pending user requests fetched.'); - } catch (error) { - throw error; - } +async function getAllPendingRequestsHandler(ctx) { + try { + ctx.body = await getAllPendingRequests(); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } +} + +async function getAllUserRequestsHandler(ctx) { + const { kc_id } = ctx.params; + if (!kc_id) { + handleMissingParameters(["kc_id"], ctx); + } else { + try { + ctx.body = await getAllRequests(kc_id); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); } -}; + } +} + +async function getAllPendingUserRequestsHandler(ctx) { + const { kc_id } = ctx.params; + if (!kc_id) { + handleMissingParameters(["kc_id"], ctx); + } else { + try { + const kc_id = ctx.params.kc_id; + ctx.body = await getAllPendingRequests(kc_id); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } + } +} module.exports = { - fetchRequests: UserRequest.list, - fetchPendingRequests: UserRequest.listPending, - UserRequest + getAllRequestsHandler, + getAllPendingRequestsHandler, + getAllUserRequestsHandler, + getAllPendingUserRequestsHandler, }; diff --git a/app/api/user-requests/index.js b/app/api/user-requests/index.js deleted file mode 100644 index 8d0a9ea..0000000 --- a/app/api/user-requests/index.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' - -const { createRequest, processRequest, fetchRequestsByUser } = require('./post'); -const { fetchRequests, fetchPendingRequests } = require('./get'); -const { deleteRequest } = require('./delete'); - -const UserRequestHandler = { - createRequest, - processRequest, - fetchRequests, - fetchPendingRequests, - fetchRequestsByUser, - deleteRequest -}; - -module.exports = UserRequestHandler; diff --git a/app/api/user-requests/post.js b/app/api/user-requests/post.js index d0b90af..ffffef4 100644 --- a/app/api/user-requests/post.js +++ b/app/api/user-requests/post.js @@ -1,43 +1,65 @@ -'use strict' +"use strict"; -const userRequestService = require('../../dal/userRequestService'); -const logger = require('../../utils/logger'); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { + createRequest, + updateRequest, +} = require("@/app/dal/userRequestService"); -const UserRequest = { - async create(ctx) { - try { - ctx.body = await userRequestService.create(ctx.request.body); - ctx.status = 201; - logger.info('User request created.'); - } catch (error) { - throw error; - } - }, - - async process(ctx) { - try { - ctx.body = await userRequestService.process(ctx.request.body); - ctx.status = 201; - logger.info('User request processed.'); - } catch (error) { - throw error; - } - }, +async function createRequestHandler(ctx) { + const { kc_id } = ctx.params; + const { message } = ctx.request.body; + if (!kc_id || !message) { + const missingParameters = []; + if (!kc_id) { + missingParameters.push("kc_id"); + } + if (!message) { + missingParameters.push("message"); + } + handleMissingParameters(missingParameters, ctx); + console.log("missingParameters", missingParameters); + } else { + try { + ctx.body = await createRequest(kc_id, message); + ctx.status = 201; + } catch (err) { + handleInternalError(err, ctx); + } + } +} - async listByUser(ctx) { - try { - ctx.body = await userRequestService.fetchByUser(ctx.request.body); - ctx.status = 201; - logger.info('User requests fetched by user.'); - } catch (error) { - throw error; - } +async function updateRequestHandler(ctx) { + const { id } = ctx.params; + const { is_processed } = ctx.request.body; + if (!id || is_processed === undefined) { + const missingParameters = []; + if (!id) { + missingParameters.push("id"); } -}; + if (is_processed === undefined) { + missingParameters.push("is_processed"); + } + await handleMissingParameters(missingParameters, ctx); + } else { + if (typeof is_processed !== "boolean") { + await handleMissingParameters(["is_processed must be a boolean"], ctx); + console.log("is_processed must be a boolean"); + } else { + try { + ctx.body = await updateRequest(id, is_processed); + ctx.status = 201; + } catch (err) { + await handleInternalError(err, ctx); + } + } + } +} module.exports = { - createRequest: UserRequest.create, - processRequest: UserRequest.process, - fetchRequestsByUser: UserRequest.listByUser, - UserRequest + createRequestHandler, + updateRequestHandler, }; diff --git a/app/api/user-search-history/delete.js b/app/api/user-search-history/delete.js index a1f7ef9..52f5621 100644 --- a/app/api/user-search-history/delete.js +++ b/app/api/user-search-history/delete.js @@ -1,21 +1,44 @@ -'use strict' +"use strict"; -const searchHistoryService = require('../../dal/searchHistoryService'); -const logger = require('../../utils/logger'); +const { + deleteHistory, + deleteAllHistoryByUser, +} = require("@/app/dal/searchHistoryService"); -const SearchHistory = { - async delete(ctx) { - try { - ctx.body = await searchHistoryService.delete(ctx.request.body); - ctx.status = 201; - logger.info('User search history deleted.'); - } catch (error) { - throw error; - } +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); + +async function deleteHistoryHandler(ctx) { + const { id } = ctx.params; + if (!id) { + handleMissingParameters(["id"], ctx); + } else { + try { + await deleteHistory(id); + ctx.status = 204; + } catch (error) { + handleInternalError(error, ctx); } -}; + } +} + +async function deleteAllHistoryByUserHandler(ctx) { + const { kc_id } = ctx.params; + if (!kc_id) { + handleMissingParameters(["kc_id"], ctx); + } else { + try { + await deleteAllHistoryByUser(kc_id); + ctx.status = 204; + } catch (error) { + handleInternalError(error, ctx); + } + } +} module.exports = { - deleteUserSearchHistory: SearchHistory.delete, - SearchHistory + deleteHistoryHandler, + deleteAllHistoryByUserHandler, }; diff --git a/app/api/user-search-history/get.js b/app/api/user-search-history/get.js index e8cc7b7..681fe78 100644 --- a/app/api/user-search-history/get.js +++ b/app/api/user-search-history/get.js @@ -1,21 +1,25 @@ -'use strict' +"use strict"; -const searchHistoryService = require('../../dal/searchHistoryService'); -const logger = require('../../utils/logger'); +const { getSearchHistoryByUser } = require("@/app/dal/searchHistoryService"); +const { + handleMissingParameters, + handleInternalError, +} = require("@/app/utils/errorHandler"); -const SearchHistory = { - async list(ctx) { - try { - ctx.body = await searchHistoryService.fetch(ctx.request.body); - ctx.status = 201; - logger.info('User search history fetched.'); - } catch (error) { - throw error; - } +async function getHistoryHandler(ctx) { + const { kc_id } = ctx.params; + if (!kc_id) { + handleMissingParameters(["kc_id"], ctx); + } else { + try { + ctx.body = await getSearchHistoryByUser(kc_id); + ctx.status = 200; + } catch (error) { + handleInternalError(error, ctx); } -}; + } +} module.exports = { - fetch: SearchHistory.list, - SearchHistory + getHistoryHandler, }; diff --git a/app/api/user-search-history/index.js b/app/api/user-search-history/index.js deleted file mode 100644 index 7dab270..0000000 --- a/app/api/user-search-history/index.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -const { create } = require('./post'); -const { fetch } = require('./get'); -const { deleteUserSearchHistory } = require('./delete'); - -const SearchHistoryHandler = { - create, - fetch, - deleteUserSearchHistory -}; - -module.exports = SearchHistoryHandler; diff --git a/app/api/user-search-history/post.js b/app/api/user-search-history/post.js index 5739977..152cb38 100644 --- a/app/api/user-search-history/post.js +++ b/app/api/user-search-history/post.js @@ -1,21 +1,48 @@ -'use strict' +"use strict"; -const searchHistoryService = require('../../dal/searchHistoryService'); -const logger = require('../../utils/logger'); +const { addSearchHistoryToUser } = require("@/app/dal/searchHistoryService"); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); -const SearchHistory = { - async create(ctx) { - try { - ctx.body = await searchHistoryService.create(ctx.request.body); - ctx.status = 201; - logger.info('Search history created.'); - } catch (error) { - throw error; - } +async function addSearchHistoryToUserHandler(ctx) { + const { kc_id } = ctx.params; + const { query, name, ui_structure, description } = ctx.request.body; + if (!kc_id || !query || !name || !ui_structure || !description) { + const missingParameters = []; + if (!kc_id) { + missingParameters.push("kc_id"); } -}; + if (!query) { + missingParameters.push("query"); + } + if (!name) { + missingParameters.push("name"); + } + if (!ui_structure) { + missingParameters.push("ui_structure"); + } + if (!description) { + missingParameters.push("description"); + } + handleMissingParameters(missingParameters, ctx); + } else { + try { + ctx.body = await addSearchHistoryToUser( + kc_id, + query, + name, + ui_structure, + description + ); + ctx.status = 201; + } catch (error) { + handleInternalError(error, ctx); + } + } +} module.exports = { - create: SearchHistory.create, - SearchHistory + addSearchHistoryToUserHandler, }; diff --git a/app/api/users/delete.js b/app/api/users/delete.js index ebe5496..f429457 100644 --- a/app/api/users/delete.js +++ b/app/api/users/delete.js @@ -1,21 +1,25 @@ -'use strict' +"use strict"; -const userService = require("../../dal/userService"); -const logger = require('../../utils/logger'); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { deleteUser } = require("@/app/dal/userService"); -const DeleteUser = { - async deleteUser(ctx) { - try { - ctx.body = await userService.delete(ctx.request.body); - ctx.status = 201; - logger.info('User deleted.'); - } catch (error) { - throw error; - } +async function deleteUserHandler(ctx) { + const { kc_id } = ctx.params; + if (!kc_id) { + handleMissingParameters(["kc_id"], ctx); + } else { + try { + ctx.body = await deleteUser(kc_id); + ctx.status = 204; + } catch (err) { + handleInternalError(err, ctx); } -}; + } +} module.exports = { - deleteUser: DeleteUser.deleteUser, - DeleteUser + deleteUserHandler, }; diff --git a/app/api/users/get.js b/app/api/users/get.js index f35efd2..cfe8e86 100644 --- a/app/api/users/get.js +++ b/app/api/users/get.js @@ -1,76 +1,42 @@ "use strict"; -const userService = require("../../dal/userService"); -const logger = require('../../utils/logger'); - -const GetUser = { - async find(ctx) { - try { - ctx.body = await userService.users(); - ctx.status = 201; - logger.info('Users fetched.'); - } catch (error) { - throw error; - } - }, - - async findOne(ctx) { - try { - ctx.body = await userService.user(ctx.request.body); - ctx.status = 201; - logger.info('User fetched.'); - } catch (error) { - throw error; - } - }, - - async kcId(ctx) { - try { - ctx.body = await userService.kcId(ctx.request.body); - ctx.status = 201; - logger.info('User kc_id fetched.'); - } catch (error) { - throw error; - } - }, - - async detail(ctx) { - try { - ctx.body = await userService.detail(ctx.request.body); - ctx.status = 201; - logger.info('User details fetched.'); - } catch (error) { - throw error; - } - }, - - async getAssignedUserByRole(ctx) { - try { - ctx.body = await userService.getAssignedUserByRole(ctx.request.body); - ctx.status = 201; - logger.info('Users fetched by role.'); - } catch (error) { - throw error; - } - }, - - async usersWithGroupAndRole(ctx) { +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); +const { getUsers, getUser } = require("@/app/dal/userService"); + +async function getUsersHandler(ctx) { + try { + ctx.body = await getUsers(); + ctx.status = 200; + } catch (err) { + handleInternalError(err, ctx); + } +} + +async function getUserHandler(ctx) { + const { params } = ctx; + const { kc_id } = params; + if (!kc_id) { + handleMissingParameters(["kc_id"], ctx); + } else { try { - ctx.body = await userService.usersWithGroupAndRole(); - ctx.status = 201; - logger.info('Users with groups and roles fetched.'); - } catch (error) { - throw error; + const user = await getUser(kc_id); + if (!user) { + ctx.status = 404; + ctx.body = { message: "User not found" }; + } else { + ctx.body = user; + ctx.status = 200; + } + } catch (err) { + handleInternalError(err, ctx); } } -}; +} module.exports = { - users: GetUser.find, - user: GetUser.findOne, - kcId: GetUser.kcId, - detail: GetUser.detail, - getAssignedUserByRole: GetUser.getAssignedUserByRole, - usersWithGroupAndRole: GetUser.usersWithGroupAndRole, - GetUser + getUsersHandler, + getUserHandler, }; diff --git a/app/api/users/index.js b/app/api/users/index.js deleted file mode 100644 index c228799..0000000 --- a/app/api/users/index.js +++ /dev/null @@ -1,24 +0,0 @@ -"use strict"; - -const { create, createSystemUser, sendMail, userWithGroupAndRole, resetPassword } = require('./post'); -const { user, users, kcId, detail, getAssignedUserByRole, usersWithGroupAndRole } = require('./get'); -const { deleteUser } = require('./delete'); -const { update } = require('./put'); - -const UserHandler = { - create, - user, - users, - deleteUser, - update, - kcId, - detail, - createSystemUser, - getAssignedUserByRole, - usersWithGroupAndRole, - userWithGroupAndRole, - sendMail, - resetPassword -}; - -module.exports = UserHandler; diff --git a/app/api/users/post.js b/app/api/users/post.js index 87e5d64..619d2f6 100644 --- a/app/api/users/post.js +++ b/app/api/users/post.js @@ -1,65 +1,39 @@ -'use strict' +"use strict"; -const userService = require("../../dal/userService"); -const logger = require('../../utils/logger'); +const { + handleInternalError, + handleMissingParameters, +} = require("@/app/utils/errorHandler"); -const AddUser = { - async create(ctx) { - try { - ctx.body = await userService.create(ctx.request.body); - ctx.status = 201; - logger.info('User created.'); - } catch (error) { - throw error; - } - }, +const { createUser, getUser } = require("@/app/dal/userService"); - async createSystemUser(ctx) { - try { - ctx.body = await userService.createSystemUser(); - ctx.status = 201; - logger.info('System user (admin) created.'); - } catch (error) { - throw error; - } - }, - - async sendMail(ctx) { - try { - ctx.body = await userService.sendMail(ctx.request.body); - ctx.status = 201; - logger.info('Mail sent.'); - } catch (error) { - throw error; - } - }, - - async userWithGroupAndRole(ctx) { - try { - ctx.body = await userService.userWithGroupAndRole(ctx.request.body); - ctx.status = 201; - logger.info('User with groups and roles fetched.'); - } catch (error) { - throw error; - } - }, - - async resetPassword(ctx) { - try { - ctx.body = await userService.resetPassword(ctx.request.body); - ctx.status = 201; - logger.info('Password reset.'); - } catch (error) { - throw error; - } +async function getOrCreateUserHandler(ctx) { + const { kc_id, email } = ctx.request.body; + if (!kc_id || !email) { + const missingParameters = []; + if (!kc_id) { + missingParameters.push("kc_id"); } + if (!email) { + missingParameters.push("email"); + } + handleMissingParameters(missingParameters, ctx); + } else { + try { + const user = await getUser(kc_id); + if (user) { + ctx.body = user; + ctx.status = 200; + } else { + ctx.body = await createUser(kc_id, email); + ctx.status = 201; + } + } catch (err) { + handleInternalError(err, ctx); + } + } } module.exports = { - create: AddUser.create, - createSystemUser: AddUser.createSystemUser, - sendMail: AddUser.sendMail, - userWithGroupAndRole: AddUser.userWithGroupAndRole, - resetPassword: AddUser.resetPassword, - AddUser -} + getOrCreateUserHandler, +}; diff --git a/app/api/users/put.js b/app/api/users/put.js deleted file mode 100644 index 3d5db10..0000000 --- a/app/api/users/put.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const userService = require("../../dal/userService"); -const logger = require('../../utils/logger'); - -const UpdateUser = { - async update(ctx) { - try { - ctx.body = await userService.update(ctx.request.body); - ctx.status = 201; - logger.info('User updated.'); - } catch (error) { - throw error; - } - } -}; - -module.exports = { - update: UpdateUser.update, - UpdateUser -}; diff --git a/app/config/db.js b/app/config/db.js deleted file mode 100644 index 7c5396f..0000000 --- a/app/config/db.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict' - -const path = require('path') -const joi = require('@hapi/joi') -const Sequelize = require('sequelize') - -const envVarsSchema = joi.object({ - DB_HOST: joi.string().required(), - DB_USER: joi.string().required(), - DB_PASSWORD: joi.string().allow('').required(), - DB_DATABASE: joi.string().required(), - DB_PORT: joi.number().required(), - DB_POOL_MIN: joi.number().default(1), - DB_POOL_MAX: joi.number().default(20), - DB_DEBUG: joi.bool().default(false) -}).unknown() - .required() - -const envVars = joi.attempt(process.env, envVarsSchema) - -const config = { - client: 'pg', - connection: { - host: process.env.DB_HOST, - user: process.env.DB_USER, - password: process.env.DB_PASSWORD, - database: process.env.DB_DATABASE, - port: process.env.DB_PORT - }, - pool: { - min: envVars.DB_POOL_MIN, - max: envVars.DB_POOL_MAX - }, - migrations: { - directory: path.join(__dirname, '../migrations') - }, - debug: envVars.DB_DEBUG -} - -const clientPgSeq = new Sequelize(config.connection.database, config.connection.user, config.connection.password, { - host: config.connection.host, - port: config.connection.port, - pool: config.pool, - dialect: 'postgres', - define: { - createdAt: "createdat", - updatedAt: "updatedat" - }, -}) - -module.exports = { - configPg: config, - postgresSeq: clientPgSeq, -} \ No newline at end of file diff --git a/app/config/elk.js b/app/config/elk.js new file mode 100644 index 0000000..7daa58c --- /dev/null +++ b/app/config/elk.js @@ -0,0 +1,17 @@ +"use strict"; +const joi = require("@hapi/joi"); + +const envVarsSchema = joi + .object({ + ELK_URL: joi.string().required(), + ELK_USERNAME: joi.string().required(), + ELK_PASSWORD: joi.string().required(), + }) + .unknown() + .required(); + +const configElk = joi.attempt(process.env, envVarsSchema); + +module.exports = { + configElk, +}; diff --git a/app/config/mongo.js b/app/config/mongo.js new file mode 100644 index 0000000..0b1eaa3 --- /dev/null +++ b/app/config/mongo.js @@ -0,0 +1,25 @@ +"use strict"; +const joi = require("@hapi/joi"); + +const envVarsSchema = joi + .object({ + MONGO_PORT: joi.number().required(), + MONGO_HOST: joi.string().required(), + MONGO_DB_NAME: joi.string().required(), + MONGO_USERNAME: joi.string().required(), + MONGO_PASSWORD: joi.string().required(), + }) + .unknown() + .required(); + +const env = joi.attempt(process.env, envVarsSchema); + +const MongoClient = require("mongodb").MongoClient; +const authMechanism = "DEFAULT"; +const url = `mongodb://${env.MONGO_USERNAME}:${env.MONGO_PASSWORD}@${env.MONGO_HOST}:${env.MONGO_PORT}/?authMechanism=${authMechanism}`; +const mongoClient = new MongoClient(url); + +module.exports = { + mongoClient, + dbName: env.MONGO_DB_NAME, +}; diff --git a/app/config/postgres.js b/app/config/postgres.js new file mode 100644 index 0000000..8627305 --- /dev/null +++ b/app/config/postgres.js @@ -0,0 +1,12 @@ +"use strict"; + +const joi = require("@hapi/joi"); + +const envVarsSchema = joi + .object({ + DATABASE_URL: joi.string().required(), + }) + .unknown() + .required(); + +joi.attempt(process.env, envVarsSchema); diff --git a/app/dal/authService.js b/app/dal/authService.js new file mode 100644 index 0000000..5c8eff6 --- /dev/null +++ b/app/dal/authService.js @@ -0,0 +1,31 @@ +const logger = require("@/app/utils/format"); + +async function isAccessTokenValid(accessToken) { + if (!accessToken) { + return false; + } + + const issuerUrl = process.env.KEYCLOAK_BASE_URL; + const realm = process.env.KEYCLOAK_REALM; + const userEndpoint = + issuerUrl + "/realms/" + realm + "/protocol/openid-connect/userinfo"; + + const response = await fetch(userEndpoint, { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + switch (response.status) { + case 401: + return false; + case 200: + return true; + default: + logger.error(`Error validating access token: ${response.statusText}`); + return false; + } +} + +module.exports = { + isAccessTokenValid, +}; diff --git a/app/dal/elasticService.js b/app/dal/elasticService.js new file mode 100644 index 0000000..f7d9b9d --- /dev/null +++ b/app/dal/elasticService.js @@ -0,0 +1,153 @@ +const {Client} = require("@elastic/elasticsearch"); +const prisma = require("@/prisma/client"); +const {configElk} = require("@/app/config/elk"); +const logger = require("@/app/utils/format"); + +async function getClientInstance() { + logger.debug(`Connecting to ${configElk.ELK_URL}`); + const client = new Client({ + node: configElk.ELK_URL, + maxRetries: 5, + requestTimeout: 60000, + sniffOnStart: false, + auth: { + username: configElk.ELK_USERNAME, + password: configElk.ELK_PASSWORD, + }, + }); + if (!(await client.ping())) { + logger.error("Failed to connect to ElasticSearch"); + } + logger.debug("Connected to ElasticSearch"); + return client; +} + +async function prepareForBulk(metaUrfms, elkIndex) { + const client = await getClientInstance(); + const response = await client.indices.create( + { + index: elkIndex, + }, + {ignore: [400]} + ); + + if (!response.acknowledged) { + logger.error(`Index ${elkIndex} not created`); + throw new Error(`Index ${elkIndex} not created`); + } + logger.debug(`Index ${elkIndex} created`); + + const body = metaUrfms.flatMap((doc) => [ + {index: {_index: elkIndex}}, + doc, + ]); + return body; +} + +async function bulk(body) { + const client = await getClientInstance(); + logger.debug("Indexing documents"); + const {errors, items} = await client.bulk({body, refresh: true}); + if (errors) { + logger.error("Error while bulk indexing", errors); + return false; + } + logger.debug("Documents indexed :" + items.length); + return true; +} + +async function search({query, sourcesId, scroll_id, advancedQuery}) { + const client = await getClientInstance(); + logger.debug(`Elasticsearch query: ${JSON.stringify(query)}`); + logger.debug("Advanced query: " + advancedQuery); + if (scroll_id) { + const {body} = await client.scroll({ + index, + scroll_id, + scroll: "10s", + }); + return body; + } else { + let queryBuilder; + if (!advancedQuery) { + queryBuilder = { + query: { + query_string: { + query: query, + default_operator: "or", + }, + }, + }; + } else { + try { + // remove trailing and leading whitespaces, tabs, newlines, commas, and semicolons + query = query.replace(/(^[,\s\n\t]+)|([,\s\n\t]+$)/g, ""); + logger.debug("Advanced query: " + query); + query = JSON.parse(query); + queryBuilder = { + query: query + }; + } catch (error) { + logger.error("Invalid query", error); + return {hits: {hits: []}}; + } + } + let indices = []; + for (let sourceId of sourcesId) { + const source = await prisma.source.findUnique({ + where: { + id: +sourceId, + }, + include: { + source_indices: true, + }, + }); + if (!source) { + logger.error(`Source ${sourceId} not found`); + continue; + } + for (let index of source.source_indices) { + indices.push(index.index_id); + } + } + logger.debug(`Searching in indices: ${indices.join(",")}`); + if (indices.length === 0) { + logger.error("No indices found for the given sources"); + return {hits: {hits: []}}; + } + logger.debug("Query : " + JSON.stringify(queryBuilder)); + const response = await client.search({ + index: indices.join(","), + body: queryBuilder, + scroll: "10s", + }); + logger.debug(`Search results: ${response.hits.total.value}`); + return response; + } +} + +async function countByIndex(index) { + const client = await getClientInstance(); + const {body: count} = await client.count({index}); + return count; +} + +async function deleteIndex(index) { + const client = await getClientInstance(); + logger.debug(`Deleting index ${index}`); + try { + await client.indices.delete({index}); + } catch (error) { + logger.error(`Error while deleting index ${index}`, error); + return false; + } + return true; +} + +module.exports = { + prepareForBulk, + bulk, + countByIndex, + deleteIndex, + search, +}; diff --git a/app/dal/groupService.js b/app/dal/groupService.js index d2a6b17..c95d5e6 100644 --- a/app/dal/groupService.js +++ b/app/dal/groupService.js @@ -1,215 +1,312 @@ -'use strict' - -const { Group, GroupUser, GroupsPolicy } = require('../models/Group') -const userService = require('./userService') -const db = require('../../db') - -const GroupService = { - async createGroup(ctx) { - if (!ctx || !ctx.kcId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const user = await userService.detail({ kcId: ctx.kcId }); - if (!user) { - const error = new Error("User account not found."); - error.status = 400; - throw error; - } - const [newGroup, isCreated] = await Group.findOrCreate( - { - where: { - name: ctx.name, - user_id: user.id, - }, - defaults: { - description: ctx.description, - }, - }); - if (!isCreated) { - const error = new Error(`Group:${ctx.name} already exists.`); - error.status = 400; - throw error; - } - return newGroup; - }, - - async createGroupPolicy(ctx) { - if (!ctx || !ctx.groupId || !ctx.policyId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const groupPolicy = await GroupsPolicy.findOne({ - where: { - group_id: ctx.groupId, - policy_id: ctx.policyId - } - }); - if (groupPolicy) { - const error = new Error(`GroupId:${ctx.groupId} and policyId:${ctx.policyId} already assigned to each other.`); - error.status = 400; - throw error; - } else { - return await GroupsPolicy.create({ - group_id: ctx.groupId, - policy_id: ctx.policyId - }); - } - }, - - async createGroupUser(ctx) { - if (!ctx || !ctx.kcId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const user = await userService.detail({ kcId: ctx.kcId }); - if (user) { - const groupUser = await GroupUser.findOne({ - where: { - group_id: ctx.groupId, - user_id: user.id - } - }); - if (groupUser) { - const error = new Error(`GroupId:${ctx.groupId} and userId:${user.id} already assigned to each other.`); - error.status = 400; - throw error; - } else { - return await GroupUser.create({ - group_id: ctx.groupId, - user_id: user.id - }); - } - } else { - const error = new Error('User account not found.'); - error.status = 400; - throw error; - } - }, - - async updateGroup(ctx) { - if (!ctx || !ctx.name || !ctx.description || !ctx.id || !ctx.userId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await Group.update({ - name: ctx.name, - description: ctx.description, - }, { - where: {id: ctx.id, user_id: ctx.userId} - }); - }, - - async updateGroupPolicy(ctx) { - if (!ctx || !ctx.groupId || !ctx.policyId || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await GroupsPolicy.update({ - group_id: ctx.groupId, - policy_id: ctx.policyId - }, {where: {id: ctx.id}}); - }, - - async updateGroupUser(ctx) { - if (!ctx || !ctx.kcId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const user = await userService.detail({ kcId: ctx.kcId }); - if (!user) { - const error = new Error("User account not found."); - error.status = 400; - throw error; - } - return await GroupUser.update({ - group_id: ctx.groupId, - user_id: user.id - }, {where: {id: ctx.id}}); - }, - - async deleteGroup(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await Group.destroy({ - where: { - id: ctx.id - } - }); - }, - - async deleteGroupPolicy(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await GroupsPolicy.destroy({ - where: { - id: ctx.id - } - }); - }, - - async deleteGroupUser(ctx) { - if (!ctx || !ctx.groupId || !ctx.userId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const params = { groupId: ctx.groupId, userId: ctx.userId }; - return db.raw(`delete from group_users where group_id = :groupId and user_id = :userId`, params); - }, - - async groups() { - return await Group.findAll({}); - }, - - async groupPolicies() { - return await GroupsPolicy.findAll({}); - }, - - async groupUsers(ctx) { - if (!ctx || !ctx.groupId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const params = { groupId: ctx.groupId }; - const groupUsers = await db.raw(` - select g.id as groupId, u.id as userId, u.username, g.name, g.description from group_users gu - inner join users u on u.id = gu.user_id - inner join groups g on gu.group_id = g.id - where g.id = :groupId - `, params); - return groupUsers.rows; - } +"use strict"; +const prisma = require("../../prisma/client"); + +async function createGroup(name, description, kc_id) { + if (!name || !description || !kc_id) { + throw Error("The request body is empty!"); + } + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); + + if (!user) { + throw Error("The user account could not be found."); + } + + const group = await prisma.group.findFirst({ + where: { + name: name, + }, + }); + if (group) { + throw Error(`A group named '${group.name}' already exists.`); + } + + const newGroup = await prisma.group.create({ + data: { + name: name, + description: description, + admin: { + connect: { + id: user.id, + }, + }, + }, + }); + + return newGroup; } +async function addPolicyToGroup(policyId, groupId) { + if (!groupId || !policyId) { + throw Error("The request body is empty!"); + } -module.exports = { - createGroup: GroupService.createGroup, - createGroupPolicy: GroupService.createGroupPolicy, - createGroupUser: GroupService.createGroupUser, + const group = await prisma.group.findFirst({ + where: { + id: +groupId, + }, + }); + + if (!group) { + throw Error("The group could not be found."); + } + + const policy = await prisma.policy.findFirst({ + where: { + id: +policyId, + }, + }); + + if (!policy) { + throw Error("The policy could not be found."); + } + + const groupPolicy = await prisma.group.findFirst({ + where: { + id: +groupId, + policies: { + some: { + policy_id: +policyId, + }, + }, + }, + }); + + if (groupPolicy) { + throw Error("The policy is already assigned to the group."); + } + + const newGroupPolicy = await prisma.groupPolicy.create({ + data: { + group_id: +groupId, + policy_id: +policyId, + }, + }); + + return newGroupPolicy; +} + +async function updateGroup(id, name, description) { + if (!name || !description || !id) { + throw Error("The request body is empty!"); + } + + const group = await prisma.group.findFirst({ + where: { + id: +id, + }, + }); + + if (!group) { + throw Error("The group could not be found."); + } + + const updated = await prisma.group.update({ + where: { + id: +id, + }, + data: { + name: name, + description: description, + }, + }); + + return updated; +} + +async function deleteGroup(id) { + if (!id) { + throw Error("The request body is empty!"); + } + const group = await prisma.group.findFirst({ + where: { + id: +id, + }, + }); + + if (!group) { + throw Error("The group could not be found."); + } + const deleted = await prisma.group.delete({ + where: { + id: +id, + }, + }); + + return deleted; +} + +async function removePolicyFromGroup(policyId, groupId) { + if (!groupId || !policyId) { + throw Error("The request body is empty!"); + } + + const policy = await prisma.policy.findFirst({ + where: { + id: +policyId, + }, + }); + + if (!policy) { + throw Error("The policy could not be found."); + } + + const group = await prisma.group.findFirst({ + where: { + id: +groupId, + }, + }); + + if (!group) { + throw Error("The group could not be found."); + } + + const groupPolicy = await prisma.groupPolicy.delete({ + where: { + group_id_policy_id: { + group_id: +groupId, + policy_id: +policyId, + }, + }, + }); - updateGroup: GroupService.updateGroup, - updateGroupPolicy: GroupService.updateGroupPolicy, - updateGroupUser: GroupService.updateGroupUser, + return groupPolicy; +} + +async function addUserToGroup(kc_id, groupId) { + if (!kc_id || !groupId) { + throw Error("The request body is empty!"); + } + + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); + if (!user) { + throw Error("The user account could not be found."); + } + + const group = await prisma.group.findFirst({ + where: { + id: +groupId, + }, + }); + + if (!group) { + throw Error( + "The group could not be found. Please ensure the group exists." + ); + } + const newGroupUser = await prisma.groupUser.create({ + data: { + group_id: +groupId, + user_id: user.id, + }, + }); + return newGroupUser; +} - deleteGroup: GroupService.deleteGroup, - deleteGroupPolicy: GroupService.deleteGroupPolicy, - deleteGroupUser: GroupService.deleteGroupUser, +async function removeUserFromGroup(kc_id, groupId) { + if (!groupId || !kc_id) { + throw Error("The request body is empty!"); + } - groups: GroupService.groups, - groupPolicies: GroupService.groupPolicies, - groupUsers: GroupService.groupUsers, + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); + if (!user) { + throw Error("The user account could not be found."); + } + + const group = await prisma.group.findFirst({ + where: { + id: +groupId, + }, + }); + + if (!group) { + throw Error("The group could not be found."); + } + + const groupUser = await prisma.groupUser.delete({ + where: { + group_id_user_id: { + group_id: +groupId, + user_id: user.id, + }, + }, + }); + + return groupUser; +} + +async function getGroup(id) { + if (!id) { + throw Error("The request body is empty!"); + } + return await prisma.group.findFirst({ + where: { + id: +id, + }, + include: { + users: { + include: { + user: true, + }, + }, + policies: { + include: { + policy: { + include: { + std_fields: { + include: { + std_field: true, + }, + }, + sources: { + include: { + source: true, + }, + }, + }, + }, + }, + }, + }, + }); +} + +async function getAllGroups() { + return await prisma.group.findMany({ + include: { + users: { + include: { + user: true, + }, + }, + policies: { + include: { + policy: true, + }, + }, + }, + }); +} + +module.exports = { + createGroup, + addPolicyToGroup, + addUserToGroup, + updateGroup, + deleteGroup, + removePolicyFromGroup, + removeUserFromGroup, + getAllGroups, + getGroup, }; diff --git a/app/dal/keycloakService.js b/app/dal/keycloakService.js new file mode 100644 index 0000000..d9922be --- /dev/null +++ b/app/dal/keycloakService.js @@ -0,0 +1,52 @@ +"use strict"; + +const KcAdminClient = require("keycloak-admin").default; + +const keycloakAdmin = new KcAdminClient({ + baseUrl: process.env.KEYCLOAK_BASE_URL, + realmName: "master", +}); + +async function auth() { + await keycloakAdmin.auth({ + username: process.env.KEYCLOAK_USER, + password: process.env.KEYCLOAK_PASSWORD, + grantType: "password", + clientId: "admin-cli", + }); +} + +async function getAllKeycloakUsers() { + try { + await auth(); + const users = await keycloakAdmin.users.find({ + realm: process.env.KEYCLOAK_REALM, + }); + + return users; + } catch (err) { + throw Error(err); + } +} + +async function deleteKeycloakUser({ kc_id }) { + try { + if (!kc_id) { + throw Error("The kc_id is missing!"); + } + + await auth(); + + return await keycloakAdmin.users.del({ + id: kc_id, + realm: process.env.KEYCLOAK_REALM, + }); + } catch (error) { + throw Error(error); + } +} + +module.exports = { + getAllKeycloakUsers, + deleteKeycloakUser, +}; \ No newline at end of file diff --git a/app/dal/mongoService.js b/app/dal/mongoService.js new file mode 100644 index 0000000..b022ea1 --- /dev/null +++ b/app/dal/mongoService.js @@ -0,0 +1,32 @@ +const { mongoClient, dbName } = require("@/app/config/mongo"); +const logger = require("../utils/format"); +const { ObjectId } = require("mongodb"); + +async function addSourceToMongoDB(metadata) { + await mongoClient.connect(); + const db = mongoClient.db(dbName); + + const result = await db.collection("metaurfm").insertOne(metadata); + return result; +} + +async function removeSourceFromMongoDB(id) { + logger.debug(`Removing source from mongo with id: ${id}`); + await mongoClient.connect(); + const db = mongoClient.db(dbName); + + const result = await db + .collection("metaurfm") + .deleteOne({ _id: new ObjectId(id) }); + if (result.deletedCount === 0) { + logger.error(`Source with id: ${id} not found`); + return false; + } + logger.debug(`Source with id: ${id} removed`); + return true; +} + +module.exports = { + addSourceToMongoDB, + removeSourceFromMongoDB, +}; diff --git a/app/dal/policyService.js b/app/dal/policyService.js index a101a67..788d6c3 100644 --- a/app/dal/policyService.js +++ b/app/dal/policyService.js @@ -1,280 +1,418 @@ -'use strict' - -const { Policy, PolicyField, PolicySource } = require('../models/Policy') -const db = require('../../db'); -const userService = require('./userService') - -const PolicyService = { - async createPolicy(ctx) { - if (!ctx || !ctx.kcId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const user = await userService.detail({ kcId: ctx.kcId }); - if (!user) { - const error = new Error("User account not found."); - error.status = 400; - throw error; - } - const [newPolicy, isCreated] = await Policy.findOrCreate( - { - where: { - name: ctx.name, - user_id: user.id, - }, - defaults: { - is_default: false - }, - }); - if (!isCreated) { - const error = new Error(`Policy:${ctx.name} already exists.`); - error.status = 400; - throw error; - } - return newPolicy; +"use strict"; + +const prisma = require("@/prisma/client"); + +async function createPolicy(name, kc_id) { + if (!name || !kc_id) { + throw Error("The request body is empty!"); + } + + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, }, + }); - async createPolicyField(ctx) { - if (!ctx || !ctx.policyId || !ctx.stdFieldId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const [newPolicyField, isCreated] = await PolicyField.findOrCreate( - { - where: { - policy_id: ctx.policyId, - std_field_id: ctx.stdFieldId, - }, - }); - if (!isCreated) { - const error = new Error("Policy and stdField already assigned to each other."); - error.status = 400; - throw error; - } - return newPolicyField; + if (!user) { + throw Error("The user account could not be found."); + } + + const policy = await prisma.policy.findFirst({ + where: { + name: name, }, + }); - async createPolicySource(ctx) { - if (!ctx || !ctx.policyId || !ctx.sourceId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const [newPolicySource, isCreated] = await PolicySource.findOrCreate( - { - where: { - policy_id: ctx.policyId, - source_id: ctx.sourceId, - }, - }); - if (!isCreated) { - const error = new Error("Policy and source already assigned to each other."); - error.status = 400; - throw error; - } - return newPolicySource; + if (policy) { + throw Error(`The policy named '${name} already inserted to database!`); + } + + const newPolicy = await prisma.policy.create({ + data: { + name: name, + user: { + connect: { + id: user.id, + }, + }, + is_default: false, }, + }); + + return newPolicy; +} + +async function addFieldToPolicy(stdFieldId, policyId) { + if (!policyId || !stdFieldId) { + throw Error("The request body is empty!"); + } + + const policy = await prisma.policy.findFirst({ + where: { + id: +policyId, + }, + }); + + if (!policy) { + throw Error( + `This policyId:${policyId} could not be found in the database!` + ); + } - async updatePolicySource(ctx) { - if (!ctx || !ctx.id || !ctx.sourceId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await PolicySource.update({ - source_id: ctx.sourceId, - }, { - where: { - id: ctx.id - } - }) + const stdField = await prisma.stdField.findFirst({ + where: { + id: +stdFieldId, }, + }); + if (!stdField) { + throw Error( + `This stdFieldId:${stdFieldId} could not be found in the database!` + ); + } - async deletePolicySource(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await PolicySource.destroy({ - where: { - id: ctx.id - } - }) + const stdFieldPolicy = await prisma.policy.findFirst({ + where: { + id: +policyId, + std_fields: { + some: { + std_field_id: +stdFieldId, + }, + }, }, + }); - async updatePolicy(ctx) { - if (!ctx || !ctx.id || !ctx.name || !ctx.sourceId || !ctx.isDefault) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await Policy.update({ - name: ctx.name, - source_id: ctx.sourceId, - is_default: ctx.isDefault - }, { - where: { - id: ctx.id, - } - }) + if (stdFieldPolicy) { + throw Error(`This stdFieldId:${stdFieldId} already exists in the policy!`); + } + + const newPolicyField = await prisma.policyStdField.create({ + data: { + policy: { + connect: { + id: +policyId, + }, + }, + std_field: { + connect: { + id: +stdFieldId, + }, + }, }, + }); + + return newPolicyField; +} - async updatePolicyField(ctx) { - if (!ctx || !ctx.id || !ctx.policyId || !ctx.stdFieldId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await PolicyField.update({ - policy_id: ctx.policyId, - std_field_id: ctx.stdFieldId, - }, { - where: { - id: ctx.id - } - }) +async function addSourceToPolicy(sourceId, policyId) { + if (!policyId || !sourceId) { + throw Error("The request body is empty!"); + } + + const policy = await prisma.policy.findFirst({ + where: { + id: +policyId, }, + }); + + if (!policy) { + throw Error( + `This policyId:${policyId} could not be found in the database!` + ); + } - async deletePolicyField(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await PolicyField.destroy({ - where: { - id: ctx.id - } - }); + const source = await prisma.source.findFirst({ + where: { + id: +sourceId, }, + }); + if (!source) { + throw Error( + `This sourceId:${sourceId} could not be found in the database!` + ); + } - async deletePolicy(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const policyField = await PolicyField.destroy({ - where: { - policy_id: ctx.id - } - }); - await Policy.destroy({ - where: { - id: ctx.id - } - }); - return policyField; + const sourcePolicy = await prisma.policy.findFirst({ + where: { + id: +policyId, + sources: { + some: { + source_id: +sourceId, + }, + }, }, + }); - async policies() { - return await Policy.findAll({}); + if (sourcePolicy) { + throw Error(`This sourceId:${sourceId} already exists in the policy!`); + } + + const newPolicySource = await prisma.policySource.create({ + data: { + policy_id: +policyId, + source_id: +sourceId, }, + }); + return newPolicySource; +} + +async function removeSourceFromPolicy(sourceId, policyId) { + if (!policyId || !sourceId) { + throw Error("The request body is empty!"); + } - async policiesByUser(ctx) { - if (!ctx || !ctx.kcId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - try { - const params = { userId: ctx.kcId }; - const policiesByUser = await db.raw(`select DISTINCT p.id, p.name, p.user_id, p.source_id, u.kc_id from policies p inner join users u on p.user_id = u.id where u.kc_id = :userId`, params); - return policiesByUser.rows; - } catch (error) { - throw new Error(error); - } + const policy = await prisma.policy.findFirst({ + where: { + id: +policyId, }, + }); + + if (!policy) { + throw Error( + `This policyId:${policyId} could not be found in the database!` + ); + } - async policyFields() { - return await PolicyField.findAll({}); + const source = await prisma.source.findFirst({ + where: { + id: +sourceId, }, + }); - async getAssignedPolicies(ctx) { - if (!ctx || !ctx.policyId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const params = { policyId: ctx.policyId }; - const assignedPolicies = await db.raw(` - select pf.id, sf.field_name, sf.definition_and_comment, sf.field_type, sf.values from policies p - inner join policy_fields pf on p.id = pf.policy_id - inner join std_fields sf on pf.std_field_id = sf.id - inner join policy_sources ps on p.id = ps.policy_id - inner join sources s on ps.source_id = s.id - where p.id = :policyId - `, params); - return assignedPolicies.rows; + if (!source) { + throw Error( + `This sourceId:${sourceId} could not be found in the database!` + ); + } + + const sourcePolicy = await prisma.policy.findFirst({ + where: { + id: +policyId, + sources: { + some: { + source_id: +sourceId, + }, + }, }, + }); + + if (!sourcePolicy) { + throw Error(`This sourceId:${sourceId} does not exist in the policy!`); + } - async getPoliciesWithSources() { - const policiesWIthSource = await db.raw(` - select p.id, p.name as policyName, s.name as sourceName from policies p - left join policy_sources ps on p.id = ps.policy_id - left join sources s on ps.source_id = s.id - `) - return policiesWIthSource.rows + const deletedPolicySource = await prisma.policySource.delete({ + where: { + policy_id_source_id: { + policy_id: +policyId, + source_id: +sourceId, + }, }, + }); + + return deletedPolicySource; +} + +async function updatePolicy(id, name, isDefault) { + if (!id || !name || !isDefault) { + throw Error("The request body is empty!"); + } - async getPoliciesWithSourcesByUser(ctx) { - if (!ctx || !ctx.kcId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - try { - const params = { userId: ctx.kcId }; - const policiesByUser = await db.raw(` - select p.id, p.name as policyName, s.name as sourceName, p.user_id, p.source_id, u.kc_id from policies p - inner join users u on p.user_id = u.id - left join policy_sources ps on p.id = ps.policy_id - left join sources s on ps.source_id = s.id - where u.kc_id = :userId`, params) - return policiesByUser.rows; - } catch (error) { - throw new Error(error); - } + const policy = await prisma.policy.update({ + where: { + id: id, }, + data: { + name: name, + is_default: isDefault, + }, + }); - async getGroupDetailsByPolicy(ctx) { - if (!ctx || !ctx.policyId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const params = { policyId: ctx.policyId }; - const groupDetailsByPolicy = await db.raw(` - select p.id as policyId, g.id as groupId, gp.id groupPolicyId, g.name as group_name, g.description as group_description, s.name as source_name, s.description as source_description from policies p - inner join groups_policies gp on p.id = gp.policy_id - inner join groups g on gp.group_id = g.id - left join policy_sources ps on p.id = ps.policy_id - left join sources s on ps.source_id = s.id - where p.id = :policyId - `, params); - return groupDetailsByPolicy.rows; - } -}; + return policy; +} + +async function removeFieldFromPolicy(stdFieldId, policyId) { + if (!policyId || !stdFieldId) { + throw Error("The request body is empty!"); + } + + const policy = await prisma.policy.findFirst({ + where: { + id: +policyId, + }, + }); + + if (!policy) { + throw Error( + `This policyId:${policyId} could not be found in the database!` + ); + } + + const stdField = await prisma.stdField.findFirst({ + where: { + id: +stdFieldId, + }, + }); + + if (!stdField) { + throw Error( + `This stdFieldId:${stdFieldId} could not be found in the database!` + ); + } + + const stdFieldPolicy = await prisma.policy.findFirst({ + where: { + id: +policyId, + std_fields: { + some: { + std_field_id: +stdFieldId, + }, + }, + }, + }); + + if (!stdFieldPolicy) { + throw Error(`This stdFieldId:${stdFieldId} does not exist in the policy!`); + } + + const deletedPolicyField = await prisma.policyStdField.delete({ + where: { + policy_id_std_field_id: { + policy_id: +policyId, + std_field_id: +stdFieldId, + }, + }, + }); + + return deletedPolicyField; +} + +async function deletePolicy(id) { + if (!id) { + throw Error("The request body is empty!"); + } + + const policy = await prisma.policy.findFirst({ + where: { + id: +id, + }, + }); + + if (!policy) { + throw Error(`This policyId:${id} could + not be found in the database!`); + } + + const deletedPolicy = await prisma.policy.delete({ + where: { + id: +id, + }, + }); + + return deletedPolicy; +} + +async function getAllPolicies() { + return await prisma.policy.findMany({ + include: { + std_fields: { + include: { + std_field: true, + }, + }, + sources: { + include: { + source: true, + }, + }, + groups: { + include: { + group: { + include: { + policies: { + include: { + policy: { + include: { + std_fields: { + include: { + std_field: true, + }, + }, + sources: { + include: { + source: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }); +} + +async function getPolicy(policyId) { + if (!policyId) { + throw Error("The request body is empty!"); + } + + const assignedPolicies = await prisma.policy.findFirst({ + where: { + id: policyId, + }, + include: { + std_fields: { + include: { + std_field: true, + }, + }, + sources: { + include: { + source: true, + }, + }, + groups: { + include: { + group: { + include: { + policies: { + include: { + policy: { + include: { + std_fields: { + include: { + std_field: true, + }, + }, + sources: { + include: { + source: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }); + + return assignedPolicies; +} module.exports = { - createPolicy: PolicyService.createPolicy, - createPolicyField: PolicyService.createPolicyField, - createPolicySource: PolicyService.createPolicySource, - - updatePolicy: PolicyService.updatePolicy, - updatePolicyField: PolicyService.updatePolicyField, - - deletePolicyField: PolicyService.deletePolicyField, - deletePolicy: PolicyService.deletePolicy, - - policies: PolicyService.policies, - policiesByUser: PolicyService.policiesByUser, - policyFields: PolicyService.policyFields, - getAssignedPolicies: PolicyService.getAssignedPolicies, - getPoliciesWithSources: PolicyService.getPoliciesWithSources, - getPoliciesWithSourcesByUser: PolicyService.getPoliciesWithSourcesByUser, - getGroupDetailsByPolicy: PolicyService.getGroupDetailsByPolicy, + createPolicy, + addFieldToPolicy, + addSourceToPolicy, + updatePolicy, + removeFieldFromPolicy, + removeSourceFromPolicy, + deletePolicy, + getAllPolicies, + getPolicy, }; diff --git a/app/dal/realmService.js b/app/dal/realmService.js deleted file mode 100644 index c311b95..0000000 --- a/app/dal/realmService.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict' - -const db = require('../../db'); - -const RealmService = { - async create(ctx) { - return await db('realm').insert({ - name: ctx.name - }).returning('*').then((result) => result[0]) - }, - - async update(ctx) { - return db('realm').where('id', ctx.id).update({ - name: ctx.name - }); - }, - - async delete(ctx) { - return db.delete().from('realm').where('id', ctx.id).timeout(1000, {cancel: true}); - }, - - async realm(ctx) { - return db.select().from('realm').where('id', ctx.id).timeout(1000, {cancel: true}); - }, - - async realms() { - return db.select().from('realm').timeout(1000, {cancel: true}); - } -} - -module.exports = { - create: RealmService.create, - update: RealmService.update, - delete: RealmService.delete, - realm: RealmService.realm, - realms: RealmService.realms -} diff --git a/app/dal/roleAllocationService.js b/app/dal/roleAllocationService.js deleted file mode 100644 index 09af151..0000000 --- a/app/dal/roleAllocationService.js +++ /dev/null @@ -1,77 +0,0 @@ -'use strict' - -const db = require('../../db'); -const { RoleUser } = require('../models/Role'); - -const RoleAllocationService = { - // Allocates a role to a user. If a role is already allocated to this user, updates it. - async create(ctx) { - if (!ctx || !ctx.kc_id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const [roleUser, created] = await RoleUser.findOrCreate({ - where: { kc_id: ctx.kc_id }, - defaults: { role_id: ctx.role_id } - }); - if (!created) { - return await RoleUser.update({ role_id: ctx.role_id, }, { - where: { - kc_id: ctx.kc_id - } - }); - } - return roleUser; - }, - - // Updates roles allocated to a user. - async update(ctx) { - if (!ctx || !ctx.kc_id || !ctx.role_id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await RoleUser.update({ role_id: ctx.role_id, }, { - where: { - kc_id: ctx.kc_id - } - }); - }, - - // Deletes roles allocated to a user. - async delete(ctx) { - if (!ctx || !ctx.kc_id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await RoleUser.destroy({ - where: { - kc_id: ctx.kc_id - } - }); - }, - - // Get allocated roles by user kc_id. - async allocatedRolesByKcId(ctx) { - if (!ctx || !ctx.kc_id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const allocatedRoles = await db.raw(` - select roles.id, u.username as user, roles.name as role , roles.description from roles - inner join roles_users ru on roles.id = ru.role_id - inner join users u on ru.kc_id = u.kc_id - where u.kc_id = ?`, [ctx.kc_id]); - return allocatedRoles.rows; - }, -}; - -module.exports = { - create: RoleAllocationService.create, - allocatedRoles: RoleAllocationService.allocatedRolesByKcId, - update: RoleAllocationService.update, - delete: RoleAllocationService.delete -}; diff --git a/app/dal/roleService.js b/app/dal/roleService.js index 8589381..4ef72ed 100644 --- a/app/dal/roleService.js +++ b/app/dal/roleService.js @@ -1,69 +1,223 @@ -"use strict" - -const db = require('../../db'); -const { Role, RoleUser } = require('../models/Role'); - -const RoleService = { - // Get all roles - async roles() { - return await Role.findAll(); - }, - - // Get a role - async role(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await Role.findAll({where: {'id': ctx.id}}); - }, - - - // Update role by id - async update(ctx) { - if (!ctx || !ctx.id || !ctx.name || !ctx.description) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await Role.update({ - 'name': ctx.name, - 'description': ctx.description - }, { - where: {'id': ctx.id} - }); - }, - - // Create a role - async create(ctx) { - return await db('roles').insert({ - name: ctx.name, - description: ctx.description - }).returning('*').then((result) => result[0]); - }, - - - // Delete role - async delete(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - await RoleUser.destroy({ - where: { - role_id: ctx.id - } - }); - return await Role.destroy({where: {'id': ctx.id}}); +"use strict"; + +const prisma = require("@/prisma/client"); + +async function createRole(name, description) { + if (!name || !description) { + throw Error("The request body is empty!"); + } + + const role = await prisma.role.findFirst({ + where: { + name: name, + }, + }); + + if (role) { + throw Error(`This Role name:${name} already inserted to database!`); + } else { + const newRole = await prisma.role.create({ + data: { + name: name, + description: description, + }, + }); + + return newRole; + } +} + +async function getRoles() { + return await prisma.role.findMany({ + include: { + users: { + include: { + user: true, + }, + }, + }, + }); +} + +async function getRole(id) { + if (!id) { + throw Error("The request body is empty!"); + } + const role = await prisma.role.findFirst({ + where: { + id: +id, + }, + include: { + users: { + include: { + user: true, + }, + }, + }, + }); + return role; +} + +async function getUserRoles(kc_id) { + if (!kc_id) { + throw Error("The request body is empty!"); + } + + const user = await prisma.role.findMany({ + where: { + users: { + some: { + kc_id: kc_id, + }, + }, + }, + }); + + return user.roles; +} + +async function deleteRole(id) { + if (!id) { + throw Error("The request body is empty!"); + } + + const role = await prisma.role.findFirst({ + where: { + id: +id, }, + }); + + if (!role) { + throw Error("The role could not be found."); + } + + const deletedRole = await prisma.role.delete({ + where: { + id: +id, + }, + }); + + return deletedRole; +} + +async function updateRole(id, name, description) { + if (!id || !name || !description) { + throw Error("The request body is empty!"); + } + + const role = await prisma.role.findFirst({ + where: { + id: +id, + }, + }); + + if (!role) { + throw Error("The role could not be found."); + } + + const updatedRole = await prisma.role.update({ + where: { + id: +id, + }, + data: { + name: name, + description: description, + }, + }); + + return updatedRole; +} + +async function addUserToRole(kc_id, role_id) { + if (!kc_id || !role_id) { + throw Error("The request body is empty!"); + } + + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); + + if (!user) { + throw Error("The user account could not be found."); + } + + const role = await prisma.role.findFirst({ + where: { + id: +role_id, + }, + }); + + if (!role) { + throw Error("The role could not be found."); + } + + const userRole = await prisma.roleUser.findFirst({ + where: { + user_id: user.id, + role_id: role.id, + }, + }); + + if (userRole) { + throw Error("The user is already assigned to this role."); + } + + const updatedRole = await prisma.roleUser.create({ + data: { + role_id: role.id, + user_id: user.id, + }, + }); + + return updatedRole; +} + +async function removeUserFromRole(kc_id, role_id) { + if (!kc_id || !role_id) { + throw Error("The request body is empty!"); + } + + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); + + if (!user) { + throw Error("The user account could not be found."); + } + + const role = await prisma.role.findFirst({ + where: { + id: +role_id, + }, + }); + + if (!role) { + throw Error("The role could not be found."); + } + + const updatedRole = await prisma.roleUser.delete({ + where: { + role_id_user_id: { + role_id: role.id, + user_id: user.id, + }, + }, + }); + + return updatedRole; } module.exports = { - create: RoleService.create, - roles: RoleService.roles, - role: RoleService.role, - delete: RoleService.delete, - update: RoleService.update, + createRole, + getRoles, + getRole, + getUserRoles, + deleteRole, + updateRole, + addUserToRole, + removeUserFromRole, }; diff --git a/app/dal/searchHistoryService.js b/app/dal/searchHistoryService.js index 1671d4d..261d58d 100644 --- a/app/dal/searchHistoryService.js +++ b/app/dal/searchHistoryService.js @@ -1,69 +1,117 @@ -'use strict' - -const userService = require("./userService"); -const { SearchHistory } = require('../models/UserHistory'); - -const SearchHistoryService = { - async create(ctx) { - if (!ctx || !ctx.kcId || !ctx.query || !ctx.name || !ctx.uiStructure) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const user = await userService.detail({ kcId: ctx.kcId }); - if (!user) { - const error = new Error("User account not found."); - error.status = 400; - throw error; - } - return await SearchHistory.create({ - kc_id: user.kc_id, - query: ctx.query, - name: ctx.name, - ui_structure: ctx.uiStructure, - description: ctx.description - }) +"use strict"; + +const prisma = require("../../prisma/client"); + +async function addSearchHistoryToUser( + kc_id, + query, + name, + ui_structure, + description +) { + if (!query || !name || !ui_structure || !description) { + throw Error("The request body is empty!"); + } + + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, }, + }); + if (!user) { + throw Error("The user account could not be found."); + } - async fetchUserSearchHistory(ctx) { - if (!ctx || !ctx.kcId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await SearchHistory.findAll({ - where: { - kc_id: ctx.kcId - } - }); + const result = prisma.user.update({ + where: { + kc_id: kc_id, + }, + data: { + history: { + create: { + query: query, + name: name, + ui_structure: ui_structure, + description: description, + }, + }, + }, + include: { + history: true, }, + }); - async update(ctx) { - if (!ctx || !ctx.kcId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - // Not finished ? I don't know why there is no method call to update db. + return result; +} + +async function getSearchHistoryByUser(kc_id) { + if (!kc_id) { + throw Error("The request body is empty!"); + } + + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); + if (!user) { + throw Error("The user account could not be found."); + } + const result = await prisma.searchHistory.findMany({ + where: { + user_id: user.id, }, + }); - async delete(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await SearchHistory.destroy({ - where: { - id: ctx.id - } - }); - } -}; + return result; +} + +async function deleteHistory(id) { + if (!id) { + throw Error("The request body is empty!"); + } + + const history = await prisma.searchHistory.findFirst({ + where: { + id: +id, + }, + }); + + if (!history) { + throw Error("The history could not be found."); + } + + return await prisma.searchHistory.delete({ + where: { + id: +id, + }, + }); +} + +async function deleteAllHistoryByUser(kc_id) { + if (!kc_id) { + throw Error("The request body is empty!"); + } + + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); + if (!user) { + throw Error("The user account could not be found."); + } + + return await prisma.searchHistory.deleteMany({ + where: { + user_id: user.id, + }, + }); +} module.exports = { - create: SearchHistoryService.create, - update: SearchHistoryService.update, - delete: SearchHistoryService.delete, - fetch: SearchHistoryService.fetchUserSearchHistory + addSearchHistoryToUser, + deleteHistory, + getSearchHistoryByUser, + deleteAllHistoryByUser, }; diff --git a/app/dal/sourceService.js b/app/dal/sourceService.js new file mode 100644 index 0000000..b238810 --- /dev/null +++ b/app/dal/sourceService.js @@ -0,0 +1,295 @@ +"use strict"; +const prisma = require("@/prisma/client"); +const { + addSourceToMongoDB, + removeSourceFromMongoDB, +} = require("@/app/dal/mongoService"); +const { + prepareForBulk, + bulk, + countByIndex, + deleteIndex, +} = require("@/app/dal/elasticService"); +const logger = require("@/app/utils/format"); + +async function getSources() { + return await prisma.source.findMany({ + include: { + source_indices: true, + providers: { + include: { + user: true, + }, + }, + }, + }); +} + +async function getSource(id) { + if (!id) { + throw new Error("Missing required parameter: id"); + } + + return await prisma.source.findUnique({ + where: { + id, + }, + include: { + source_indices: true, + providers: { + include: { + user: true, + }, + }, + }, + }); +} + +async function getSourcesByProvider(kc_id) { + if (!kc_id) { + throw new Error("Missing required parameter: kc_id"); + } + + return await prisma.source.findMany({ + where: { + providers: { + some: { + user: { + kc_id, + }, + }, + }, + }, + include: { + source_indices: true, + providers: { + include: { + user: true, + }, + }, + }, + }); +} + +async function getIndexedSourcesByProvider(kc_id) { + if (!kc_id) { + throw new Error("Missing required parameter: kc_id"); + } + + return await prisma.source.findMany({ + where: { + providers: { + some: { + user: { + kc_id, + }, + }, + }, + source_indices: { + some: {}, + }, + }, + include: { + source_indices: true, + providers: { + include: { + user: true, + }, + }, + }, + }); +} + +async function getIndexedSources() { + return await prisma.source.findMany({ + where: { + source_indices: { + some: {}, + }, + }, + include: { + source_indices: true, + providers: { + include: { + user: true, + }, + }, + }, + }); +} + +async function createSource(name, description, metaUrfms, kc_id) { + if (!name) { + throw new Error("Missing required parameter: name"); + } + + if (!description) { + throw new Error("Missing required parameter: description"); + } + + if (!kc_id) { + throw new Error("Missing required parameter: kc_id"); + } + + const user = await prisma.user.findUnique({ + where: { + kc_id, + }, + }); + + if (!user) { + throw new Error("User not found"); + } + + const existingSource = await prisma.source.findFirst({ + where: { + name, + }, + }); + + if (existingSource) { + throw new Error("Source already exists"); + } + + if (!metaUrfms) { + throw new Error("Missing required parameter: metaUrfms"); + } + + const metadata = { + name, + description, + metaUrfms, + }; + + const result = await addSourceToMongoDB(metadata); + + if (!result) { + throw new Error("Failed to create source"); + } + const { insertedId } = result; + + logger.debug(`Inserted source with id: ${insertedId}`); + const elkIndex = `${name.split(/\s/).join("").toLowerCase()}-${insertedId}`; + logger.debug(`Indexing documents with index: ${elkIndex}`); + + const body = await prepareForBulk(metaUrfms, elkIndex); + try { + const success = await bulk(body); + if (!success) { + throw new Error("Failed to index documents"); + } + + const newSource = await prisma.source.create({ + data: { + name, + description, + source_indices: { + create: { + mng_id: insertedId, + index_id: elkIndex, + is_send: true, + }, + }, + }, + include: { + source_indices: true, + providers: { + include: { + user: true, + }, + }, + }, + }); + + logger.debug(`Created source with id: ${newSource.id}`); + + await prisma.sourceUser.create({ + data: { + source_id: newSource.id, + user_id: user.id, + }, + }); + + logger.debug( + `Assigned source with id: ${newSource.id} to provider with id: ${user.id}` + ); + + return newSource; + } catch (error) { + logger.error("Error while inserting in elastic", error); + await removeSourceFromMongoDB(insertedId); + await deleteIndex(elkIndex); + const sourceToDelete = await prisma.source.findUnique({ + where: { + source_indices: { + some: { + mng_id: insertedId, + }, + }, + }, + }); + await prisma.source.deleteMany({ + where: { + id: sourceToDelete.id, + }, + }); + await prisma.sourceUser.deleteMany({ + where: { + source_id: sourceToDelete.id, + }, + }); + throw new Error("Failed to create source"); + } +} + +async function deleteSource(id) { + if (!id) { + throw new Error("Missing required parameter: id"); + } + id = +id; + logger.debug(`Deleting source with id: ${id}`); + const source = await prisma.source.findUnique({ + where: { + id, + }, + include: { + source_indices: true, + }, + }); + + if (!source) { + logger.error(`Source with id: ${id} not found`); + throw new Error("Source not found"); + } + logger.debug(`Found source with id: ${id}`); + const source_indices = source.source_indices.length; + if (source_indices > 0) { + const { mng_id, index_id } = source.source_indices[0]; + + if (mng_id) { + await removeSourceFromMongoDB(mng_id); + } + if (index_id) { + await deleteIndex(index_id); + } + } + + await prisma.source.delete({ + where: { + id, + }, + }); + + logger.debug(`Deleted source with id: ${id}`); + return true; +} + +module.exports = { + getSources, + getSource, + getSourcesByProvider, + getIndexedSourcesByProvider, + createSource, + deleteSource, + getIndexedSources, +}; diff --git a/app/dal/stdFieldService.js b/app/dal/stdFieldService.js new file mode 100644 index 0000000..c9d0b09 --- /dev/null +++ b/app/dal/stdFieldService.js @@ -0,0 +1,166 @@ +"use strict"; +const prisma = require("../../prisma/client"); +const logger = require("@/app/utils/format"); + +async function getStdFields() { + const stdFields = await prisma.stdField.findMany({ + include: { + policies: { + include: { + policy: { + include: { + sources: { + include: { + source: true, + }, + }, + }, + }, + }, + }, + }, + }); + return stdFields; +} + +async function getPublicStdFields() { + const stdFields = await prisma.stdField.findMany({ + where: { + ispublic: true, + }, + include: { + policies: { + include: { + policy: { + include: { + sources: { + include: { + source: true, + }, + }, + }, + }, + }, + }, + }, + }); + return stdFields; +} + +async function getStdField(id) { + if (!id) { + throw Error("The request body is empty!"); + } + + const stdField = await prisma.stdField.findUnique({ + where: { + id: +id, + }, + }); + + if (!stdField) { + throw Error( + `The stdField with id ${id} could not be found in the database!` + ); + } + + return stdField; +} + +async function createOrUpdateStdField({ + id, + category, + field_name, + definition_and_comment, + obligation_or_condition, + cardinality, + field_type, + values, + ispublic, + isoptional, + list_url, default_display_fields + }) { + if (!field_name) { + throw Error("The request body is empty!"); + } + + logger.debug(default_display_fields); + // Transform string to bool + const is_default_display_fields = default_display_fields === "1" ? true : false; + + if (id) { + const stdField = await prisma.stdField.findFirst({ + where: { + id: +id, + }, + }); + + if (!stdField) { + throw Error(`The stdField with id '${id} doesn't exist`); + } + + if (!typeof ispublic === "boolean" || !typeof isoptional === "boolean") { + throw Error("ispublic and isoptional must be boolean"); + } + + return await prisma.stdField.update({ + where: { + id: +id, + }, + update: { + category, + definition_and_comment, + obligation_or_condition, + cardinality, + field_type, + values, + ispublic, + isoptional, + list_url, + default_display_fields: is_default_display_fields + }, + }); + } else { + // Need to create the std_field + const stdField = await prisma.stdField.findFirst({ + where: { + field_name: field_name, + }, + }); + + if (stdField) { + throw Error( + `The stdField named '${field_name} already inserted to database!` + ); + } + + const newStdField = await prisma.stdField.create({ + data: { + category, + field_name, + definition_and_comment, + obligation_or_condition, + cardinality, + field_type, + values, + ispublic, + isoptional, + list_url, + default_display_fields: is_default_display_fields + }, + }); + return newStdField; + } +} + +async function deleteAllStdFields() { + return await prisma.stdField.deleteMany(); +} + +module.exports = { + getStdField, + getStdFields, + getPublicStdFields, + createOrUpdateStdField, + deleteAllStdFields, +}; diff --git a/app/dal/userDisplayFieldsService.js b/app/dal/userDisplayFieldsService.js new file mode 100644 index 0000000..a3ad2b6 --- /dev/null +++ b/app/dal/userDisplayFieldsService.js @@ -0,0 +1,66 @@ +"use strict"; + +const prisma = require("@/prisma/client"); +const logger = require("../utils/format"); + +async function getUserDisplayFields(kc_id) { + return await prisma.stdField.findMany({ + where: { + display_fields: { + some: { + user: { + kc_id: kc_id, + }, + }, + }, + }, + }); +} + +async function setUserDisplayFields(kc_id, fields_ids) { + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); + + if (!user) { + throw new Error("User not found"); + } + + let ids = []; + // Reset the user display fields + try { + await prisma.displayStdField.deleteMany({ + where: { + user_id: user.id, + }, + }); + } catch (err) { + logger.error("Error while resetting user display fields", { + error: err.message, + }); + } + for (const field_id of fields_ids) { + try { + const id = await prisma.displayStdField.create({ + data: { + user_id: user.id, + std_field_id: +field_id, + }, + }); + ids.push(id); + } catch (err) { + logger.error("Error while adding user display field", { + error: err.message, + }); + } + } + return ids; +} + +module.exports = { + getUserDisplayFields, + setUserDisplayFields, + setUserDisplayFields, +}; diff --git a/app/dal/userFieldsDisplaySettingsService.js b/app/dal/userFieldsDisplaySettingsService.js deleted file mode 100644 index 48af77e..0000000 --- a/app/dal/userFieldsDisplaySettingsService.js +++ /dev/null @@ -1,117 +0,0 @@ -"use strict" - -const {UserFieldsDisplaySettings} = require("../models/UserFieldsDisplaySettings"); - -/* - userFieldsDisplaySettingsService - Manage all users fields display settings - Used in search tool for results table columns -*/ -const userFieldsDisplaySettingsService = { - - // POST create fields settings for a specific user - // userId: id of the user - // stdFieldsIds: array of ids of fields selected by user - async create(ctx) { - if (!ctx || !ctx.userId || !ctx.stdFieldsIds) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - // Check if all fields ids exists - if (!await UserFieldsDisplaySettings.validateStdFieldIds(ctx.stdFieldsIds)) { - const error = new Error("One or more std_field_id do not exists."); - error.status = 400; - throw error; - } - // Insert new row in user settings table - const [newUserFieldsSettings, isCreated] = await UserFieldsDisplaySettings.findOrCreate({ - where: { user_id: ctx.userId }, // Unique field(s) to check for existence - defaults: { - std_fields_ids: ctx.stdFieldsIds, - }, - }); - if (!isCreated) { - const error = new Error("User fields display settings already exists."); - error.status = 400; - throw error; - } - return { newUserFieldsSettings, status: 201 }; - }, - - // GET all users fields settings - async usersFieldsDisplaySettings() { - return await UserFieldsDisplaySettings.findAll({}); - }, - - // GET single user settings - // userId: id of the user - async userFieldsDisplaySettings(ctx) { - if (!ctx || !ctx.userId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const result = await UserFieldsDisplaySettings.findOne({ where: { user_id: ctx.userId } }); - if (!result) { - const error = new Error("No settings found for this userId."); - error.status = 404; - throw error; - } - return result; - }, - - // PUT update a user settings - // userId: id of the user - // stdFieldsIds: new array of all fields ids - async update(ctx) { - if (!ctx || !ctx.userId || !ctx.stdFieldsIds) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - // Check if all fields ids exists - if (!await UserFieldsDisplaySettings.validateStdFieldIds(ctx.stdFieldsIds)) { - const error = new Error("One or more std_field_id do not exists."); - error.status = 400; - throw error; - } - const [updatedCount, updatedRows] = await UserFieldsDisplaySettings.update({ - std_fields_ids: ctx.stdFieldsIds, - }, { where: { user_id: ctx.userId }}); - if (updatedCount === 0) { - const error = new Error("No settings found for this userId."); - error.status = 404; - throw error; - } - return updatedRows ? updatedRows[0] : { message: 'User fields display settings updated.'}; - }, - - // DELETE the user settings - async delete(ctx) { - if (!ctx || !ctx.userId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const deletedCount = await UserFieldsDisplaySettings.destroy({ - where: { - user_id: ctx.userId - } - }); - if (deletedCount === 0) { - const error = new Error("No settings found for this userId."); - error.status = 404; - throw error; - } - return {message: 'User fields display settings deleted.'}; - }, -} - -module.exports = { - create: userFieldsDisplaySettingsService.create, - usersFieldsDisplaySettings: userFieldsDisplaySettingsService.usersFieldsDisplaySettings, - userFieldsDisplaySettings: userFieldsDisplaySettingsService.userFieldsDisplaySettings, - delete: userFieldsDisplaySettingsService.delete, - update: userFieldsDisplaySettingsService.update, -}; diff --git a/app/dal/userRequestService.js b/app/dal/userRequestService.js index d0c0c10..e3a3b77 100644 --- a/app/dal/userRequestService.js +++ b/app/dal/userRequestService.js @@ -1,88 +1,145 @@ -'use strict' - -const userService = require("./userService"); -const { UserRequest } = require('../models/UserRequest'); - -const UserRequestService = { - async create(ctx) { - if (!ctx || !ctx.kcId || !ctx.message) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const user = await userService.detail({ kcId: ctx.kcId }); - if (!user) { - const error = new Error("User account not found."); - error.status = 400; - throw error; - } - return await UserRequest.create({ - user_id: user.id, - request_message: ctx.message, - is_processed: false, - }); +"use strict"; + +const prisma = require("@/prisma/client"); + +async function createRequest(kc_id, message) { + if (!kc_id || !message) { + throw Error("The request body is empty!"); + } + + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, }, + }); - async getAllRequests() { - return await UserRequest.findAll({}); + if (!user) { + throw Error("The user account could not found."); + } + + const result = await prisma.userRequest.create({ + data: { + user: { + connect: { + id: user.id, + }, + }, + request_message: message, + is_processed: false, }, + }); + return result; +} - async getUserRequests(ctx) { - if (!ctx || !ctx.kcId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const user = await userService.detail({ kcId: ctx.kcId }) - if (user) { - return await UserRequest.findAll({ - where: { - user_id: user.id - } - }); - } +async function getAllRequests() { + return await prisma.userRequest.findMany(); +} + +async function getAllPendingRequests() { + const requests = await prisma.userRequest.findMany({ + where: { + is_processed: false, }, + }); + + return requests; +} - async getPendingRequests() { - return await UserRequest.findAll({ - where: { - is_processed: false - } - }); +async function updateRequest(id, isProcessed) { + if (!id || isProcessed === undefined) { + throw Error("The request body is empty!"); + } + + const request = await prisma.userRequest.findFirst({ + where: { + id: +id, }, + }); + if (!request) { + throw Error("The request could not found."); + } - async process(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await UserRequest.update({ - is_processed: true, - }, { - where: {id: ctx.id} - }); + const result = await prisma.userRequest.update({ + where: { + id: +id, + }, + data: { + is_processed: isProcessed, }, + }); - async delete(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await UserRequest.destroy({ - where: { - id: ctx.id - } - }); - } + return result; } -module.exports = { - create: UserRequestService.create, - fetch: UserRequestService.getAllRequests, - fetchPending: UserRequestService.getPendingRequests, - fetchByUser: UserRequestService.getUserRequests, - process: UserRequestService.process, - delete: UserRequestService.delete, +async function getUserRequests(kc_id) { + if (!kc_id) { + throw Error("The request body is empty!"); + } + + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); + + if (!user) { + throw Error("The user account could not found."); + } + + const requests = await prisma.userRequest.findMany({ + where: { + user_id: user.id, + }, + }); + + return requests; +} + +async function getPendingRequests(kc_id) { + if (!kc_id) { + throw Error("The request body is empty!"); + } + + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); + + if (!user) { + throw Error("The user account could not found."); + } + + const requests = await prisma.userRequest.findMany({ + where: { + user_id: user.id, + is_processed: false, + }, + }); + + return requests; +} + +async function deleteRequest(id) { + if (!id) { + throw Error("The request body is empty!"); + } + + const result = await prisma.userRequest.delete({ + where: { + id: +id, + }, + }); + + return result; } + +module.exports = { + createRequest, + getAllRequests, + getUserRequests, + getAllPendingRequests, + getPendingRequests, + updateRequest, + deleteRequest, +}; diff --git a/app/dal/userService.js b/app/dal/userService.js index 3ffe89e..fdcd57e 100644 --- a/app/dal/userService.js +++ b/app/dal/userService.js @@ -1,394 +1,126 @@ -"use strict" +"use strict"; -const KcAdminClient = require("keycloak-admin").default; -const db = require('../../db'); -const { User } = require('../models/User'); -const { RoleUser } = require('../models/Role'); -const { postgresSeq } = require('../config/db'); -const { BotService, MailService } = require('@in-sylva/common'); -const { Op } = require("sequelize"); -const bcrypt = require('bcrypt'); +const prisma = require("../../prisma/client"); +const { deleteKeycloakUser } = require("../dal/keycloakService"); -const mailService = new MailService(); -mailService.auth_user = process.env.IN_SYLVA_EMAIL; -mailService.auth_pass = process.env.IN_SYLVA_EMAIL_PASSWORD; -mailService.smtp_host = process.env.IN_SYLVA_SMTP_HOST; -mailService.smtp_port = process.env.IN_SYLVA_SMTP_PORT; -mailService.smtp_secure = true; - -const botService = new BotService(); -botService.token = process.env.BOT_SERVICE_TOKEN; -botService.channel = process.env.BOT_SERVICE_CHANNEL; - -const keycloakAdmin = new KcAdminClient({ - baseUrl: `${process.env.IN_SYLVA_KEYCLOAK_HOST}:${process.env.IN_SYLVA_KEYCLOAK_PORT}/keycloak/auth`, - realmName: "master" -}); - -const auth = async () => { - await keycloakAdmin.auth({ - username: process.env.KEYCLOAK_USERNAME, - password: process.env.KEYCLOAK_PASSWORD, - grantType: process.env.KEYCLOAK_GRANT_TYPE, - clientId: process.env.KEYCLOAK_CLIENT_ID, +async function getUsers() { + return await prisma.user.findMany({ + include: { + roles: { + include: { + role: true, + }, + }, + groups: { + include: { + group: true, + }, + }, + group_admin: true, + }, }); } -const generatePassword = async (length) => { - const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", retVal = []; - for (let i = 0, n = charset.length; i < length; ++i) { - retVal.push(charset.charAt(Math.floor(Math.random() * n))); +async function getUser(kc_id) { + if (!kc_id) { + return null; } - const password = retVal.join(""); - const salt = bcrypt.genSaltSync(); - return bcrypt.hashSync(password, salt); -} -const UserService = { - // POST Create a user account - async create(ctx) { - if (!ctx || !ctx.username || !ctx.email || !ctx.password) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - // Check roleId or assign to default role "normal-user" - const roleId = ctx.roleId || 3; - // TODO fetch roles ids and verify that ctx.roleId is included - // TODO assign roleId of normal-user dynamically ? instead of hard-coded value 3 - if (![1, 2, 3].includes(roleId)) { - const error = new Error("RoleId must be either 1, 2 or 3."); - error.status = 400; - throw error; - } - const t = await postgresSeq.transaction(); - await auth(); - const [newUser, isCreated] = await User.findOrCreate({ - where: { - username: ctx.username, - email: ctx.email, + return await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + include: { + roles: { + include: { + role: true, + }, }, - defaults: { - password: ctx.password + groups: { + include: { + group: { + include: { + policies: true, + }, + }, + }, }, - }); - if (!isCreated) { - const error = new Error(`User ${ctx.email} or ${ctx.username} already exists.`); - error.status = 400; - throw error; - } - // Insert new user into keycloak db - const kcUser = await keycloakAdmin.users.create({ - username: ctx.username, - email: ctx.email, - emailVerified: true, - enabled: true, - realm: process.env.KEYCLOAK_REALM, - }); - if (!kcUser.id) { - await t.rollback(); - } - // Set new user's password in kc - await keycloakAdmin.users.resetPassword({ - id: kcUser.id, - credential: { - temporary: false, - type: 'password', - value: ctx.password + group_admin: true, + requests: true, + display_fields: { + include: { + std_field: true, + }, }, - realm: process.env.KEYCLOAK_REALM, - }); - // Set user kc_id in db - await User.update({ - kc_id: kcUser.id - }, { - where: { - id: newUser.id - } - }); - // User Role allocation, default user is normal-user - await RoleUser.create({ - role_id: roleId, - kc_id: kcUser.id - }) - await t.commit(); - const date = new Date().toLocaleDateString(); - const message = `[INFO]:[User ${ctx.username} is created with this email address: ${ctx.email} successfully.]:[${date}]`; - const subject = "In-Sylva new user added"; - await mailService.send(process.env.IN_SYLVA_EMAIL_FROM, process.env.IN_SYLVA_EMAIL_TO, subject, "", message); - await botService.message(message); - return newUser; - }, - - // GET list users - // Backlog: security vulnerability. Realm structure should implement here. - async users(){ - await auth(); - return await keycloakAdmin.users.find({ - realm: process.env.KEYCLOAK_REALM - }); - }, - - // GET Fetch single user - async user(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - await auth(); - return await keycloakAdmin.users.findOne({ - id: ctx.id, realm: process.env.KEYCLOAK_REALM, - }); - }, + }, + }); +} - // GET kc_id from email - async kcId(ctx) { - if (!ctx || !ctx.email) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const { kc_id } = await db.select('kc_id').from('users').where('email', ctx.email).timeout(1000, { cancel: true }).then((result) => result[0]); - const role = await RoleUser.findOne({ - attributes: ['role_id'], - where: { - kc_id: kc_id - } - }); - return { kcId: kc_id, role: role }; - }, +async function createUser(kc_id, email) { + if (!kc_id || !email) { + throw Error("The request body is empty!"); + } - // GET user account details - // TODO return user details such as roles and policies - async detail(ctx) { - if (!ctx || !ctx.kcId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - return await db.select("*").from('users').where('kc_id', ctx.kcId).timeout(1000, {cancel: true}).then((result) => result[0]); - }, + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); - // GET users assigned to a role - async getAssignedUserByRole(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const params = { roleId: ctx.id }; - const assignedUserByRole = await db.raw(` - select u.username as username,u.email as useremail, r.name as rolename from roles r - inner join roles_users ru on r.id = ru.role_id - inner join users u on ru.kc_id = u.kc_id - where r.id = :roleId - `, params); - return assignedUserByRole.rows; - }, + if (user) { + throw Error("User already exists!"); + } - // PUT update the user. - async update(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - await auth(); - const userId = ctx.id - keycloakAdmin.setConfig({ - realmName: process.env.KEYCLOAK_REALM - }); - await keycloakAdmin.users.update( - { id: userId }, - { - firstName: ctx.firstName, - lastName: ctx.lastName, - // email: ctx.email, - emailVerified: true, - }); - const user = await keycloakAdmin.users.findOne({ - id: userId, realm: process.env.KEYCLOAK_REALM, - }); - await db('users').where('kc_id', userId).update({ - firstname: ctx.firstName, - lastname: ctx.lastName - }).returning('*').then((result) => result[0]); - return user; - }, + const newUser = await prisma.user.create({ + data: { + kc_id: kc_id, + email, + }, + }); - // DELETE a user - async delete(ctx) { - if (!ctx || !ctx.userId) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - await auth(); - const deleted = await User.destroy({ - where: { kc_id: ctx.userId } - }); - if (deleted) { - return await keycloakAdmin.users.del({ - id: ctx.userId, - realm: process.env.KEYCLOAK_REALM - }) - } - }, + await prisma.roleUser.create({ + data: { + role_id: 3, + user_id: newUser.id, + }, + }); - // POST create the system admin. - // It should be used once at system start-up. - async createSystemUser() { - await auth(); - const checkUser = await User.findAndCountAll({ - where: { - [Op.or]: [ - { username: "admin" }, - { email: "admin@inrae.fr" } - ] - } - }); - if (checkUser.count) { - return { user: null, status: 409 }; - } - // Insert new user into keycloak db - const kc_user = await keycloakAdmin.users.create({ - username: "admin", - email: process.env.IN_SYLVA_ADMIN_USERNAME, - emailVerified: true, - enabled: true, - realm: process.env.KEYCLOAK_REALM, - }); - // Set new user's password - if (kc_user) { - await keycloakAdmin.users.resetPassword({ - id: kc_user.id, - credential: { - temporary: false, - type: 'password', - value: process.env.IN_SYLVA_ADMIN_PASSWORD - }, - realm: process.env.KEYCLOAK_REALM, - }); - } - const pg_user = await User.create({ - kc_id: kc_user.id, - username: "admin", - email: process.env.IN_SYLVA_ADMIN_USERNAME, - password: process.env.IN_SYLVA_ADMIN_PASSWORD - }); - await RoleUser.create({ - role_id: 1, - kc_id: kc_user.id - }); - return { user: pg_user, status: 201 }; - }, + return newUser; +} - // GET Fetch all users and their group and role - async usersWithGroupAndRole() { - const usersWithGroupsAndRole = await db.raw( - `select DISTINCT u.id, u.kc_id, u.username, u.email, - g.name as groupName, g.description as groupDescription, r.name as roleName, r.description as roleDescription from users u - inner join roles_users ru on u.kc_id = ru.kc_id - inner join roles r on ru.role_id = r.id - left join group_users gu on u.id = gu.user_id - left join groups g on g.id = gu.group_id`); - return usersWithGroupsAndRole.rows; - }, +async function deleteUser(kc_id) { + if (!kc_id) { + throw Error("The request body is empty!"); + } - // GET Fetch a user and their group and role - async userWithGroupAndRole(ctx) { - if (!ctx || !ctx.id) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const params = { userId: ctx.id }; - const userWithGroupAndRole = await db.raw( - `select DISTINCT u.id, u.kc_id, u.username, u.email, g.id as groupId, - g.name as groupName, g.description as groupDescription, r.name as roleName, r.id as roleId, r.description as roleDescription from users u - inner join roles_users ru on u.kc_id = ru.kc_id - inner join roles r on ru.role_id = r.id - left join group_users gu on u.id = gu.user_id - left join groups g on g.id = gu.group_id - where u.kc_id = :userId`, params); - return userWithGroupAndRole.rows; - }, + const user = await prisma.user.findFirst({ + where: { + kc_id: kc_id, + }, + }); - // POST Email a user - async sendMail(ctx) { - if (!ctx || !ctx.subject || !ctx.message) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - await mailService.send(process.env.IN_SYLVA_EMAIL_FROM, ctx.email, ctx.subject, "", ctx.message); - await botService.message(ctx.message); - return {message: "Mail sent."}; - }, + if (!user) { + throw Error("User not found"); + } - // POST Reset a user password. - async resetPassword(ctx) { - if (!ctx || !ctx.email) { - const error = new Error("Required parameters are missing."); - error.status = 400; - throw error; - } - const t = await postgresSeq.transaction(); - await auth(); - const users = await keycloakAdmin.users.find({ - email: ctx.email, - realm: process.env.KEYCLOAK_REALM, - }); - if (!users || users.length === 0) { - const error = new Error("User account not found."); - error.status = 400; - throw error; - } - const user = users[0]; - const hashedPassword = await generatePassword(10); - // Update user in insylva db. - const [updatedRowsCount, updatedRows] = await User.update({ - password: hashedPassword, - }, { - where: { - kc_id: user.id, - }, - }); - if (!updatedRowsCount || updatedRowsCount <= 0) { - t.rollback(); - const error = new Error("Reset password failed."); - error.status = 500; - throw error; - } - // Update user in keycloak db. - await keycloakAdmin.users.resetPassword({ - id: user.id, - credential: { - temporary: false, - type: 'password', - value: password, - }, - realm: process.env.KEYCLOAK_REALM, - }); - await this.sendMail({...ctx, subject: "Password reset", message: `Your new password is ${password}`}); - t.commit(); - return {message: "Password reset successfully"}; - }, + const deleted = await prisma.user.delete({ + where: { + kc_id: kc_id, + }, + }); + if (!deleted) { + throw Error("User not found!"); + } + const kcDeleted = await deleteKeycloakUser({ kc_id: kc_id }); + if (!kcDeleted) { + throw Error("Keycloak user not found!"); + } + return deleted; } module.exports = { - create: UserService.create, - user: UserService.user, - users: UserService.users, - kcId: UserService.kcId, - update: UserService.update, - detail: UserService.detail, - delete: UserService.delete, - getAssignedUserByRole: UserService.getAssignedUserByRole, - createSystemUser: UserService.createSystemUser, - usersWithGroupAndRole: UserService.usersWithGroupAndRole, - userWithGroupAndRole: UserService.userWithGroupAndRole, - sendMail: UserService.sendMail, - resetPassword: UserService.resetPassword + getUsers, + getUser, + createUser, + deleteUser, }; diff --git a/app/middlewares/errorHandler.js b/app/middlewares/errorHandler.js deleted file mode 100644 index a31ed06..0000000 --- a/app/middlewares/errorHandler.js +++ /dev/null @@ -1,20 +0,0 @@ -const logger = require("../utils/logger"); - -/* - * Global error handler middleware - * - * - Catches and handles all errors thrown within services, handlers, and routes. - * - Logs error details for debugging and monitoring. - * - Ensures the application does not crash on unhandled errors. - */ -const errorHandler = async (ctx, next) => { - try { - await next(); - } catch (err) { - logger.error(err.message); - ctx.status = err.status || 500; - ctx.body = { error: err.message || "Internal Server Error" }; - } -}; - -module.exports = errorHandler; diff --git a/app/models/Group.js b/app/models/Group.js deleted file mode 100644 index 00c3764..0000000 --- a/app/models/Group.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict' - -const { postgresSeq } = require('../config/db') -const Sequelize = require('sequelize') -const { Policy } = require('./Policy') -const { User } = require('./User') - -class Group extends Sequelize.Model { } -class GroupsPolicy extends Sequelize.Model { } -class GroupUser extends Sequelize.Model { } - -Group.init({ - name: { type: Sequelize.STRING, allowNull: false }, - description: { type: Sequelize.TEXT, allowNull: true }, - user_id: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: User, - key: "id", - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE, - } - }, -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "groups" -}) - -GroupsPolicy.init({ - group_id: { - type: Sequelize.INTEGER, allowNull: false, - references: { - model: Group, - key: "id", - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE - }, - }, - policy_id: { - type: Sequelize.INTEGER, allowNull: false, - references: { - model: Policy, - key: "id", - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE - }, - } -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "groups_policies" -}) - -GroupUser.init({ - group_id: { - type: Sequelize.INTEGER, allowNull: false, references: { - model: Group, - key: "id", - deferrable: Sequelize.Deferrable.INITIALLY_DEFERRED - }, - - }, - user_id: { - type: Sequelize.INTEGER, allowNull: false, references: { - model: User, - key: "id", - deferrable: Sequelize.Deferrable.INITIALLY_DEFERRED - }, - - } -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "group_users" -}) - -module.exports = { - Group, - GroupsPolicy, - GroupUser, -} diff --git a/app/models/Policy.js b/app/models/Policy.js deleted file mode 100644 index 7b2a923..0000000 --- a/app/models/Policy.js +++ /dev/null @@ -1,97 +0,0 @@ -"use strict"; - -const { postgresSeq } = require("../config/db"); -const Sequelize = require("sequelize"); -const { StdField } = require("./StdField"); -const { Source } = require("./Source"); -const { User } = require('./User') -class Policy extends Sequelize.Model { } -class PolicyField extends Sequelize.Model { } -class PolicySource extends Sequelize.Model { } - -Policy.init( - { - name: { type: Sequelize.STRING, allowNull: false }, - is_default: { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: false, - }, - user_id: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: User, - key: "id", - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE, - } - }, - }, - { - underscored: true, - sequelize: postgresSeq, - modelName: "policies" - } -) - -PolicyField.init( - { - policy_id: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: Policy, - key: "id", - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE, - }, - unique: "policy_field_policy_id_fkey" - }, - std_field_id: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: StdField, - key: "id", - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE, - }, - unique: "std_fields_id_fkkey" - }, - }, - { - underscored: true, - sequelize: postgresSeq, - modelName: "policy_fields", - }) - -PolicySource.init({ - policy_id: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: Policy, - key: "id", - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE, - }, - unique: "policy_field_policy_id_fkey" - }, - source_id: { - type: Sequelize.INTEGER, allowNull: false, - references: { - model: Source, - key: 'id', - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE - }, - unique: "policy_source_source_id_fkkey" - } -}, - { - underscored: true, - sequelize: postgresSeq, - modelName: "policy_sources", -}) - -module.exports = { - Policy, - PolicyField, - PolicySource -}; diff --git a/app/models/Role.js b/app/models/Role.js deleted file mode 100644 index 0c2a95e..0000000 --- a/app/models/Role.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict' - -const { postgresSeq } = require('../config/db') -const Sequelize = require('sequelize') -const { User } = require('./User') - -class Role extends Sequelize.Model { } -class RoleUser extends Sequelize.Model { } - -Role.init({ - name: { type: Sequelize.STRING, allowNull: false }, - description: { type: Sequelize.STRING, allowNull: false }, -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "roles" -}) - -RoleUser.init({ - role_id: { - type: Sequelize.INTEGER, allowNull: false, - references: { - model: Role, - key: 'id', - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE - }, - unique: false - }, - kc_id: { - type: Sequelize.STRING, allowNull: false, - references: { - model: User, - key: 'kc_id', - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE - }, - unique: false - } -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "roles_users" -}) - -module.exports = { - Role, - RoleUser -} diff --git a/app/models/Source.js b/app/models/Source.js deleted file mode 100644 index 615afa8..0000000 --- a/app/models/Source.js +++ /dev/null @@ -1,78 +0,0 @@ -'use strict' - -const { postgresSeq } = require('../config/db') -const Sequelize = require('sequelize') -const { User } = require('./User') - -class Source extends Sequelize.Model { } - -Source.init({ - name: { type: Sequelize.TEXT, allowNull: true }, - description: { type: Sequelize.TEXT, allowNull: true }, -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "sources" -}) - -class SourceIndices extends Sequelize.Model { } -class SourceSharing extends Sequelize.Model { } - -SourceIndices.init({ - source_id: { - type: Sequelize.INTEGER, allowNull: false, - references: { - model: Source, - key: 'id', - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE - }, - unique: false - }, - index_id: { - type: Sequelize.STRING, allowNull: false - }, - mng_id: { - type: Sequelize.STRING, allowNull: false - }, - is_send: { - type: Sequelize.BOOLEAN, allowNull: false - } -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "sources_indices" -}) - -SourceSharing.init({ - source_id: { - type: Sequelize.INTEGER, allowNull: false, - references: { - model: Source, - key: 'id', - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE - }, - unique: false - }, - user_id: { - type: Sequelize.INTEGER, allowNull: false, references: { - model: User, - key: 'id', - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE - }, - unique: false - } -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "sources_indices" -}) - -SourceIndices.belongsTo(Source, { - foreignKey: "sources_indices_source_id_fkey" -}) - -module.exports = { - Source, - SourceIndices, - SourceSharing -} \ No newline at end of file diff --git a/app/models/StdField.js b/app/models/StdField.js deleted file mode 100644 index 5ae463c..0000000 --- a/app/models/StdField.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict' - -const { postgresSeq } = require('../config/db') -const Sequelize = require('sequelize') - -class StdField extends Sequelize.Model { } - -StdField.init({ - std_field_id: { - type: Sequelize.INTEGER, allowNull: true, - references: { - model: StdField, - key: 'id', - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE - } - }, - category: { type: Sequelize.TEXT, allowNull: true }, - field_name: { type: Sequelize.TEXT, allowNull: true }, - definition_and_comment: { type: Sequelize.TEXT, allowNull: true }, - obligation_or_condition: { type: Sequelize.TEXT, allowNull: true }, - cardinality: { type: Sequelize.TEXT, allowNull: true }, - field_type: { type: Sequelize.TEXT, allowNull: true }, - values: { type: Sequelize.TEXT, allowNull: true }, - ispublic: { type: Sequelize.BOOLEAN, allowNull: true, defaultValue: false }, - isoptional: { type: Sequelize.BOOLEAN, allowNull: true, defaultValue: false }, -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "std_fields" -}) - -module.exports = { - StdField -} diff --git a/app/models/User.js b/app/models/User.js deleted file mode 100644 index 14f18e3..0000000 --- a/app/models/User.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict' - -const { postgresSeq } = require('../config/db') -const Sequelize = require('sequelize') -const bcrypt = require('bcrypt') -class User extends Sequelize.Model { } - -User.init({ - kc_id: { type: Sequelize.STRING, allowNull: true, unique: 'compositeIndex' }, - username: { type: Sequelize.STRING, allowNull: false, unique: 'compositeIndex' }, - name: { type: Sequelize.STRING, allowNull: true }, - surname: { type: Sequelize.STRING, allowNull: true }, - email: { - type: Sequelize.STRING, allowNull: false, validate: { - isEmail: true - } - }, - password: { type: Sequelize.STRING, allowNull: false } -}, { - underscored: true, - sequelize: postgresSeq, - modelName: 'user', - instanceMethods: { - validPassword: function (password) { - return bcrypt.compareSync(password, this.password); - } - } -}) - -User.beforeCreate(async (user, options) => { - const salt = bcrypt.genSaltSync() - user.password = bcrypt.hashSync(user.password, salt) -}) - -module.exports = { - User -} diff --git a/app/models/UserFieldsDisplaySettings.js b/app/models/UserFieldsDisplaySettings.js deleted file mode 100644 index 98b7ccb..0000000 --- a/app/models/UserFieldsDisplaySettings.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict' - -const { Model, DataTypes, Deferrable} = require('sequelize'); -const { postgresSeq } = require('../config/db'); -const { User } = require('./User'); -const {StdField} = require("./StdField"); - -class UserFieldsDisplaySettings extends Model { } - -UserFieldsDisplaySettings.init({ - user_id: { - type: DataTypes.INTEGER, - allowNull: false, - unique: true, - references: { - model: User, - key: "id", - deferrable: Deferrable.INITIALLY_IMMEDIATE, - } - }, - std_fields_ids: { - type: DataTypes.ARRAY(DataTypes.INTEGER), - allowNull: false, - references: { - model: StdField, - key: 'id', - deferrable: Deferrable.INITIALLY_IMMEDIATE - }, - } -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "user_fields_display_settings" -}); - -// Verify if all standard fields exists in database -UserFieldsDisplaySettings.validateStdFieldIds = async (stdFieldIds) => { - // Count the number of existing IDs in std_fields - const validIds = await StdField.count({ - where: { id: stdFieldIds }, - }); - // Check if it matches the length of stdFieldIds - return validIds === stdFieldIds.length; -}; - -module.exports = { - UserFieldsDisplaySettings, -}; diff --git a/app/models/UserHistory.js b/app/models/UserHistory.js deleted file mode 100644 index eb4e685..0000000 --- a/app/models/UserHistory.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict' - -const { postgresSeq } = require('../config/db') -const Sequelize = require('sequelize') -const { User } = require('./User') - -class SearchHistory extends Sequelize.Model { } - -SearchHistory.init({ - kc_id: { - type: Sequelize.INTEGER, allowNull: false, - references: { - model: User, - key: 'id', - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE - } - }, - query: { type: Sequelize.TEXT, allowNull: false }, - name: { type: Sequelize.STRING, allowNull: false }, - ui_structure: { type: Sequelize.TEXT, allowNull: false }, - description: { type: Sequelize.TEXT, allowNull: false }, -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "user_search_his" -}) - -module.exports = { - SearchHistory -} diff --git a/app/models/UserRequest.js b/app/models/UserRequest.js deleted file mode 100644 index 4f55515..0000000 --- a/app/models/UserRequest.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict' - -const { postgresSeq } = require('../config/db') -const Sequelize = require('sequelize') -const { User } = require('./User') - -class UserRequest extends Sequelize.Model { } - -UserRequest.init({ - user_id: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: User, - key: "id", - deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE, - } - }, - request_message: { type: Sequelize.TEXT, allowNull: false }, - is_processed: { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: false, - }, -}, { - underscored: true, - sequelize: postgresSeq, - modelName: "user_requests" -}) - -module.exports = { - UserRequest -} diff --git a/app/utils/cast.js b/app/utils/cast.js new file mode 100644 index 0000000..d5e76db --- /dev/null +++ b/app/utils/cast.js @@ -0,0 +1,18 @@ +function isBooleanString(value) { + if (typeof value !== "string") { + return false; + } + return value.toLowerCase() === "true" || value.toLowerCase() === "false"; +} + +function castStringToBoolean(value) { + if (isBooleanString(value)) { + return value.toLowerCase() === "true"; + } + return value; +} + +module.exports = { + isBooleanString, + castStringToBoolean, +}; diff --git a/app/utils/errorHandler.js b/app/utils/errorHandler.js new file mode 100644 index 0000000..a071ce0 --- /dev/null +++ b/app/utils/errorHandler.js @@ -0,0 +1,31 @@ +const util = require("util"); +const logger = require("../utils/format"); + +async function handleInternalError(err, ctx) { + const error = JSON.stringify( + util.inspect(err, { compact: false, depth: 1, breakLength: 80 }) + ); + ctx.body = { + error: "Internal server error", + statusCode: 500, + message: err.message, + }; + ctx.status = 500; + logger.error(error); +} + +async function handleMissingParameters(missingParameters, ctx) { + ctx.status = 400; + ctx.body = { + error: "Bad request", + statusCode: 400, + message: `The following parameters are missing: ${missingParameters.join( + ", " + )}`, + }; +} + +module.exports = { + handleInternalError, + handleMissingParameters, +}; diff --git a/app/utils/logger.js b/app/utils/format.js similarity index 90% rename from app/utils/logger.js rename to app/utils/format.js index 9d10265..6843d08 100644 --- a/app/utils/logger.js +++ b/app/utils/format.js @@ -4,6 +4,7 @@ const { createLogger, format, transports } = require('winston'); const { combine, timestamp, printf, colorize, splat, label } = format; const path = require('path'); + const mFormat = printf((info) => { if (info.meta && info.meta instanceof Error) { return `${info.timestamp} ${info.level} ${info.message} : ${info.meta.stack}`; @@ -11,11 +12,12 @@ const mFormat = printf((info) => { return `${info.timestamp} ${info.level} [${info.label}]: ${info.message}`; }); +const LOG_LEVEL = process.env.LOGGER_LEVEL || 'debug'; const logger = createLogger({ transports: [ new (transports.Console)( { - level: process.env.LOG_LEVEL || 'debug', + level: LOG_LEVEL, format: combine( label({ label: path.basename(process.mainModule.filename) }), colorize(), @@ -27,5 +29,7 @@ const logger = createLogger({ ) ] }); - module.exports = logger; + + + diff --git a/db.js b/db.js deleted file mode 100644 index 076fcbe..0000000 --- a/db.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict' - -const { configPg } = require('./app/config/db') -const knex = require('knex') - -module.exports = knex(configPg) \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..88c8b14 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +services: + postgres: + image: registry.forgemia.inra.fr/in-sylva-development/in-sylva.postgres-postgis:1.0.0 + ports: + - "5432:5432" + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + volumes: + - data:/var/lib/postgresql/data + +volumes: + data: diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..19def7c --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +echo -n "Generating Prisma client... " +yarn prisma generate +echo "OK" + +echo -n "Running migrations... " +yarn prisma migrate deploy +echo "OK" + +echo -n "Seeding database... " +yarn prisma db seed +echo "OK" + +exec "$@" diff --git a/gitlab-ci.yml b/gitlab-ci.yml new file mode 100644 index 0000000..485dc39 --- /dev/null +++ b/gitlab-ci.yml @@ -0,0 +1,25 @@ +stages: + - test + - build + +test: + stage: test + image: node:20.16.0 + script: + - npm install + - npm test + +build: + stage: build + image: docker:latest + services: + - docker:dind + script: + - docker build -t in-sylva/gatekeeper . + - version=$(jq -r .version package.json) + - docker tag in-sylva/gatekeeper in-sylva/gatekeeper:$version + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker push in-sylva/gatekeeper:$version + only: + - master + when: manual diff --git a/index.js b/index.js index 2a67bf7..b8a0587 100644 --- a/index.js +++ b/index.js @@ -1,22 +1,10 @@ -'use strict' +"use strict"; -const dotenv = require('dotenv') +require("module-alias/register"); -if (process.env.NODE_ENV === 'development') { - dotenv.config({ silent: true }) +const dotenv = require("dotenv"); +if (process.env.NODE_ENV === "development") { + dotenv.config({ silent: true }); } -const processType = process.env.PROCESS_TYPE - -switch (processType) { - case 'web': - require('./web') - break - case 'script': - require('./migrate') - break - default: - throw new Error( - `Invalid process type: ${processType}. It should be one of: 'web', 'script'.` - ) -} \ No newline at end of file +require("./web"); diff --git a/init-keycloak.js b/init-keycloak.js deleted file mode 100644 index c0b75b7..0000000 --- a/init-keycloak.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -const KeycloakConnect = require('@in-sylva/keycloak-koa-connect').default -const config = require('./keycloak.js') - -// If this option is used, it is legal to include the value of jwt in the body -const bodyStore = require('@in-sylva/keycloak-koa-connect/stores/body-store').default -// If this option is used, it is also legal to pass a token at http://a.com?jwt=token -const queryStore = require('@in-sylva/keycloak-koa-connect/stores/query-store').default - -const keycloak = new KeycloakConnect({ store: [queryStore, bodyStore]}, config) - -module.exports = { keycloak } diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..1cd6c68 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + moduleNameMapper: { + "^@/(.*)$": "<rootDir>/$1", + }, + clearMocks: true, + testEnvironment: "node", + setupFilesAfterEnv: ["<rootDir>/jest.setup.js"], +}; diff --git a/jest.setup.js b/jest.setup.js new file mode 100644 index 0000000..6ffe91e --- /dev/null +++ b/jest.setup.js @@ -0,0 +1,17 @@ +jest.mock("@/app/utils/format", () => ({ + info: jest.fn(), + error: jest.fn(), + debug: jest.fn(), +})); + +jest.mock("@/app/dal/elasticService", () => ({ + prepareForBulk: jest.fn(), + bulk: jest.fn(), + countByIndex: jest.fn(), + search: jest.fn(), +})); + +jest.mock("@/app/dal/mongoService", () => ({ + addSourceToMongoDB: jest.fn(), + removeSourceFromMongoDB: jest.fn(), +})); diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..f126305 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "baseUrl": "./", // Base directory for resolving non-relative module imports + "paths": { + "@/*": ["./*"] // Alias mapping + } + }, + "exclude": ["node_modules"] +} diff --git a/keycloak.js b/keycloak.js deleted file mode 100644 index 2724de9..0000000 --- a/keycloak.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - 'realm': process.env.KEYCLOAK_REALM, - 'auth-server-url': process.env.KEYCLOAK_SERVER_URL, - "ssl-required": "none", - 'resource': process.env.KEYCLOAK_CLIENT_ID, - "verify-token-audience": true, - "credentials": { - "secret": process.env.KEYCLOAK_CREDENTIALS_SECRET - }, - "confidential-port": 0, - "policy-enforcer": {}, - 'bearer-only':true, - 'use-resource-role-mappings': true, - 'realm-public-key': `${process.env.KEYCLOAK_SERVER_PUBLIC_KEY}` -} diff --git a/knexfile.js b/knexfile.js deleted file mode 100644 index 5491c51..0000000 --- a/knexfile.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -const dbConfig = require('./config/db') - -module.exports = { - development: dbConfig, - staging: dbConfig, - production: dbConfig -} diff --git a/migrate.js b/migrate.js deleted file mode 100644 index b71cce0..0000000 --- a/migrate.js +++ /dev/null @@ -1,8 +0,0 @@ -const { exec } = require('child_process') -const logger = require('winston') - -exec('./node_modules/.bin/knex migrate:latest', - (err, stdout, stderr) => { - if (err) throw err - logger.info(stdout) - }) diff --git a/package.json b/package.json index 8287ebe..715c2ca 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,19 @@ { "name": "in-sylva.gatekeeper", - "version": "1.0.0", + "version": "1.1.0", "description": "", "main": "index.js", "scripts": { "test": "jest --setupFiles dotenv/config", - "db-start": "docker run --rm --name inra-paca-local -e POSTGRES_PASSWORD=root -e POSTGRES_USER=root -e POSTGRES_DB=test-inra-paca-db -p 3001:5432 -d postgres:10", - "db-stop": "docker rm -f inra-paca-local || true", - "db-migrate": "./node_modules/.bin/knex migrate:latest", "start": "node index.js", "start:dev": "NODE_ENV=development PROCESS_TYPE=web ./node_modules/.bin/nodemon ./index.js", - "start:prod": "NODE_ENV=production PROCESS_TYPE=web ./node_modules/.bin/nodemon ./index.js", - "docker-build": "../scripts/build \"user\"" + "start:prod": "NODE_ENV=production PROCESS_TYPE=web ./node_modules/.bin/nodemon ./index.js" + }, + "_moduleAliases": { + "@": "." + }, + "prisma": { + "seed": "node prisma/seed.js" }, "repository": { "type": "git", @@ -20,17 +22,15 @@ "author": "IN-SYLVA DEVELOPMENT TEAM", "license": "ISC", "dependencies": { + "@elastic/elasticsearch": "^8.17.0", "@hapi/joi": "17.1.1", - "@in-sylva/common": "git+ssh://git@forgemia.inra.fr:in-sylva-development/in-sylva.common.git", - "@in-sylva/keycloak-koa-connect": "git+ssh://git@forgemia.inra.fr:in-sylva-development/keycloak-koa-connect.git", - "@pixelygroup/keycloak-koa-connect": "^0.9.5", + "@prisma/client": "^6.0.1", "array.prototype.flatmap": "1.2.4", "bcrypt": "^5.1.0", "bluebird": "3.7.2", + "consul": "^0.37.0", + "jest-mock-extended": "^4.0.0-beta1", "keycloak-admin": "1.14.8", - "keycloak-connect": "12.0.3", - "keycloak-koa-connect": "^1.0.5", - "knex": "0.21.18", "koa": "2.13.1", "koa-bodyparser": "4.3.0", "koa-compose": "4.1.0", @@ -38,22 +38,24 @@ "koa-router": "10.0.0", "koa-session": "6.1.0", "koa2-cors": "2.0.6", + "module-alias": "^2.2.3", + "mongodb": "^6.12.0", "nodemon": "2.0.7", - "openid-client": "4.4.0", "pg": "8.5.1", "pg-hstore": "^2.3.3", "request": "^2.88.0", - "sequelize": "^6.3.4", "winston": "3.3.3", "yarn": "1.22.10" }, "devDependencies": { "dotenv": "8.2.0", + "dotenv-cli": "^7.4.4", "eslint": "^7.6.0", "eslint-config-prettier": "^6.11.0", "eslint-plugin-prettier": "^3.1.4", "jest": "26.6.3", "jscs": "^3.0.7", - "pretty-format": "^26.2.0" + "pretty-format": "^26.2.0", + "prisma": "^6.0.1" } } diff --git a/prisma/client.js b/prisma/client.js new file mode 100644 index 0000000..6d75495 --- /dev/null +++ b/prisma/client.js @@ -0,0 +1,5 @@ +const { PrismaClient } = require("@prisma/client"); + +const prisma = new PrismaClient(); + +module.exports = prisma; diff --git a/prisma/migrations/20241211224745_0_init/migration.sql b/prisma/migrations/20241211224745_0_init/migration.sql new file mode 100644 index 0000000..dcc15e6 --- /dev/null +++ b/prisma/migrations/20241211224745_0_init/migration.sql @@ -0,0 +1,186 @@ +-- CreateTable +CREATE TABLE "users" ( + "id" SERIAL NOT NULL, + "kc_id" TEXT NOT NULL, + "email" TEXT NOT NULL, + + CONSTRAINT "users_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "search_history" ( + "id" SERIAL NOT NULL, + "user_id" INTEGER NOT NULL, + "query" TEXT NOT NULL, + "name" TEXT NOT NULL, + "ui_structure" TEXT NOT NULL, + "description" TEXT NOT NULL, + + CONSTRAINT "search_history_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "user_requests" ( + "id" SERIAL NOT NULL, + "user_id" INTEGER NOT NULL, + "request_message" TEXT NOT NULL, + "is_processed" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "user_requests_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "std_fields" ( + "id" SERIAL NOT NULL, + "category" TEXT, + "field_name" TEXT, + "definition_and_comment" TEXT, + "obligation_or_condition" TEXT, + "cardinality" TEXT, + "field_type" TEXT, + "values" TEXT, + "ispublic" BOOLEAN NOT NULL DEFAULT false, + "isoptional" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "std_fields_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "sources" ( + "id" SERIAL NOT NULL, + "name" TEXT, + "description" TEXT, + + CONSTRAINT "sources_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "source_indices" ( + "id" SERIAL NOT NULL, + "source_id" INTEGER NOT NULL, + "index_id" TEXT NOT NULL, + "mng_id" TEXT NOT NULL, + "is_send" BOOLEAN NOT NULL, + + CONSTRAINT "source_indices_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "roles" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL, + + CONSTRAINT "roles_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "policies" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "is_default" BOOLEAN NOT NULL DEFAULT false, + "user_id" INTEGER NOT NULL, + + CONSTRAINT "policies_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "groups" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL, + "admin_id" INTEGER NOT NULL, + + CONSTRAINT "groups_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "groups_users" ( + "group_id" INTEGER NOT NULL, + "user_id" INTEGER NOT NULL, + + CONSTRAINT "groups_users_pkey" PRIMARY KEY ("group_id","user_id") +); + +-- CreateTable +CREATE TABLE "groups_policies" ( + "group_id" INTEGER NOT NULL, + "policy_id" INTEGER NOT NULL, + + CONSTRAINT "groups_policies_pkey" PRIMARY KEY ("group_id","policy_id") +); + +-- CreateTable +CREATE TABLE "policies_fields" ( + "policy_id" INTEGER NOT NULL, + "std_field_id" INTEGER NOT NULL, + + CONSTRAINT "policies_fields_pkey" PRIMARY KEY ("policy_id","std_field_id") +); + +-- CreateTable +CREATE TABLE "policies_sources" ( + "policy_id" INTEGER NOT NULL, + "source_id" INTEGER NOT NULL, + + CONSTRAINT "policies_sources_pkey" PRIMARY KEY ("policy_id","source_id") +); + +-- CreateTable +CREATE TABLE "roles_users" ( + "role_id" INTEGER NOT NULL, + "user_id" INTEGER NOT NULL, + + CONSTRAINT "roles_users_pkey" PRIMARY KEY ("role_id","user_id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "users_kc_id_key" ON "users"("kc_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); + +-- AddForeignKey +ALTER TABLE "search_history" ADD CONSTRAINT "search_history_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "user_requests" ADD CONSTRAINT "user_requests_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "source_indices" ADD CONSTRAINT "source_indices_source_id_fkey" FOREIGN KEY ("source_id") REFERENCES "sources"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "policies" ADD CONSTRAINT "policies_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "groups" ADD CONSTRAINT "groups_admin_id_fkey" FOREIGN KEY ("admin_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "groups_users" ADD CONSTRAINT "groups_users_group_id_fkey" FOREIGN KEY ("group_id") REFERENCES "groups"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "groups_users" ADD CONSTRAINT "groups_users_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "groups_policies" ADD CONSTRAINT "groups_policies_group_id_fkey" FOREIGN KEY ("group_id") REFERENCES "groups"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "groups_policies" ADD CONSTRAINT "groups_policies_policy_id_fkey" FOREIGN KEY ("policy_id") REFERENCES "policies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "policies_fields" ADD CONSTRAINT "policies_fields_policy_id_fkey" FOREIGN KEY ("policy_id") REFERENCES "policies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "policies_fields" ADD CONSTRAINT "policies_fields_std_field_id_fkey" FOREIGN KEY ("std_field_id") REFERENCES "std_fields"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "policies_sources" ADD CONSTRAINT "policies_sources_policy_id_fkey" FOREIGN KEY ("policy_id") REFERENCES "policies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "policies_sources" ADD CONSTRAINT "policies_sources_source_id_fkey" FOREIGN KEY ("source_id") REFERENCES "sources"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "roles_users" ADD CONSTRAINT "roles_users_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "roles_users" ADD CONSTRAINT "roles_users_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20241211225353_1_cascade_delete_group_when_admin_deleted/migration.sql b/prisma/migrations/20241211225353_1_cascade_delete_group_when_admin_deleted/migration.sql new file mode 100644 index 0000000..a3aeb94 --- /dev/null +++ b/prisma/migrations/20241211225353_1_cascade_delete_group_when_admin_deleted/migration.sql @@ -0,0 +1,5 @@ +-- DropForeignKey +ALTER TABLE "groups" DROP CONSTRAINT "groups_admin_id_fkey"; + +-- AddForeignKey +ALTER TABLE "groups" ADD CONSTRAINT "groups_admin_id_fkey" FOREIGN KEY ("admin_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20241211235543_2_cascade_delete/migration.sql b/prisma/migrations/20241211235543_2_cascade_delete/migration.sql new file mode 100644 index 0000000..bc87f49 --- /dev/null +++ b/prisma/migrations/20241211235543_2_cascade_delete/migration.sql @@ -0,0 +1,17 @@ +-- DropForeignKey +ALTER TABLE "policies" DROP CONSTRAINT "policies_user_id_fkey"; + +-- DropForeignKey +ALTER TABLE "search_history" DROP CONSTRAINT "search_history_user_id_fkey"; + +-- DropForeignKey +ALTER TABLE "user_requests" DROP CONSTRAINT "user_requests_user_id_fkey"; + +-- AddForeignKey +ALTER TABLE "search_history" ADD CONSTRAINT "search_history_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "user_requests" ADD CONSTRAINT "user_requests_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "policies" ADD CONSTRAINT "policies_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20241212020153_3_std_field_unique_name/migration.sql b/prisma/migrations/20241212020153_3_std_field_unique_name/migration.sql new file mode 100644 index 0000000..cb831ad --- /dev/null +++ b/prisma/migrations/20241212020153_3_std_field_unique_name/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - A unique constraint covering the columns `[field_name]` on the table `std_fields` will be added. If there are existing duplicate values, this will fail. + - Made the column `field_name` on table `std_fields` required. This step will fail if there are existing NULL values in that column. + +*/ +-- AlterTable +ALTER TABLE "std_fields" ALTER COLUMN "field_name" SET NOT NULL; + +-- CreateIndex +CREATE UNIQUE INDEX "std_fields_field_name_key" ON "std_fields"("field_name"); diff --git a/prisma/migrations/20241212233751_4_nullable_and_default_source_indices/migration.sql b/prisma/migrations/20241212233751_4_nullable_and_default_source_indices/migration.sql new file mode 100644 index 0000000..0e228af --- /dev/null +++ b/prisma/migrations/20241212233751_4_nullable_and_default_source_indices/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE "source_indices" ALTER COLUMN "index_id" DROP NOT NULL, +ALTER COLUMN "mng_id" DROP NOT NULL, +ALTER COLUMN "is_send" SET DEFAULT false; diff --git a/prisma/migrations/20241212234126_5_source_providers/migration.sql b/prisma/migrations/20241212234126_5_source_providers/migration.sql new file mode 100644 index 0000000..c9ca428 --- /dev/null +++ b/prisma/migrations/20241212234126_5_source_providers/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "sources_users" ( + "source_id" INTEGER NOT NULL, + "user_id" INTEGER NOT NULL, + + CONSTRAINT "sources_users_pkey" PRIMARY KEY ("source_id","user_id") +); + +-- AddForeignKey +ALTER TABLE "sources_users" ADD CONSTRAINT "sources_users_source_id_fkey" FOREIGN KEY ("source_id") REFERENCES "sources"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "sources_users" ADD CONSTRAINT "sources_users_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20241214231527_6_display_fields/migration.sql b/prisma/migrations/20241214231527_6_display_fields/migration.sql new file mode 100644 index 0000000..237f1d7 --- /dev/null +++ b/prisma/migrations/20241214231527_6_display_fields/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "display_std_fields" ( + "user_id" INTEGER NOT NULL, + "std_field_id" INTEGER NOT NULL, + + CONSTRAINT "display_std_fields_pkey" PRIMARY KEY ("user_id","std_field_id") +); + +-- AddForeignKey +ALTER TABLE "display_std_fields" ADD CONSTRAINT "display_std_fields_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "display_std_fields" ADD CONSTRAINT "display_std_fields_std_field_id_fkey" FOREIGN KEY ("std_field_id") REFERENCES "std_fields"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20250116142811_7_list_url_and_default_fields/migration.sql b/prisma/migrations/20250116142811_7_list_url_and_default_fields/migration.sql new file mode 100644 index 0000000..2de09fd --- /dev/null +++ b/prisma/migrations/20250116142811_7_list_url_and_default_fields/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "std_fields" ADD COLUMN "default_display_fields" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "list_url" TEXT; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..648c57f --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..1f9fc9a --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,200 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model User { + id Int @id @default(autoincrement()) + kc_id String @unique + email String @unique + history SearchHistory[] + requests UserRequest[] + display_fields DisplayStdField[] + policy_admin Policy[] + source_provider SourceUser[] + group_admin Group[] + groups GroupUser[] + roles RoleUser[] + + @@map("users") +} + +model SearchHistory { + id Int @id @default(autoincrement()) + user_id Int + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + query String + name String + ui_structure String + description String + + @@map("search_history") +} + +model UserRequest { + id Int @id @default(autoincrement()) + user_id Int + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + request_message String + is_processed Boolean @default(false) + + @@map("user_requests") +} + +model StdField { + id Int @id @default(autoincrement()) + field_name String @unique + category String? + definition_and_comment String? + obligation_or_condition String? + cardinality String? + field_type String? + values String? + ispublic Boolean @default(false) + isoptional Boolean @default(false) + list_url String? + default_display_fields Boolean @default(false) + policies PolicyStdField[] + display_fields DisplayStdField[] + + @@map("std_fields") +} + +model Source { + id Int @id @default(autoincrement()) + name String? + description String? + source_indices SourceIndices[] + providers SourceUser[] + policies PolicySource[] + + @@map("sources") +} + +model SourceIndices { + id Int @id @default(autoincrement()) + source_id Int + source Source @relation(fields: [source_id], references: [id], onDelete: Cascade) + index_id String? + mng_id String? + is_send Boolean @default(false) + + @@map("source_indices") +} + +model SourceUser { + source_id Int + user_id Int + + source Source @relation(fields: [source_id], references: [id], onDelete: Cascade) + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + + @@id([source_id, user_id]) + @@map("sources_users") +} + +model Role { + id Int @id @default(autoincrement()) + name String + description String + + users RoleUser[] + + @@map("roles") +} + +model Policy { + id Int @id @default(autoincrement()) + name String + is_default Boolean @default(false) + user_id Int + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + + std_fields PolicyStdField[] + sources PolicySource[] + groups GroupPolicy[] + + @@map("policies") +} + +model Group { + id Int @id @default(autoincrement()) + name String + description String + admin_id Int + admin User @relation(fields: [admin_id], references: [id], onDelete: Cascade) + users GroupUser[] + policies GroupPolicy[] + + @@map("groups") +} + +model GroupUser { + group_id Int + user_id Int + + group Group @relation(fields: [group_id], references: [id], onDelete: Cascade) + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + + @@id([group_id, user_id]) + @@map("groups_users") +} + +model GroupPolicy { + group_id Int + policy_id Int + + group Group @relation(fields: [group_id], references: [id], onDelete: Cascade) + policy Policy @relation(fields: [policy_id], references: [id], onDelete: Cascade) + + @@id([group_id, policy_id]) + @@map("groups_policies") +} + +model PolicyStdField { + policy_id Int + std_field_id Int + + policy Policy @relation(fields: [policy_id], references: [id], onDelete: Cascade) + std_field StdField @relation(fields: [std_field_id], references: [id], onDelete: Cascade) + + @@id([policy_id, std_field_id]) + @@map("policies_fields") +} + +model PolicySource { + policy_id Int + source_id Int + + policy Policy @relation(fields: [policy_id], references: [id], onDelete: Cascade) + source Source @relation(fields: [source_id], references: [id], onDelete: Cascade) + + @@id([policy_id, source_id]) + @@map("policies_sources") +} + +model RoleUser { + role_id Int + user_id Int + + role Role @relation(fields: [role_id], references: [id], onDelete: Cascade) + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + + @@id([role_id, user_id]) + @@map("roles_users") +} + +model DisplayStdField { + user_id Int + std_field_id Int + + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + std_field StdField @relation(fields: [std_field_id], references: [id], onDelete: Cascade) + + @@id([user_id, std_field_id]) + @@map("display_std_fields") +} diff --git a/prisma/seed.js b/prisma/seed.js new file mode 100644 index 0000000..3d9159e --- /dev/null +++ b/prisma/seed.js @@ -0,0 +1,49 @@ +const { PrismaClient } = require("@prisma/client"); +const prisma = new PrismaClient(); + +async function main() { + await prisma.role.deleteMany(); + // Add default roles + await prisma.role.upsert({ + where: { + id: 1, + }, + create: { + id: 1, + name: "admin", + description: "Admin role", + }, + update: {}, + }); + await prisma.role.upsert({ + where: { + id: 2, + }, + create: { + id: 2, + name: "source-manager", + description: "Source manager role", + }, + update: {}, + }); + await prisma.role.upsert({ + where: { + id: 3, + }, + create: { + id: 3, + name: "policy-manager", + description: "Policy manager role", + }, + update: {}, + }); +} +main() + .then(async () => { + await prisma.$disconnect(); + }) + .catch(async (e) => { + console.error(e); + await prisma.$disconnect(); + process.exit(1); + }); diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100644 index 66884ad..0000000 --- a/scripts/build.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -e - -app_name=$1 - -image_name=rs-ms/$app_name -current_version=$(docker images $image_name --format "{{.Tag}}" | head -2 | tail -1 | grep -o "[0-9]\+" || echo 0) -next_version=$(( $current_version + 1)) -docker build -t $image_name:v$next_version -t $image_name:latest . - -kubectl delete pod -l app=$app_name \ No newline at end of file diff --git a/scripts/migrate.sh b/scripts/migrate.sh deleted file mode 100644 index 571004c..0000000 --- a/scripts/migrate.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -function get_pod () { - kubectl get pods -l app="$1" -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' | head -1 -} - -product_pod_name=$(get_pod product) -user_pod_name=$(get_pod user) - -for pod_name in $user_pod_name $product_pod_name; do - kubectl exec $pod_name npm run db-migrate -done - -kubectl exec $product_pod_name npm run db-seed diff --git a/scripts/up.sh b/scripts/up.sh deleted file mode 100644 index 3b88979..0000000 --- a/scripts/up.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -eval $(minikube docker-env) - -script_directory="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -for app in "user" "product" "gateway" "front-end"; do - echo "cding into: $script_directory/$app" - cd "$script_directory/../$app" - npm run docker-build - kubectl create -f ./k8s -done - -docker-compose up -d - -echo 'waiting 30 seconds for postgresql to start...' -sleep 30 - -docker exec $(docker ps --format '{{.ID}}' -f name=trainingmicroservicesv3_db) psql -d test-products-db -c "CREATE DATABASE \"test-users-db\"" - -$script_directory/migrate diff --git a/scripts/util.sh b/scripts/util.sh deleted file mode 100644 index 86d3520..0000000 --- a/scripts/util.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -pid1=$(lsof -t -i:4000) - -kill -9 $pid1 diff --git a/server.js b/server.js index 62fdcb4..50fec82 100644 --- a/server.js +++ b/server.js @@ -1,39 +1,62 @@ -'use strict' - -const Koa = require('koa'); -const api = require('./app/api'); -const koaLogger = require('koa-logger'); -const cors = require('koa2-cors'); -const { keycloak } = require('./init-keycloak'); -const errorHandler = require('./app/middlewares/errorHandler'); - -const responseTime = async (ctx, next) => { - const start = Date.now(); - await next(); - const ms = Date.now() - start; - ctx.set('X-Response-Time', `${ms}ms`); +"use strict"; + +const Koa = require("koa"); +const logger = require("@/app/utils/format"); +const api = require("./app/api"); +const koaLogger = require("koa-logger"); +const cors = require("koa2-cors"); +const { isAccessTokenValid } = require("./app/dal/authService"); + +async function responseTime(ctx, next) { + const start = Date.now(); + await next(); + const ms = Date.now() - start; + ctx.set("X-Response-Time", `${ms}ms`); } const app = new Koa(); -app.use(errorHandler); - -const middlewares = keycloak.middleware({ - logout: '/logout', - admin: '/', - protected: '/protected/resource' -}); -middlewares.forEach(function (middleware) { - app.use(middleware); -}) - app.proxy = false; -app.use(cors({origin: '*'})); -app.use(koaLogger()); -app.use(responseTime); -// Register routes -app.use(api.routes()); -app.use(api.allowedMethods()); +app + .use( + cors({ + origin: "*", + }) + ) + .use(koaLogger()) + .use(async (ctx, next) => { + if (ctx.path === "/healthcheck") { + ctx.status = 200; + ctx.body = { message: "Healthcheck OK" }; + return; + } + const { authorization } = ctx.request.headers; + if (!authorization) { + ctx.status = 401; + ctx.body = { + message: "Bearer token is required in Authorization header", + }; + } else { + const accessToken = authorization.split(" ")[1]; + const isValid = await isAccessTokenValid(accessToken); + if (!isValid) { + ctx.status = 401; + ctx.body = { + message: "Invalid access token", + }; + } else { + await next(); + } + } + }) + .use(api.routes()) + .use(api.allowedMethods()) + .use(responseTime); + +app.on("error", (err) => { + logger.error("Server error", { error: err.message }); + throw err; +}); module.exports = app; diff --git a/web.js b/web.js index 2ed23e2..f8502c9 100644 --- a/web.js +++ b/web.js @@ -1,17 +1,31 @@ -'use strict' +"use strict"; -const { promisify } = require('util'); -const http = require('http'); -const app = require('./server'); -const config = require('./app/config/server'); -const logger = require("./app/utils/logger"); +const { promisify } = require("util"); +const http = require("http"); +const logger = require("@/app/utils/format"); +const app = require("@/server"); +require("@/app/config/server"); +require("@/app/config/postgres"); +require("@/app/config/elk"); +require("@/app/config/mongo"); -const server = http.createServer(app.callback()); -const serverListen = promisify(server.listen).bind(server); +logger.level = process.env.LOGGER_LEVEL || "info"; -serverListen(config.port).then(() => { - logger.info(`in-sylva.gatekeeper service is up and running on localhost:${config.port}`); - }).catch((error) => { - logger.error(error); - process.exit(1); +try { + const server = http.createServer(app.callback()); + const serverListen = promisify(server.listen).bind(server); + + serverListen(process.env.PORT) + .then(() => { + logger.info( + `in-sylva.gatekeeper service is up and running on localhost:${process.env.PORT}` + ); + }) + .catch((err) => { + logger.error(err); + process.exit(1); }); +} catch (error) { + console.error("Failed to start app:", error); + process.exit(1); +} -- GitLab