Compare commits

...
87 Commits
Author SHA1 Message Date
arthur simas 5122dbde85 Merge pull request #126 from dadosfera/feat/scoped-packages
FEAT: scoped packages
2022-08-02 14:43:07 -03:00
Arthur Simas dcb3094890 FIX: protospack-v2 changes 2022-08-01 17:05:00 -03:00
Arthur Simas c8e4e90100 FIX: aws codeartifact region 2022-08-01 15:52:30 -03:00
Arthur Simas 09cff9fa15 CI: cleanup ci tests 2022-08-01 15:52:24 -03:00
Arthur Simas 79b624cea6 FEAT: using scoped package @dadosfera/dadosfera-logs 2022-08-01 15:52:18 -03:00
Arthur Simas 5356334258 FEAT: using scoped package @dadosfera/protospack-v2 2022-08-01 15:52:06 -03:00
Gabriel Amorim 5f6e4e88a5 Merge pull request #125 from dadosfera/fix/users-roles-management
FIX: sending access token to role function to evaluate editing permissions
2022-08-01 15:30:28 -03:00
Gabriel Rosa 32ab834834 FIX: fix user.decorator tests failing because of missing access_token on fakeUserPayload 2022-08-01 13:46:27 -03:00
Gabriel Rosa ebbfe0abff FIX: sending access token to role function to evaluate editing permissions 2022-08-01 09:17:08 -03:00
arthur simas d76a2b4ab8 Merge pull request #123 from dadosfera/feat/user-management
FEAT: user management
2022-07-28 19:24:50 -03:00
Arthur Simas 047737eadd FIX: removing log from subscribe.complete method 2022-07-28 19:21:16 -03:00
Arthur Simas e20ae71672 FIX: resend invite endpoint renamed 2022-07-27 19:35:25 -03:00
Arthur Simas 97b6047cf1 FIX: avoiding to change user object received in adjustUsersPayload, returning modified object instead 2022-07-27 19:20:35 -03:00
Arthur Simas 9c6d2b499a FEAT: resend invite 2022-07-27 18:15:07 -03:00
Arthur Simas f43c270c33 CHORE: protospack-v2 pre-release 2022-07-27 12:51:07 -03:00
Arthur Simas b0ccb39f2c FIX: adding error message on each failed user (BatchCreateUserRes) 2022-07-27 12:46:20 -03:00
Gabriel Rosa 9f900882cb FIX: sending customerId on updateUser 2022-07-25 10:09:18 -03:00
Gabriel Rosa a24abed9ed updated protospack version 2022-07-22 08:53:27 -03:00
Gabriel Rosa b04a38bcc0 FIX: get departments and jobTitles 2022-07-22 08:45:58 -03:00
Gabriel Rosa 67706c5fc3 FIX: returning hierarchies the right way 2022-07-20 16:58:53 -03:00
Gabriel Rosa 0fa4dcb933 new protospack version 2022-07-20 15:37:01 -03:00
Gabriel Rosa 04284e1feb FIX: changing hierarchy strategy 2022-07-20 15:18:14 -03:00
Gabriel Rosa 3e7581e20c FIX: changing hierarchy strategy 2022-07-20 15:17:58 -03:00
Gabriel Rosa c61c37b268 FIX: new user columns 2022-07-20 09:50:58 -03:00
Gabriel Rosa 5ce8099c25 tests fixes 2022-07-19 16:22:24 -03:00
Gabriel Rosa 9cdb0a5d38 FIX: Update user accepting roleIds 2022-07-19 10:17:57 -03:00
Gabriel Rosa 57f47d3b2e FIX: get all hierarchies 2022-07-18 18:20:15 -03:00
Gabriel Rosa 4ab955a7a1 removed unused import 2022-07-18 18:00:44 -03:00
Gabriel Rosa 1ca3694b75 merge main 2022-07-18 18:00:18 -03:00
Gabriel Rosa a6a35c507a new Protospack 2022-07-18 17:56:50 -03:00
Gabriel Rosa cfcb8c1c42 FEAT: new user endpoints 2022-07-18 17:50:12 -03:00
Gabriel Rosa a55da8e20f FEAT: batch create user 2022-07-18 15:37:56 -03:00
Gabriel Rosa 82310c6314 FEAT: Users roles id = seqid 2022-07-18 14:04:35 -03:00
Gabriel Rosa 52f9d06ad0 FIX: get permissions route 2022-07-18 11:24:52 -03:00
Victor Radael a83f91a720 Merge pull request #122 from dadosfera/ft/connectors-manager
FEAT: Ft/connectors manager
2022-07-15 18:46:01 -03:00
Victor Radael ccef7628f9 FIX: Remove AuthController 2022-07-15 18:00:09 -03:00
Victor Radael a5e424c854 Update app.module.ts 2022-07-15 17:53:38 -03:00
Gabriel Rosa 339432df16 FIX: server urls on swagger 2022-07-15 15:24:42 -03:00
Victor Radael 7dfa10d5ee Merge branch 'ft/connectors-manager' of github.com:dadosfera/maestro into ft/connectors-manager 2022-07-15 15:10:40 -03:00
Victor Radael c87ceec77b FIX: Upload Imaga Files and add permissions 2022-07-15 15:10:11 -03:00
Gabriel Rosa 071c524a2e FIX: changed get roles to search roles 2022-07-15 14:39:30 -03:00
Gabriel Rosa 1de4eedd38 FEAT: new permission for user management 2022-07-15 12:27:49 -03:00
Gabriel Rosa 29276d6070 Merge branch 'main' into feat/user-management 2022-07-15 11:42:13 -03:00
Gabriel Rosa 344a251de1 merge main 2022-07-15 11:42:01 -03:00
Arthur Simas 3b95032554 Merge branch 'main' into ft/connectors-manager 2022-07-15 11:24:46 -03:00
arthur simas a0089b5d5d Merge pull request #121 from dadosfera/fix/terms-of-use-ip
FIX: terms of use ip:port
2022-07-15 11:15:12 -03:00
Gabriel Rosa 1b620b6fb0 FEAT: ROle management 2022-07-15 11:08:57 -03:00
Arthur Simas 1eb76d4b1c FIX: terms of use ip:port 2022-07-15 11:08:46 -03:00
Victor Radael 314bc5a562 Merge remote-tracking branch 'origin' into ft/connectors-manager 2022-07-14 17:34:17 -03:00
Victor Radael d01b0357ed FIX: Remove logs and add Authenticated 2022-07-14 17:31:12 -03:00
arthur simas a206aad8db Merge pull request #120 from dadosfera/feature/terms-of-use
FEAT: terms of use
2022-07-14 14:50:01 -03:00
Gabriel Rosa de57bfed1e FEAT: new roles routes working 2022-07-14 12:53:00 -03:00
Gabriel Rosa 14cfc46492 FEAT: Roles WIP 2022-07-14 10:23:04 -03:00
Victor Radael 9d10e58b1b FIX: SSL 2022-07-13 14:14:48 -03:00
Victor Radael 6dec2c1e56 FIX: Header Dadosfera-Lang 2022-07-13 14:11:35 -03:00
Victor Radael 23a65d18a6 Search Adapt fields 2022-07-12 17:04:25 -03:00
Gabriel Rosa d5cd562860 FEAT: get permissions working 2022-07-12 15:43:21 -03:00
Gabriel Rosa f5064420a7 FIX: missing import 2022-07-12 14:48:01 -03:00
Gabriel Rosa bb5befb2f6 FEAT: new route for updating Roles and changed hierarchy to string 2022-07-12 14:27:54 -03:00
Gabriel Rosa 398360fadb FEAT: new routes do add or remove permisisons from role 2022-07-12 12:11:03 -03:00
Victor Radael 13072978f5 FEAT: Accept-Language 2022-07-12 09:21:45 -03:00
Gabriel Rosa b9612609d9 FIX: add description on create role documentation 2022-07-11 16:58:40 -03:00
Victor Radael e7b37629a1 Insomnia File 2022-07-11 14:08:25 -03:00
Victor Radael 5b6994b247 Merge remote-tracking branch 'origin/main' into ft/connectors-manager 2022-07-11 12:26:02 -03:00
Gabriel Rosa 039f837914 FIX: tests fixing 2022-07-11 11:07:08 -03:00
Gabriel Rosa 83dcc7942c FIX: better code organization 2022-07-11 10:42:01 -03:00
Gabriel Rosa 5c079bb4f5 FIX: permissions contract 2022-07-11 10:09:06 -03:00
Arthur Simas f383cf49d5 FIX: added get token method 2022-07-11 02:13:53 -03:00
Arthur Simas db78ee3d8b FEAT: terms of use 2022-07-11 02:13:33 -03:00
Gabriel Rosa 730fab8ed1 FEAT: internationalization of permissions 2022-07-08 19:03:32 -03:00
Victor Radael 04e1e1ff2b New features to Connector's Search 2022-07-08 18:33:58 -03:00
Victor Radael d309c20cbf New features to Connector's Search 2022-07-08 18:32:06 -03:00
Gabriel Rosa 4cff593695 FEAT: Roles and permissions modules 2022-07-08 17:28:23 -03:00
Victor Radael 03d42df3fd Att Modules 2022-07-08 13:06:26 -03:00
Gabriel Rosa ea04f59e04 FIX: Fixed create user req 2022-07-08 12:28:40 -03:00
Gabriel Rosa d1961a9710 FEAT: User Management WIP 2022-07-08 12:04:56 -03:00
Gabriel Rosa 358dd6a15a merge main 2022-07-08 10:08:22 -03:00
Victor Radael 395db05b05 Merge main 2022-07-07 17:17:57 -03:00
Victor Radael 2d69e6db70 Merge main 2022-07-07 17:13:30 -03:00
Victor Radael 9514134db1 Merge main 2022-07-07 16:58:33 -03:00
Victor Radael 0c69cebca1 Merge main 2022-07-07 16:55:52 -03:00
Victor Radael 9791b8bdb0 Merge main 2022-07-07 16:55:04 -03:00
Victor Radael 6803ab3420 Merge main 2022-07-07 16:36:28 -03:00
Victor Radael 6aa7be98d2 Merge main 2022-07-07 16:32:17 -03:00
Gabriel Rosa f5665557c9 FIX: better parameter naming 2022-07-07 16:17:45 -03:00
Gabriel Rosa 4356adea65 FEAT: User module init 2022-07-07 15:57:40 -03:00
Victor Radael 8d23eaeea2 Code artifact and Get Connectors Tags 2022-06-30 16:55:46 -03:00
63 changed files with 2850 additions and 680 deletions
+3 -15
View File
@@ -11,30 +11,18 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- name: Install Docker Compose
run: |
python3 -m pip install docker-compose --upgrade
- name: Build
env:
ENV: test
IMAGE_TAG: test
APP_NAME: ${{ github.event.repository.name }}
run: |
export ENV=test
export IMAGE_TAG=test
export ACCOUNT_ID=test
docker-compose -f build.docker-compose.yml build
docker build -t $APP_NAME --target test .
- name: Run Test
env:
ENV: test
IMAGE_TAG: test
ACCOUNT_ID: test
APP_NAME: ${{ github.event.repository.name }}
run: |
export ENV=test
export IMAGE_TAG=test
docker-compose -f build.docker-compose.yml run --rm --entrypoint="npm run test" $APP_NAME
docker run --rm --entrypoint="npm" $APP_NAME run test
- name: Remove Docker's Trash
if: always()
+12 -7
View File
@@ -1,16 +1,21 @@
# install all dependencies
FROM node:18.3.0-alpine3.15 as packages
WORKDIR /packages
COPY package.json .
COPY package-lock.json .
RUN apk update \
&& apk add --no-cache aws-cli \
&& aws codeartifact login --tool npm --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1 \
WORKDIR /app
COPY package.json package-lock.json ./
RUN apk add --no-cache aws-cli \
&& aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1 \
&& npm install
# unit test specific build
FROM node:18.3.0-alpine3.15 as test
WORKDIR /app
COPY --from=packages /app/node_modules ./node_modules
COPY . ./
FROM node:18.3.0-alpine3.15
WORKDIR /app
COPY . /app/
COPY --from=packages /packages/node_modules /app/node_modules
COPY --from=packages /app/node_modules ./node_modules
RUN npm run build
EXPOSE 3333
ENTRYPOINT npm run start
Binary file not shown.
Binary file not shown.
+5 -1
View File
@@ -3,7 +3,11 @@
"sourceRoot": "src",
"compilerOptions": {
"assets": [
"**/*.proto"
"**/*.proto",
{
"include": "i18n/**/*",
"watchAssets": true
}
],
"plugins": [
"@nestjs/swagger"
+102 -47
View File
@@ -10,6 +10,8 @@
"license": "UNLICENSED",
"dependencies": {
"@aws-sdk/client-secrets-manager": "^3.112.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.3",
"@dadosfera/protospack-v2": "3.10.0",
"@grpc/grpc-js": "^1.6.7",
"@grpc/proto-loader": "^0.6.13",
"@nestjs/common": "^8.4.7",
@@ -23,7 +25,6 @@
"@nestjs/swagger": "^5.2.1",
"axios": "^0.25.0",
"cron-parser": "^4.4.0",
"dadosfera-logs": "^1.0.0-alpha.10",
"dotenv": "^14.3.2",
"elastic-apm-node": "^3.36.0",
"helmet": "^5.1.0",
@@ -35,8 +36,7 @@
"passport-google-oauth20": "^2.0.0",
"passport-hubspot-oauth2": "^1.0.3",
"passport-mailchimp": "^1.1.0",
"protospack": "2.5.1",
"protospack-v2": "3.0.0",
"protospack": "2.5.2",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.5.5",
@@ -1712,6 +1712,26 @@
"kuler": "^2.0.0"
}
},
"node_modules/@dadosfera/dadosfera-logs": {
"version": "1.0.0-beta.3",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/dadosfera-logs/-/dadosfera-logs-1.0.0-beta.3.tgz",
"integrity": "sha512-VuGiQlpDulcl1+FSFoT4/6PA9Pr+DAzbFHAYesz9rPinR46ejG7ZBZk3m86NYqQtgMyurqXvgCR8GPy9FpwqFw==",
"dependencies": {
"@elastic/ecs-winston-format": "^1.3.1",
"winston": "^3.7.2",
"winston-log2gelf": "^2.4.0"
}
},
"node_modules/@dadosfera/protospack-v2": {
"version": "3.10.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.10.0.tgz",
"integrity": "sha512-XPfRh2R2zZtN/MRxAwbC994xDJftRlnl3w2vqPGMt996tJCpZkedzdH1hHdqer68QDIMM5td0OpW/Gs8K1H07A==",
"dependencies": {
"@grpc/grpc-js": "^1.6.7",
"rxjs": "^7.5.5",
"ts-proto": "^1.112.2"
}
},
"node_modules/@elastic/ecs-helpers": {
"version": "1.1.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@elastic/ecs-helpers/-/ecs-helpers-1.1.0.tgz",
@@ -4237,6 +4257,17 @@
"dev": true,
"license": "MIT"
},
"node_modules/class-validator": {
"version": "0.13.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/class-validator/-/class-validator-0.13.2.tgz",
"integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==",
"optional": true,
"peer": true,
"dependencies": {
"libphonenumber-js": "^1.9.43",
"validator": "^13.7.0"
}
},
"node_modules/cli-cursor": {
"version": "3.1.0",
"dev": true,
@@ -4553,16 +4584,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/dadosfera-logs": {
"version": "1.0.0-alpha.10",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/dadosfera-logs/-/dadosfera-logs-1.0.0-alpha.10.tgz",
"integrity": "sha512-OZ5sAkELiEQrViSvHjxM/Vb/skiXnkimNfgExdKPrHuVfy2s46d47p0QkL5XabFCvjEGyRKUCK8+UL/kRsKnnw==",
"dependencies": {
"@elastic/ecs-winston-format": "^1.3.1",
"winston": "^3.7.2",
"winston-log2gelf": "^2.4.0"
}
},
"node_modules/data-urls": {
"version": "2.0.0",
"dev": true,
@@ -7823,6 +7844,13 @@
"node": ">= 0.8.0"
}
},
"node_modules/libphonenumber-js": {
"version": "1.10.7",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/libphonenumber-js/-/libphonenumber-js-1.10.7.tgz",
"integrity": "sha512-jZXLCCWMe1b/HXkjiLeYt2JsytZMcqH26jLFIdzFDFF0xvSUWrYKyvPlyPG+XJzEyKUFbcZxLdWGMwQsWaHDxQ==",
"optional": true,
"peer": true
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"dev": true,
@@ -8989,22 +9017,13 @@
}
},
"node_modules/protospack": {
"version": "2.5.1",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/protospack/-/protospack-2.5.1.tgz",
"integrity": "sha512-4JTx7B7WxpC1G3S8T7SUEAEgVM9UjdYrOG37fr42wKxl9y6wOyCZJYjG/lXeAqFXOEPj9KSlM+MqBfLIMHWHeA==",
"version": "2.5.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/protospack/-/protospack-2.5.2.tgz",
"integrity": "sha512-t7NyWE4WDuAVixjL10SDUXHu8iW4H0azndTC3n/w335D/IodPgN47ebm3iMXbvoQTlQcCbjjc+HblaRa9oR8bA==",
"dependencies": {
"rxjs": "^7.5.5"
}
},
"node_modules/protospack-v2": {
"version": "3.0.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/protospack-v2/-/protospack-v2-3.0.0.tgz",
"integrity": "sha512-GEFbf8+vVaq/lA28K4nFf7bOSlghQJn/aeVnrYzh4YJUaa1+ccNz3uQs9p5OGbfjt2u/fFE5rHGEBUtueAA8KA==",
"dependencies": {
"rxjs": "^7.5.5",
"ts-proto": "^1.112.2"
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"license": "MIT",
@@ -10570,6 +10589,16 @@
"node": ">=10.12.0"
}
},
"node_modules/validator": {
"version": "13.7.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/validator/-/validator-13.7.0.tgz",
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==",
"optional": true,
"peer": true,
"engines": {
"node": ">= 0.10"
}
},
"node_modules/vary": {
"version": "1.1.2",
"license": "MIT",
@@ -12247,6 +12276,26 @@
"kuler": "^2.0.0"
}
},
"@dadosfera/dadosfera-logs": {
"version": "1.0.0-beta.3",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/dadosfera-logs/-/dadosfera-logs-1.0.0-beta.3.tgz",
"integrity": "sha512-VuGiQlpDulcl1+FSFoT4/6PA9Pr+DAzbFHAYesz9rPinR46ejG7ZBZk3m86NYqQtgMyurqXvgCR8GPy9FpwqFw==",
"requires": {
"@elastic/ecs-winston-format": "^1.3.1",
"winston": "^3.7.2",
"winston-log2gelf": "^2.4.0"
}
},
"@dadosfera/protospack-v2": {
"version": "3.10.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.10.0.tgz",
"integrity": "sha512-XPfRh2R2zZtN/MRxAwbC994xDJftRlnl3w2vqPGMt996tJCpZkedzdH1hHdqer68QDIMM5td0OpW/Gs8K1H07A==",
"requires": {
"@grpc/grpc-js": "^1.6.7",
"rxjs": "^7.5.5",
"ts-proto": "^1.112.2"
}
},
"@elastic/ecs-helpers": {
"version": "1.1.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@elastic/ecs-helpers/-/ecs-helpers-1.1.0.tgz",
@@ -13969,6 +14018,17 @@
"version": "1.2.2",
"dev": true
},
"class-validator": {
"version": "0.13.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/class-validator/-/class-validator-0.13.2.tgz",
"integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==",
"optional": true,
"peer": true,
"requires": {
"libphonenumber-js": "^1.9.43",
"validator": "^13.7.0"
}
},
"cli-cursor": {
"version": "3.1.0",
"dev": true,
@@ -14199,16 +14259,6 @@
}
}
},
"dadosfera-logs": {
"version": "1.0.0-alpha.10",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/dadosfera-logs/-/dadosfera-logs-1.0.0-alpha.10.tgz",
"integrity": "sha512-OZ5sAkELiEQrViSvHjxM/Vb/skiXnkimNfgExdKPrHuVfy2s46d47p0QkL5XabFCvjEGyRKUCK8+UL/kRsKnnw==",
"requires": {
"@elastic/ecs-winston-format": "^1.3.1",
"winston": "^3.7.2",
"winston-log2gelf": "^2.4.0"
}
},
"data-urls": {
"version": "2.0.0",
"dev": true,
@@ -16407,6 +16457,13 @@
"type-check": "~0.4.0"
}
},
"libphonenumber-js": {
"version": "1.10.7",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/libphonenumber-js/-/libphonenumber-js-1.10.7.tgz",
"integrity": "sha512-jZXLCCWMe1b/HXkjiLeYt2JsytZMcqH26jLFIdzFDFF0xvSUWrYKyvPlyPG+XJzEyKUFbcZxLdWGMwQsWaHDxQ==",
"optional": true,
"peer": true
},
"lines-and-columns": {
"version": "1.2.4",
"dev": true
@@ -17190,22 +17247,13 @@
}
},
"protospack": {
"version": "2.5.1",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/protospack/-/protospack-2.5.1.tgz",
"integrity": "sha512-4JTx7B7WxpC1G3S8T7SUEAEgVM9UjdYrOG37fr42wKxl9y6wOyCZJYjG/lXeAqFXOEPj9KSlM+MqBfLIMHWHeA==",
"version": "2.5.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/protospack/-/protospack-2.5.2.tgz",
"integrity": "sha512-t7NyWE4WDuAVixjL10SDUXHu8iW4H0azndTC3n/w335D/IodPgN47ebm3iMXbvoQTlQcCbjjc+HblaRa9oR8bA==",
"requires": {
"rxjs": "^7.5.5"
}
},
"protospack-v2": {
"version": "3.0.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/protospack-v2/-/protospack-v2-3.0.0.tgz",
"integrity": "sha512-GEFbf8+vVaq/lA28K4nFf7bOSlghQJn/aeVnrYzh4YJUaa1+ccNz3uQs9p5OGbfjt2u/fFE5rHGEBUtueAA8KA==",
"requires": {
"rxjs": "^7.5.5",
"ts-proto": "^1.112.2"
}
},
"proxy-addr": {
"version": "2.0.7",
"requires": {
@@ -18241,6 +18289,13 @@
"source-map": "^0.7.3"
}
},
"validator": {
"version": "13.7.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/validator/-/validator-13.7.0.tgz",
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==",
"optional": true,
"peer": true
},
"vary": {
"version": "1.1.2"
},
+10 -4
View File
@@ -38,7 +38,7 @@
"@nestjs/swagger": "^5.2.1",
"axios": "^0.25.0",
"cron-parser": "^4.4.0",
"dadosfera-logs": "^1.0.0-alpha.10",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.3",
"dotenv": "^14.3.2",
"elastic-apm-node": "^3.36.0",
"helmet": "^5.1.0",
@@ -50,8 +50,8 @@
"passport-google-oauth20": "^2.0.0",
"passport-hubspot-oauth2": "^1.0.3",
"passport-mailchimp": "^1.1.0",
"protospack": "2.5.1",
"protospack-v2": "3.0.0",
"protospack": "2.5.2",
"@dadosfera/protospack-v2": "3.10.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.5.5",
@@ -96,7 +96,13 @@
"json",
"ts"
],
"rootDir": "src",
"rootDir": ".",
"modulePaths": [
"<rootDir>"
],
"moduleDirectories": [
"node_modules"
],
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
+15 -14
View File
@@ -1,8 +1,6 @@
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { ConfigModule } from '@nestjs/config';
import { DadosferaLogger } from 'dadosfera-logs';
import { InputsController } from './modules/inputs/inputs.controller';
import { TransformationsController } from './modules/transformations/transformations.controller';
import { OutputsController } from './modules/outputs/outputs.controllers';
@@ -15,12 +13,10 @@ import { OutputsService } from './modules/outputs/outputs.service';
import { PipelinesService } from './modules/pipelines/pipelines.service';
import { HealthService } from './modules/health/health.service';
import { AuthModule } from './modules/auth/auth.module';
import { InputsClientService } from './clients/inputs/client.service';
import { TransformationsClientService } from './clients/transformations/client.service';
import { OutputsClientService } from './clients/outputs/client.service';
import { PipelinesClientService } from './clients/pipelines/client.service';
import { PermissionsModule } from './modules/permissions/permissions.module';
import { OutputsClientConfiguration } from './clients/outputs/client.config';
import { TransformationsClientConfiguration } from './clients/transformations/client.config';
@@ -37,15 +33,20 @@ import { APP_GUARD } from '@nestjs/core';
import { GoogleStrategy } from './modules/oauth/passport-strategies/google-strategy';
import { MailChimpStrategy } from './modules/oauth/passport-strategies/mailchimp-strategy';
import { SalesforceStrategy } from './modules/oauth/passport-strategies/salesforce-strategy';
import { ConnectorClientConfiguration } from './clients/connector/client.config';
import { ConnectorController } from './modules/connector/connector.controller';
import { ConnectorClientService } from './clients/connector/client.service';
import { UsersModule } from './modules/users/users.module';
import { RolesModule } from './modules/roles/roles.module';
import { ConnectorModule } from './modules/connector/connector.module';
import { AuthModule } from './modules/auth/auth.module';
import { PermissionsModule } from './modules/permissions/permissions.module';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { TermsOfUseModule } from './modules/termsOfUse/termsOfUse.module';
const inputClient = new InputsClientConfiguration();
const outputClient = new OutputsClientConfiguration();
const pipelineClient = new PipelinesClientConfiguration();
const transformationClient = new TransformationsClientConfiguration();
const connectorClient = new ConnectorClientConfiguration();
@Module({
controllers: [
@@ -56,7 +57,6 @@ const connectorClient = new ConnectorClientConfiguration();
HealthController,
CatalogController,
OauthController,
ConnectorController,
],
providers: [
DadosferaLogger,
@@ -65,7 +65,6 @@ const connectorClient = new ConnectorClientConfiguration();
TransformationsService,
OutputsService,
PipelinesService,
ConnectorClientService,
HealthService,
InputsClientService,
TransformationsClientService,
@@ -86,8 +85,10 @@ const connectorClient = new ConnectorClientConfiguration();
ConfigModule.forRoot({
isGlobal: true,
}),
ConnectorModule,
AuthModule,
PermissionsModule,
TermsOfUseModule,
ClientsModule.register([
{
@@ -106,11 +107,11 @@ const connectorClient = new ConnectorClientConfiguration();
name: 'PIPELINES_PACKAGE',
...pipelineClient.config(),
},
{
name: 'CONNECTOR_PACKAGE',
...connectorClient.config(),
},
]),
UsersModule,
RolesModule,
],
})
export class AppModule {}
@@ -1,6 +1,6 @@
import { Request } from 'express';
import { CustomDecorator } from '@nestjs/common';
import { Permission } from './permissions.enum';
import { PermissionsObjectPermission } from './permissions.enum';
import { RequestUser } from './user.decorator';
export const AUTH_FUNCTION_KEY = '__AUTH_FUNCTION__';
@@ -31,13 +31,17 @@ function SetMultipleMetadata(metadataKey, metadataValue): CustomDecorator {
return decoratorFactory as any;
}
export function RequireAllPermissions(...permissions: Permission[]) {
export function RequireAllPermissions(
...permissions: PermissionsObjectPermission[]
) {
return SetMultipleMetadata(AUTH_FUNCTION_KEY, (req, user: RequestUser) =>
permissions.every(({ seqid }) => user.permissions.includes(seqid)),
);
}
export function RequireSomePermission(...permissions: Permission[]) {
export function RequireSomePermission(
...permissions: PermissionsObjectPermission[]
) {
return SetMultipleMetadata(AUTH_FUNCTION_KEY, (req, user: RequestUser) =>
permissions.some(({ seqid }) => user.permissions.includes(seqid)),
);
+22 -12
View File
@@ -5,7 +5,7 @@ import { APP_GUARD } from '@nestjs/core';
import jwt from 'jsonwebtoken';
import { Body, Controller, Get } from '@nestjs/common';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
AuthenticateCondition,
Authenticated,
@@ -13,11 +13,15 @@ import {
RequireSomePermission,
} from './authentication.decorator';
import { AuthenticationGuard } from './authentication.guard';
import { Permissions } from './permissions.enum';
import { PERMISSIONS } from './permissions.enum';
import { AuthClientService } from '../modules/auth/auth.service';
import ErrorCodes from '../utils/errorCodes';
const logger = {
info: (...args) => args,
error: (...args) => args,
};
@Controller('no-class-auth')
class NoClassAuthController {
@Get('body')
@@ -32,19 +36,25 @@ class NoClassAuthController {
}
@Get('required-permission')
@RequireSomePermission(Permissions.ZENDESK.OPEN)
@RequireSomePermission(PERMISSIONS.ZENDESK.permissions.OPEN)
async requiredPermission(@Body() body) {
return { body };
}
@Get('has-all-permissions')
@RequireAllPermissions(Permissions.ZENDESK.OPEN, Permissions.METABASE.OPEN)
@RequireAllPermissions(
PERMISSIONS.ZENDESK.permissions.OPEN,
PERMISSIONS.METABASE.permissions.OPEN,
)
async hasAllPermissions(@Body() body) {
return { body };
}
@Get('has-some-permission')
@RequireSomePermission(Permissions.ZENDESK.OPEN, Permissions.METABASE.OPEN)
@RequireSomePermission(
PERMISSIONS.ZENDESK.permissions.OPEN,
PERMISSIONS.METABASE.permissions.OPEN,
)
async hasSomePermission(@Body() body) {
return { body };
}
@@ -52,7 +62,7 @@ class NoClassAuthController {
@Controller('class-auth-condition')
@AuthenticateCondition((req) => req.get('x-on-class') === 'ok')
@RequireSomePermission(Permissions.METABASE.OPEN)
@RequireSomePermission(PERMISSIONS.METABASE.permissions.OPEN)
class ClassAuthConditionController {
@Get('body')
async getBody(@Body() body) {
@@ -67,7 +77,7 @@ class ClassAuthConditionController {
@Get('required-permission')
@AuthenticateCondition((req) => req.get('x-on-route') === 'ok')
@RequireSomePermission(Permissions.ZENDESK.OPEN)
@RequireSomePermission(PERMISSIONS.ZENDESK.permissions.OPEN)
async requiredPermission(@Body() body) {
return { body };
}
@@ -104,7 +114,7 @@ describe('authentication.guard', () => {
},
{
provide: DadosferaLogger,
useValue: { logger: console },
useValue: { logger },
},
{
provide: APP_GUARD,
@@ -430,17 +440,17 @@ describe('authentication.guard', () => {
NoClassAuthTest(null, null);
ClassAuthConditionTest(null, null);
const tokenZ = CreateToken([Permissions.ZENDESK.OPEN]);
const tokenZ = CreateToken([PERMISSIONS.ZENDESK.permissions.OPEN]);
NoClassAuthTest(tokenZ, ['zendesk']);
ClassAuthConditionTest(tokenZ, ['zendesk']);
const tokenM = CreateToken([Permissions.METABASE.OPEN]);
const tokenM = CreateToken([PERMISSIONS.METABASE.permissions.OPEN]);
NoClassAuthTest(tokenM, ['metabase']);
ClassAuthConditionTest(tokenM, ['metabase']);
const tokenZM = CreateToken([
Permissions.ZENDESK.OPEN,
Permissions.METABASE.OPEN,
PERMISSIONS.ZENDESK.permissions.OPEN,
PERMISSIONS.METABASE.permissions.OPEN,
]);
NoClassAuthTest(tokenZM, ['zendesk', 'metabase']);
ClassAuthConditionTest(tokenZM, ['zendesk', 'metabase']);
+5 -5
View File
@@ -3,13 +3,12 @@ import {
CanActivate,
OnApplicationBootstrap,
ExecutionContext,
Logger,
Inject,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import assert from 'assert';
import jwt from 'jsonwebtoken';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { AuthClientService } from '../modules/auth/auth.service';
import {
AuthenticationFunction,
@@ -23,7 +22,7 @@ import ErrorCodes from '../utils/errorCodes';
export class AuthenticationGuard
implements CanActivate, OnApplicationBootstrap
{
logger: any;
logger: DadosferaLogger;
pems: Map<string, string>;
@@ -96,7 +95,7 @@ export class AuthenticationGuard
);*/
assert(
accessTokenDecoded.payload.token_use === 'access',
accessTokenDecoded?.payload?.token_use === 'access',
'should be an access token',
);
@@ -108,7 +107,7 @@ export class AuthenticationGuard
} catch (err) {
// log errors if authentication is required
if (mustBeAuthenticated) {
this.logger.error('Unable to verify duc access token\n' + err.stack);
this.logger.error('Unable to verify duc access token\n' + err?.stack);
}
return false;
@@ -122,6 +121,7 @@ export class AuthenticationGuard
customer_id: accessTokenPayload.customer_id,
customer_name: accessTokenPayload.customer_name,
customer_tier: accessTokenPayload.customer_tier,
access_token: accessToken,
};
// TODO: for backwards compatibility. remove in the future
request.body.info = {
+375 -154
View File
@@ -1,5 +1,6 @@
import { Logger } from '@nestjs/common';
import assert from 'assert';
import { LanguageEnum } from 'src/utils/languages.enum';
const logger = new Logger();
@@ -8,10 +9,24 @@ export enum PermissionUsages {
INTERNAL = 'internal',
}
export interface Permission {
export interface PermissionsObjectPermission {
seqid: number;
claim: string;
usage: PermissionUsages;
name?: Record<LanguageEnum, string>;
}
export interface PermissionsObjectPermissionTranslated {
seqid: number;
claim: string;
usage: PermissionUsages;
name?: string;
}
export interface PermissionsObjectGroup {
title: {
'pt-br': string;
'en-us': string;
};
permissions: Record<string, PermissionsObjectPermission>;
}
/*
@@ -22,216 +37,422 @@ export interface Permission {
/*export const Permissions: {
[P in keyof any]: { [Q in keyof any]: Permission };
} = {*/
export const Permissions = {
export const PERMISSIONS = {
AUTH: {
CREATE: {
seqid: 18,
claim: 'POST /auth',
usage: PermissionUsages.PUBLIC,
title: {
'pt-br': 'Autenticação',
'en-us': 'Authentication',
},
permissions: {
CREATE: {
seqid: 18,
claim: 'POST /auth',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': '',
'en-us': '',
},
},
},
},
PIPELINE: {
CREATE: {
seqid: 23,
claim: 'POST /pipelines',
usage: PermissionUsages.PUBLIC,
title: {
'pt-br': 'Pipelines',
'en-us': 'Pipelines',
},
GET: {
seqid: 13,
claim: 'GET /pipelines',
usage: PermissionUsages.PUBLIC,
},
UPDATE: {
seqid: 29,
claim: 'PUT /pipelines',
usage: PermissionUsages.PUBLIC,
},
DELETE: {
seqid: 5,
claim: 'DELETE /pipelines',
usage: PermissionUsages.PUBLIC,
permissions: {
CREATE: {
seqid: 23,
claim: 'POST /pipelines',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Criar pipelines',
'en-us': 'Create pipelines',
},
},
GET: {
seqid: 13,
claim: 'GET /pipelines',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Buscar pipelines',
'en-us': 'Get pipelines',
},
},
UPDATE: {
seqid: 29,
claim: 'PUT /pipelines',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Atualizar pipeline',
'en-us': 'Update pipeline',
},
},
DELETE: {
seqid: 5,
claim: 'DELETE /pipelines',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Remover pipeline',
'en-us': 'Delete pipeline',
},
},
},
},
INPUT: {
CREATE: {
seqid: 21,
claim: 'POST /inputs',
usage: PermissionUsages.PUBLIC,
title: {
'pt-br': 'Conexões',
'en-us': 'Connections',
},
GET: {
seqid: 9,
claim: 'GET /inputs',
usage: PermissionUsages.PUBLIC,
},
UPDATE: {
seqid: 27,
claim: 'PUT /inputs',
usage: PermissionUsages.PUBLIC,
},
UPDATE_PARTIAL: {
seqid: 17,
claim: 'PATCH /inputs',
usage: PermissionUsages.PUBLIC,
},
DELETE: {
seqid: 3,
claim: 'DELETE /inputs',
usage: PermissionUsages.PUBLIC,
},
GET_ENTITIES: {
seqid: 10,
claim: 'GET /inputs/available-entities',
usage: PermissionUsages.PUBLIC,
permissions: {
CREATE: {
seqid: 21,
claim: 'POST /inputs',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Criar conexões',
'en-us': 'Create connections',
},
},
GET: {
seqid: 9,
claim: 'GET /inputs',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Buscar conexões',
'en-us': 'Get connections',
},
},
UPDATE: {
seqid: 27,
claim: 'PUT /inputs',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Atualizar conexões',
'en-us': 'Create connections',
},
},
DELETE: {
seqid: 3,
claim: 'DELETE /inputs',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Remover conexões',
'en-us': 'Delete connections',
},
},
GET_ENTITIES: {
seqid: 10,
claim: 'GET /inputs/available-entities',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Remover conexões',
'en-us': 'Delete connections',
},
},
},
},
OUTPUT: {
CREATE: {
seqid: 22,
claim: 'POST /outputs',
usage: PermissionUsages.PUBLIC,
title: {
'pt-br': 'Output',
'en-us': 'Output',
},
GET: {
seqid: 12,
claim: 'GET /outputs',
usage: PermissionUsages.PUBLIC,
},
UPDATE: {
seqid: 28,
claim: 'PUT /outputs',
usage: PermissionUsages.PUBLIC,
},
DELETE: {
seqid: 4,
claim: 'DELETE /outputs',
usage: PermissionUsages.PUBLIC,
permissions: {
CREATE: {
seqid: 22,
claim: 'POST /outputs',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Criar outputs',
'en-us': 'Create outputs',
},
},
GET: {
seqid: 12,
claim: 'GET /outputs',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Buscar outputs',
'en-us': 'Get outputs',
},
},
UPDATE: {
seqid: 28,
claim: 'PUT /outputs',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Editar outputs',
'en-us': 'Edit outputs',
},
},
DELETE: {
seqid: 4,
claim: 'DELETE /outputs',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Remover outputs',
'en-us': 'Remove outputs',
},
},
},
},
TRANSFORMATIONS: {
CREATE: {
seqid: 24,
claim: 'POST /transformations',
usage: PermissionUsages.PUBLIC,
title: {
'pt-br': 'Micro-transformações',
'en-us': 'Micro-transformations',
},
GET: {
seqid: 15,
claim: 'GET /transformations',
usage: PermissionUsages.PUBLIC,
},
UPDATE: {
seqid: 30,
claim: 'PUT /transformations',
usage: PermissionUsages.PUBLIC,
},
DELETE: {
seqid: 6,
claim: 'DELETE /transformations',
usage: PermissionUsages.PUBLIC,
permissions: {
CREATE: {
seqid: 24,
claim: 'POST /transformations',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Criar micro-transformações',
'en-us': 'Create micro-transformations',
},
},
GET: {
seqid: 15,
claim: 'GET /transformations',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Buscar micro-transformações',
'en-us': 'Get micro-transformations',
},
},
UPDATE: {
seqid: 30,
claim: 'PUT /transformations',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Editar micro-transformações',
'en-us': 'Edit micro-transformations',
},
},
DELETE: {
seqid: 6,
claim: 'DELETE /transformations',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Remover micro-transformações',
'en-us': 'Remove micro-transformations',
},
},
},
},
CATALOG: {
CREATE: {
seqid: 19,
claim: 'POST /catalog',
usage: PermissionUsages.PUBLIC,
title: {
'pt-br': 'Catálogo',
'en-us': 'Catalog',
},
GET: {
seqid: 7,
claim: 'GET /catalog',
usage: PermissionUsages.PUBLIC,
},
UPDATE: {
seqid: 25,
claim: 'PUT /catalog',
usage: PermissionUsages.PUBLIC,
},
DELETE: {
seqid: 1,
claim: 'DELETE /catalog',
usage: PermissionUsages.PUBLIC,
permissions: {
CREATE: {
seqid: 19,
claim: 'POST /catalog',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Criar atributos no catálogo',
'en-us': 'Create catalog attributes',
},
},
GET: {
seqid: 7,
claim: 'GET /catalog',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Buscar catálogos',
'en-us': 'Get catalogs',
},
},
UPDATE: {
seqid: 25,
claim: 'PUT /catalog',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Editar catálogo',
'en-us': 'Edit catalogs',
},
},
DELETE: {
seqid: 1,
claim: 'DELETE /catalog',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Remover catálogo',
'en-us': 'Remove catalogs',
},
},
},
},
CONNECTORS: {
CREATE: {
seqid: 20,
claim: 'POST /connectors',
usage: PermissionUsages.PUBLIC,
title: {
'pt-br': 'Conectores',
'en-us': 'Connectors',
},
GET: {
seqid: 8,
claim: 'GET /connectors',
usage: PermissionUsages.PUBLIC,
},
UPDATE: {
seqid: 26,
claim: 'PUT /connectors',
usage: PermissionUsages.PUBLIC,
},
DELETE: {
seqid: 2,
claim: 'DELETE /connectors',
usage: PermissionUsages.PUBLIC,
permissions: {
CREATE: {
seqid: 20,
claim: 'POST /connectors',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Criar conectores',
'en-us': 'Create connectors',
},
},
GET: {
seqid: 8,
claim: 'GET /connectors',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Buscar conectores',
'en-us': 'Get connectors',
},
},
UPDATE: {
seqid: 26,
claim: 'PUT /connectors',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Editar conectores',
'en-us': 'Edit connectors',
},
},
DELETE: {
seqid: 2,
claim: 'DELETE /connectors',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Remover conectores',
'en-us': 'Remove connectors',
},
},
},
},
SNOWFLAKE: {
OPEN: {
seqid: 14,
claim: 'GET /snowflake',
usage: PermissionUsages.PUBLIC,
title: {
'pt-br': 'Snowflake',
'en-us': 'Snowflake',
},
permissions: {
OPEN: {
seqid: 14,
claim: 'GET /snowflake',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Acessar Snowflake',
'en-us': 'Access Snowflake',
},
},
},
},
ZENDESK: {
OPEN: {
seqid: 16,
claim: 'GET /zendesk',
usage: PermissionUsages.PUBLIC,
title: {
'pt-br': 'Zendesk',
'en-us': 'Zendesk',
},
permissions: {
OPEN: {
seqid: 16,
claim: 'GET /zendesk',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Acessar Zendesk',
'en-us': 'Access Zendesk',
},
},
},
},
METABASE: {
OPEN: {
seqid: 11,
claim: 'GET /metabase',
usage: PermissionUsages.PUBLIC,
title: {
'pt-br': 'Metabase',
'en-us': 'Metabase',
},
permissions: {
OPEN: {
seqid: 11,
claim: 'GET /metabase',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Acessar Metabase',
'en-us': 'Access Metabase',
},
},
},
},
ORCHEST: {
INTELLIGENCE: {
seqid: 31,
claim: 'intelligence:open',
usage: PermissionUsages.PUBLIC,
title: {
'pt-br': 'Orchest',
'en-us': 'Orchest',
},
APP_BUILDER: {
seqid: 32,
claim: 'app-builder:open',
usage: PermissionUsages.PUBLIC,
permissions: {
INTELLIGENCE: {
seqid: 31,
claim: 'intelligence:open',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Acessar inteligência',
'en-us': 'Access intelligence',
},
},
APP_BUILDER: {
seqid: 32,
claim: 'app-builder:open',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Acessar App Builder',
'en-us': 'Access App Builder',
},
},
MACHINE_LEARNING: {
seqid: 33,
claim: 'machine-learning:open',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Acessar Machine Learning',
'en-us': 'Access Machine Learning',
},
},
},
MACHINE_LEARNING: {
seqid: 33,
claim: 'machine-learning:open',
usage: PermissionUsages.PUBLIC,
},
USERS: {
title: {
'pt-br': 'Usuários',
'en-us': 'Users',
},
permissions: {
ADMIN: {
seqid: 34,
claim: 'users:admin',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Administrar usuários e grupos',
'en-us': 'Manage user and roles',
},
},
},
},
};
// traverses the object searching for duplicate seqids or claims (executes at runtime)
let nextAvailableSeqid = 0;
const seqids = Object.values(Permissions).flatMap((namespace) =>
Object.values(namespace).map(({ seqid }) => seqid),
const seqids = Object.values(PERMISSIONS).flatMap((namespace) =>
Object.values(namespace.permissions).map(({ seqid }) => seqid),
);
const claims = Object.values(Permissions).flatMap((namespace) =>
Object.values(namespace).map(({ claim }) => claim),
const claims = Object.values(PERMISSIONS).flatMap((namespace) =>
Object.values(namespace.permissions).map(({ claim }) => claim),
);
Object.values(Permissions).map((namespace) =>
Object.values(namespace).map(({ seqid, claim }) => {
Object.values(PERMISSIONS).map((namespace) =>
Object.values(namespace.permissions).map(({ seqid, claim }) => {
const seqidsCount = seqids.filter((x) => x === seqid).length;
assert(seqidsCount === 1, `seqid ${seqid} count is not 1`);
+12 -4
View File
@@ -5,14 +5,18 @@ import { APP_GUARD } from '@nestjs/core';
import jwt from 'jsonwebtoken';
import { Controller, Get } from '@nestjs/common';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { AuthenticationGuard } from './authentication.guard';
import { AuthClientService } from '../modules/auth/auth.service';
import ErrorCodes from '../utils/errorCodes';
import { User } from './user.decorator';
import { Permissions } from './permissions.enum';
import { PERMISSIONS } from './permissions.enum';
const logger = {
info: (...args) => args,
error: (...args) => args,
};
@Controller('user')
class UserController {
@Get()
@@ -45,10 +49,13 @@ describe('user.decorator', () => {
const fakeUserPayload = {
user_id: 'd50d33c7-6c2b-463c-861f-e21667e7c125',
username: 'super.admin',
permissions: [Permissions.METABASE.OPEN].map(({ seqid }) => seqid),
permissions: [PERMISSIONS.METABASE.permissions.OPEN].map(
({ seqid }) => seqid,
),
customer_id: '9d18e8ae-24b9-41a3-9e8f-a25ce57555b11',
customer_name: 'dadosfera',
customer_tier: 'BASIC',
access_token: '',
};
beforeAll(async () => {
@@ -64,7 +71,7 @@ describe('user.decorator', () => {
},
{
provide: DadosferaLogger,
useValue: { logger: console },
useValue: { logger },
},
{
provide: APP_GUARD,
@@ -170,5 +177,6 @@ describe('user.decorator', () => {
UserTest(null);
const token = CreateToken();
fakeUserPayload.access_token = token;
UserTest(token);
});
+1
View File
@@ -10,6 +10,7 @@ export interface RequestUser {
customer_id: string;
customer_name: string;
customer_tier: string;
access_token: string;
}
export const User: (options?: { required?: boolean }) => ParameterDecorator =
-307
View File
@@ -1,307 +0,0 @@
import {
OnModuleInit,
Inject,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { DadosferaLogger } from 'dadosfera-logs';
import { ClientGrpc } from '@nestjs/microservices';
import { ConnectorManager } from 'protospack-v2';
import { from } from 'rxjs';
export class ConnectorClientService implements OnModuleInit {
private connectorServiceRead: ConnectorManager.ReadService.ConnectorManagerReadServices;
private connectorServiceWrite: ConnectorManager.WriteService.ConnectorManagerWriteServices;
logger: any;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject('CONNECTOR_PACKAGE') private readonly grpcClient: ClientGrpc,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.connectorServiceWrite =
this.grpcClient.getService<ConnectorManager.WriteService.ConnectorManagerWriteServices>(
ConnectorManager.ProtoServices.ConnectorManagerWriteServices,
);
this.connectorServiceRead =
this.grpcClient.getService<ConnectorManager.ReadService.ConnectorManagerReadServices>(
ConnectorManager.ProtoServices.ConnectorManagerReadServices,
);
}
async uploadConnector(uploadConnector) {
const connector: ConnectorManager.Entities.ConnectorCreateRequest = {
file: {
buffer: uploadConnector.file.buffer,
mimetypes: uploadConnector.file.mimetype,
},
connector: JSON.stringify(uploadConnector.connector),
};
const serviceBody: ConnectorManager.Messages.RegisterConnectorRequest = {
connector,
};
const observable = from(
this.connectorServiceWrite.RegisterConnector(serviceBody),
);
const result = await new Promise((resolve, reject) => {
observable.subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
//console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.details);
this.logger.error(err);
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
this.logger.info('ConnectorClientService - Upload');
return result;
}
async uploadFile(uploadFile) {
const body: ConnectorManager.Messages.UploadFileRequest = {
file: {
buffer: uploadFile.file.buffer,
mimetypes: uploadFile.file.mimetype,
},
name: uploadFile.name,
};
const observable = from(this.connectorServiceWrite.UploadFile(body));
const result = await new Promise((resolve, reject) => {
observable.subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
//console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.details);
this.logger.error(err);
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
this.logger.info('ConnectorClientService - Upload');
return result;
}
async getAllConnectors(body) {
const observable = from(
this.connectorServiceRead.GetAllConnectors({
search: body.search,
filters: JSON.stringify(body.filters),
}),
);
const result = await new Promise((resolve, reject) => {
observable.subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
//console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err);
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
this.logger.info('ConnectorClientService - getAllConnectors');
return result;
}
async getConnectorDetails(plugin: string) {
const observable = from(
this.connectorServiceRead.GetConnectorDetails({
plugin,
}),
);
const result = await new Promise((resolve, reject) => {
observable.subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
//console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err);
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
this.logger.info('ConnectorClientService - getAllConnectors');
return result;
}
async getConnector(plugin: string) {
const observable = from(
this.connectorServiceRead.GetConnector({
plugin,
}),
);
const result = await new Promise((resolve, reject) => {
observable.subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
//console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err);
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
this.logger.info('ConnectorClientService - getAllConnectors');
return result;
}
async deleteConnector(plugin: string, version: string) {
const observable = from(
this.connectorServiceWrite.DeleteConnector({
version,
plugin,
}),
);
const result = await new Promise((resolve, reject) => {
observable.subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
//console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err);
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
this.logger.info('ConnectorClientService - getAllConnectors');
return result;
}
async updateConnector({ plugin, changes }) {
const observable = from(
this.connectorServiceWrite.UpdateConnector({
plugin,
changes,
}),
);
const result = await new Promise((resolve, reject) => {
observable.subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
//console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err);
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
this.logger.info('ConnectorClientService - getAllConnectors');
return result;
}
}
+2 -4
View File
@@ -1,9 +1,6 @@
import { credentials } from '@grpc/grpc-js';
import { ClientOptions, Transport } from '@nestjs/microservices';
import {
ProtoPackages,
ProtoPaths,
} from 'protospack-v2/dist/lib/Duc';
import { ProtoPackages, ProtoPaths } from '@dadosfera/protospack-v2/dist/lib/Duc';
export class DucClient {
config(): ClientOptions {
@@ -12,6 +9,7 @@ export class DucClient {
options: {
url: process.env.DUC_URL,
package: ProtoPackages.WritePackage,
// credentials: credentials.createSsl(),
credentials: process.env.LOCAL_ENV
? undefined
: credentials.createSsl(),
+2 -2
View File
@@ -24,14 +24,14 @@ import {
objectSnakeToCamel,
} from 'src/utils/CaseConverter';
import { mustache } from 'src/utils/mustache';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { OauthSecrets } from 'src/utils/OauthSecrets';
import { URLSearchParams } from 'url';
import { IIdRequest, UpdateInputRequest } from './interfaces';
export class InputsClientService implements OnModuleInit {
private inputService: InputService;
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
+3 -6
View File
@@ -1,10 +1,7 @@
import { Controller, Inject, OnModuleInit } from '@nestjs/common';
import { ClientGrpc, Payload } from '@nestjs/microservices';
import { DadosferaLogger } from 'dadosfera-logs';
import {
OutputServicesNames,
OutputsServiceInterface,
} from 'protospack';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { OutputServicesNames, OutputsServiceInterface } from 'protospack';
import {
objectCamelToSnake,
objectSnakeToCamel,
@@ -18,7 +15,7 @@ import {
@Controller('output')
export class OutputsClientService implements OnModuleInit {
private outputService: OutputsServiceInterface;
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
+1 -4
View File
@@ -1,8 +1,5 @@
import { ClientOptions, Transport } from '@nestjs/microservices';
import {
PipelinePackages,
PipelineProtoFilePath,
} from 'protospack';
import { PipelinePackages, PipelineProtoFilePath } from 'protospack';
import { credentials } from '@grpc/grpc-js';
export class PipelinesClientConfiguration {
+3 -3
View File
@@ -17,11 +17,11 @@ import {
IIdRequest,
IUpdatePipelineRequest,
} from './interfaces';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
export class PipelinesClientService implements OnModuleInit {
private pipelineService: PipelinesServiceInterface;
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@@ -255,7 +255,7 @@ export class PipelinesClientService implements OnModuleInit {
reject(err);
},
complete() {
this.logger.info('Done');
//this.logger.info('Done');
},
});
})
@@ -1,7 +1,7 @@
import { Controller, Inject, OnModuleInit, Post } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { TransformationsServiceInterface } from 'protospack';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
objectCamelToSnake,
objectSnakeToCamel,
@@ -11,7 +11,7 @@ import { ICreateTransformationsRequest, IIdRequest } from './interfaces';
@Controller('transformation')
export class TransformationsClientService implements OnModuleInit {
private transformationService: TransformationsServiceInterface;
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
+8 -14
View File
@@ -2,7 +2,7 @@ if (process.env.ENV !== 'local') require('elastic-apm-node/start');
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import helmet from 'helmet';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import documentEmpty from '../swagger_empty.json';
@@ -12,21 +12,10 @@ async function bootstrap() {
serviceName: 'maestro',
serviceEnvironment: process.env.ENV,
});
const logger = new DadosferaLogger().logger;
const orginalWinstonLog = logger.log.bind(logger);
logger.log = function (...kwargs: any[]) {
if (kwargs[0] in logger.levels) {
if (kwargs[0] == 'error') {
orginalWinstonLog('error', kwargs[2]);
}
} else {
orginalWinstonLog('info', ...kwargs);
}
};
const logger = new DadosferaLogger();
const app = await NestFactory.create(AppModule, {
logger: logger,
logger,
cors: {
origin: '*',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
@@ -42,6 +31,11 @@ async function bootstrap() {
.setDescription('Documentation for Maestro gateway')
.setVersion('1.0')
.addBearerAuth()
.addServer(
process.env.ENV === 'local'
? 'http://127.0.0.1:3333'
: 'https://maestro.dev.dadosfera.ai',
)
.build();
const document = SwaggerModule.createDocument(app, config);
+3 -3
View File
@@ -19,17 +19,17 @@ import {
AuthEnableTotpMfaRequest,
AuthDisableTotpMfaRequest,
AuthVerifyTotpMfaRequest,
} from 'protospack-v2/dist/lib/Duc/interfaces/messages';
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { AuthClientService } from './auth.service';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { GrpcToHttpExceptionFilter } from '../../error/grpc-to-http-exception.filter';
@ApiTags('Auth')
@UseFilters(new GrpcToHttpExceptionFilter())
@Controller('auth')
export class AuthController {
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
+1 -1
View File
@@ -1,6 +1,6 @@
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { AuthController } from './auth.controller';
import { AuthClientService } from './auth.service';
+5 -5
View File
@@ -1,10 +1,10 @@
import { OnModuleInit, Inject, Injectable } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { lastValueFrom } from 'rxjs';
import { ProtoServices } from 'protospack-v2/dist/lib/Duc';
import { AuthProtoService as AuthServiceInterface } from 'protospack-v2/dist/lib/Duc/interfaces/write-service';
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
import { AuthProtoService as AuthServiceInterface } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import {
AuthSignInRequest,
AuthRefreshAccessTokenRequest,
@@ -16,11 +16,11 @@ import {
AuthResetPasswordRequest,
AuthVerifyResetPasswordCodeRequest,
AuthConfirmResetPasswordRequest,
} from 'protospack-v2/dist/lib/Duc/interfaces/messages';
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
@Injectable()
export class AuthClientService implements OnModuleInit {
logger: any;
logger: DadosferaLogger;
private authService: AuthServiceInterface;
constructor(
+6 -4
View File
@@ -10,9 +10,9 @@ import {
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { AuthenticateCondition } from '../../authentication/authentication.decorator';
import { Permissions } from '../../authentication/permissions.enum';
import { PERMISSIONS } from '../../authentication/permissions.enum';
import { CatalogService } from './catalog.service';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
@ApiTags('Catalog')
@Controller('catalog')
@@ -32,10 +32,12 @@ import { DadosferaLogger } from 'dadosfera-logs';
action = req.method;
}
return user.permissions.includes(Permissions.CATALOG[action].seqid);
return user.permissions.includes(
PERMISSIONS.CATALOG.permissions[action].seqid,
);
})
export class CatalogController {
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@@ -1,6 +1,6 @@
import { credentials } from '@grpc/grpc-js';
import { ClientOptions, Transport } from '@nestjs/microservices';
import { ConnectorManager } from 'protospack-v2';
import { ConnectorManager } from '@dadosfera/protospack-v2';
export class ConnectorClientConfiguration {
config(): ClientOptions {
@@ -12,10 +12,10 @@ export class ConnectorClientConfiguration {
ConnectorManager.ProtoPackages.WritePackage,
ConnectorManager.ProtoPackages.ReadPackage,
],
credentials:
process.env.LOCAL_ENV === 'local'
? undefined
: credentials.createSsl(),
// credentials: undefined,
credentials: process.env.LOCAL_ENV
? undefined
: credentials.createSsl(),
protoPath: [
ConnectorManager.ProtoPaths.WriteFilePath,
ConnectorManager.ProtoPaths.ReadFilePath,
+193
View File
@@ -0,0 +1,193 @@
import { Metadata } from '@grpc/grpc-js';
import {
OnModuleInit,
Inject,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { ConnectorManager } from '@dadosfera/protospack-v2';
import { lastValueFrom } from 'rxjs';
export class ConnectorClientService implements OnModuleInit {
private connectorServiceRead: ConnectorManager.ReadService.ConnectorManagerReadServices;
private connectorServiceWrite: ConnectorManager.WriteService.ConnectorManagerWriteServices;
constructor(
@Inject('CONNECTOR_PACKAGE') private readonly grpcClient: ClientGrpc,
) {}
onModuleInit() {
this.connectorServiceWrite =
this.grpcClient.getService<ConnectorManager.WriteService.ConnectorManagerWriteServices>(
ConnectorManager.ProtoServices.ConnectorManagerWriteServices,
);
this.connectorServiceRead =
this.grpcClient.getService<ConnectorManager.ReadService.ConnectorManagerReadServices>(
ConnectorManager.ProtoServices.ConnectorManagerReadServices,
);
}
async uploadConnector(uploadConnector) {
const connector: ConnectorManager.Entities.ConnectorCreateRequest = {
file: {
buffer: uploadConnector.file.buffer,
mimetypes: uploadConnector.file.mimetype,
},
connector: JSON.stringify(uploadConnector.connector),
};
const serviceBody: ConnectorManager.Messages.RegisterConnectorRequest = {
connector,
};
return lastValueFrom(
this.connectorServiceWrite.RegisterConnector(serviceBody),
).catch((err) => {
console.log(err.details);
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
}
async uploadFile(uploadFile) {
const body: ConnectorManager.Messages.UploadFileRequest = {
file: {
buffer: uploadFile.file.buffer,
mimetypes: uploadFile.file.mimetype,
},
name: uploadFile.name,
};
return lastValueFrom(this.connectorServiceWrite.UploadFile(body)).catch(
(err) => {
console.log(err.details);
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
},
);
}
async uploadConnectorsFile(uploadFile) {
const connectors = JSON.parse(uploadFile.file.buffer.toString('utf8'));
const body = connectors.map((connector) => {
return JSON.stringify(connector);
});
const request = {
connectors: body,
};
return lastValueFrom(
this.connectorServiceWrite.RegisterMultipleConnectorsWithoutImage(
request,
),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
}
async getAllConnectors(body) {
const meta = new Metadata();
meta.add('Language', body.language);
return lastValueFrom(
this.connectorServiceRead.GetAllConnectors(
{
search: body.search,
filters: JSON.stringify(body.filters),
language: body.language,
page: body.page,
size: body.size,
},
meta,
),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
}
async getConnectorDetails(plugin: string, language: string) {
const meta = new Metadata();
meta.add('Language', language);
return lastValueFrom(
this.connectorServiceRead.GetConnectorDetails(
{
plugin,
},
meta,
),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
}
async getConnector(plugin: string, language: string) {
const meta = new Metadata();
meta.add('Language', language);
return lastValueFrom(
this.connectorServiceRead.GetConnector(
{
plugin,
},
meta,
),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
}
async getConnectorsTags() {
return lastValueFrom(this.connectorServiceRead.GetConnectorsTags({})).catch(
(err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
},
);
}
async deleteConnector(plugin: string, version: string) {
return lastValueFrom(
this.connectorServiceWrite.DeleteConnector({
version,
plugin,
}),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
}
async updateConnector({ plugin, changes }) {
return lastValueFrom(
this.connectorServiceWrite.UpdateConnector({
plugin,
changes,
}),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
}
}
+61 -12
View File
@@ -11,24 +11,32 @@ import {
UploadedFile,
UseInterceptors,
Inject,
Headers,
UploadedFiles,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
import { ApiBearerAuth, ApiConsumes, ApiTags } from '@nestjs/swagger';
import { ConnectorClientService } from 'src/clients/connector/client.service';
import { ConnectorClientService } from './client.service';
import { AddTagDto } from './dtos/add-tag';
import { CreateConnectorDto } from './dtos/create-connector';
import { UploadConnectorsFileDto } from './dtos/create-multiple-connector';
import { DeleteConnectorDto } from './dtos/delete-connector';
import { GetAllDto } from './dtos/get-all';
import { RemoveTagDto } from './dtos/remove-tag';
import { UpdateDto } from './dtos/update';
import { UploadFileDto } from './dtos/upload-file';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
Authenticated,
RequireAllPermissions,
} from 'src/authentication/authentication.decorator';
import { PERMISSIONS } from 'src/authentication/permissions.enum';
@ApiTags('connectors')
@ApiBearerAuth()
@Authenticated()
@Controller('connectors')
export class ConnectorController {
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@@ -38,6 +46,7 @@ export class ConnectorController {
}
@Post()
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.CREATE)
@ApiConsumes('multipart/form-data')
@UseInterceptors(FileInterceptor('file'))
async uploadConnector(
@@ -54,29 +63,53 @@ export class ConnectorController {
return response;
}
@Post('/upload')
@Post('/uploads')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.CREATE)
@ApiConsumes('multipart/form-data')
@UseInterceptors(FilesInterceptor('files'))
async uploadFile(@UploadedFiles() files) {
this.logger.info('/upload - Upload Connector Route');
const promises = files.map(async (file) => {
return await this.connectorClientService.uploadFile({
file,
name: file.originalname.split('.')[0],
});
});
const response = await Promise.all(promises);
return response;
}
@Post('/multiple-connectors-upload')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.CREATE)
@ApiConsumes('multipart/form-data')
@UseInterceptors(FileInterceptor('file'))
async uploadFile(@UploadedFile() file, @Body() { name }: UploadFileDto) {
this.logger.info('/upload - Upload Connector Route');
async uploadConnectorsFile(@UploadedFile() file: UploadConnectorsFileDto) {
console.log(`/upload`, 'Upload Connector Route');
const response = await this.connectorClientService.uploadFile({
const response = await this.connectorClientService.uploadConnectorsFile({
file,
name,
});
return response;
}
@Get()
async getAllConnectors(@Query() queries: GetAllDto) {
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.GET)
async getAllConnectors(
@Headers('dadosfera-lang') language,
@Query() queries: GetAllDto,
) {
this.logger.info('/upload - Upload Connector Route');
const { search, ...filters } = queries;
const { search, size, page, ...filters } = queries;
const response: any = await this.connectorClientService.getAllConnectors({
filters: filters || {},
search: search || '',
language: language || 'pt-br',
size: size || 10,
page: page || 1,
});
const connectors = JSON.parse(response.connectors).connectors;
@@ -87,8 +120,16 @@ export class ConnectorController {
};
}
@Get('/tags')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.GET)
async getConnectorsTags() {
return await this.connectorClientService.getConnectorsTags();
}
@Get('/:plugin')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.GET)
async getConnector(
@Headers('dadosfera-lang') language,
@Param('plugin') plugin: string,
@Query('version') version: string,
) {
@@ -98,6 +139,7 @@ export class ConnectorController {
const response: any = await this.connectorClientService.getConnector(
pluginId,
language,
);
return {
@@ -107,7 +149,9 @@ export class ConnectorController {
}
@Get('/:plugin/details')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.GET)
async getConnectorDetails(
@Headers('dadosfera-lang') language,
@Param('plugin') plugin: string,
@Query('version') version: string,
) {
@@ -117,6 +161,7 @@ export class ConnectorController {
const response: any = await this.connectorClientService.getConnectorDetails(
pluginId,
language || 'pt-br',
);
return {
@@ -126,6 +171,7 @@ export class ConnectorController {
}
@Put('/:plugin')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.UPDATE)
@ApiConsumes('multipart/form-data')
async updateConnector(
@Param('plugin') plugin: string,
@@ -147,6 +193,7 @@ export class ConnectorController {
}
@Put('/:plugin/add-tag')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.UPDATE)
async addTagOnConnector(
@Param('plugin') plugin: string,
@Body() body: AddTagDto,
@@ -173,6 +220,7 @@ export class ConnectorController {
}
@Put('/:plugin/remove-tag')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.UPDATE)
async removeTagOnConnector(
@Param('plugin') plugin: string,
@Body() body: RemoveTagDto,
@@ -200,6 +248,7 @@ export class ConnectorController {
}
@Delete('/:plugin')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.DELETE)
async deleteConnector(
@Param('plugin') plugin: string,
@Body() { version }: DeleteConnectorDto,
+22
View File
@@ -0,0 +1,22 @@
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { ConnectorClientService } from './client.service';
import { ConnectorClientConfiguration } from './client.config';
import { ConnectorController } from './connector.controller';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
const connectorClient = new ConnectorClientConfiguration();
@Module({
controllers: [ConnectorController],
providers: [ConnectorClientService, DadosferaLogger],
imports: [
ClientsModule.register([
{
name: 'CONNECTOR_PACKAGE',
...connectorClient.config(),
},
]),
],
})
export class ConnectorModule {}
@@ -0,0 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
export class UploadConnectorsFileDto {
@ApiProperty({ format: 'binary' })
file: string;
}
+9
View File
@@ -4,6 +4,15 @@ export class GetAllDto {
@ApiProperty()
search: string;
@ApiProperty()
language: string;
@ApiProperty()
size: string;
@ApiProperty()
page: string;
@ApiProperty()
filters: any;
}
+2 -2
View File
@@ -1,12 +1,12 @@
import { Controller, Get, Inject } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { HealthService } from './health.service';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
@ApiTags('Health')
@Controller('health')
export class HealthController {
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
+2 -2
View File
@@ -1,9 +1,9 @@
import { Inject, Injectable } from '@nestjs/common';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
@Injectable()
export class HealthService {
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
+5 -5
View File
@@ -9,10 +9,10 @@ import {
Post,
} from '@nestjs/common';
import { InputsService } from './inputs.service';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { InputsClientService } from 'src/clients/inputs/client.service';
import { UpdateInputRequest } from 'src/clients/inputs/interfaces';
import { Permissions } from '../../authentication/permissions.enum';
import { PERMISSIONS } from '../../authentication/permissions.enum';
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import {
@@ -41,7 +41,7 @@ import {
break;
case 'PATCH':
action = 'UPDATE_PARTIAL';
action = 'UPDATE';
break;
default:
@@ -55,11 +55,11 @@ import {
action = 'GET_ENTITIES';
}
return user.permissions.includes(Permissions.INPUT[action].seqid);
return user.permissions.includes(PERMISSIONS.INPUT.permissions[action].seqid);
})
export class InputsController {
inputService: InputsService;
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
+3 -2
View File
@@ -7,7 +7,7 @@ import { IIdRequest, Info } from 'src/clients/inputs/interfaces';
@Injectable()
export class InputsService {
constructor(private inputClient: InputsClientService) { }
constructor(private inputClient: InputsClientService) {}
secondsInADay = 60 * 60 * 24;
secondsInAnHour = 60 * 60;
@@ -54,7 +54,7 @@ export class InputsService {
switch (data.plugin.toLowerCase()) {
case 'csv':
case 'json':
case 'parquet':
case 'parquet': {
const { info, ...input } = data;
const inputPayload = this.generateInputS3Payload(input);
response = await this.inputClient.createS3Inputs({
@@ -62,6 +62,7 @@ export class InputsService {
info,
});
break;
}
case 'oracle':
case 'mysql':
case 'postgresql':
+6 -4
View File
@@ -12,9 +12,9 @@ import { Payload } from '@nestjs/microservices';
import { ApiTags } from '@nestjs/swagger';
import { OutputsClientService } from 'src/clients/outputs/client.service';
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
import { Permissions } from '../../authentication/permissions.enum';
import { PERMISSIONS } from '../../authentication/permissions.enum';
import { OutputsService } from './outputs.service';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
@ApiTags('Outputs')
@Controller('outputs')
@@ -34,10 +34,12 @@ import { DadosferaLogger } from 'dadosfera-logs';
action = req.method;
}
return user.permissions.includes(Permissions.OUTPUT[action].seqid);
return user.permissions.includes(
PERMISSIONS.OUTPUT.permissions[action].seqid,
);
})
export class OutputsController {
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
+49
View File
@@ -0,0 +1,49 @@
import { ApiProperty } from '@nestjs/swagger';
export class PermissionGroupPermission {
@ApiProperty()
name: string;
@ApiProperty()
id: number;
}
export class PermissionGroup {
@ApiProperty()
title: string;
@ApiProperty({ type: [PermissionGroupPermission] })
permissions: PermissionGroupPermission[];
}
export class Permission {
@ApiProperty()
id: string;
@ApiProperty()
seqid: number;
@ApiProperty()
name: string;
@ApiProperty()
claim: string;
@ApiProperty()
createdAt: string;
@ApiProperty()
updatedAt: string;
}
export class PermissionDto {
@ApiProperty()
id: number;
@ApiProperty()
seqid: number;
@ApiProperty()
name: string;
@ApiProperty()
claim: string;
@ApiProperty()
createdAt: string;
@ApiProperty()
updatedAt: string;
}
export class GetPublicPermissionsRes {
@ApiProperty({ type: [PermissionGroup] })
permissionGroups: PermissionGroup[];
}
@@ -0,0 +1,42 @@
import { ClientsModule } from '@nestjs/microservices';
import { Test, TestingModule } from '@nestjs/testing';
import { DucClient } from 'src/clients/duc/client.config';
import { PermissionsController } from './permissions.controller';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { PermissionsService } from './permissions.service';
const ducClient = new DucClient();
const logger = {
info: (...args) => args,
error: (...args) => args,
};
describe('PermissionsController', () => {
let controller: PermissionsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ClientsModule.register([
{
name: 'DUC_PACKAGE',
...ducClient.config(),
},
]),
],
controllers: [PermissionsController],
providers: [
{
provide: DadosferaLogger,
useValue: { logger },
},
PermissionsService,
],
}).compile();
controller = module.get<PermissionsController>(PermissionsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
@@ -0,0 +1,51 @@
import { Controller, Get, Headers, Inject } from '@nestjs/common';
import { ApiHeader, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
Authenticated,
RequireAllPermissions,
} from 'src/authentication/authentication.decorator';
import {
PERMISSIONS,
PermissionUsages,
} from 'src/authentication/permissions.enum';
import { RequestUser, User } from 'src/authentication/user.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
import { GetPublicPermissionsRes } from './dto/entities';
import { PermissionsService } from './permissions.service';
@ApiTags('Permissions')
@Controller('permissions')
@ApiHeader({
name: 'dadosfera-lang',
enum: LanguageEnum,
required: false,
description: 'Defaults to pt-br',
})
@Authenticated()
export class PermissionsController {
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private permissionsService: PermissionsService,
) {
this.logger = dadosferaLogger.logger;
}
@Get()
@ApiOkResponse({ type: GetPublicPermissionsRes })
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
getPublicPermissions(
@User() user: RequestUser,
@Headers('dadosfera-lang') language,
) {
this.logger.info('getPublicPermissions', { user });
const permissionGroups = this.permissionsService.getPermissionsGrouped(
PermissionUsages.PUBLIC,
language,
);
return { permissionGroups };
}
}
@@ -1,10 +1,12 @@
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { PermissionsClientService } from './permissions.service';
import { PermissionsService } from './permissions.service';
import { DucClient } from '../../clients/duc/client.config';
import { PermissionsController } from './permissions.controller';
const ducClient = new DucClient();
@Module({
@@ -16,6 +18,8 @@ const ducClient = new DucClient();
},
]),
],
providers: [PermissionsClientService, DadosferaLogger],
providers: [PermissionsService, DadosferaLogger],
controllers: [PermissionsController],
exports: [PermissionsService],
})
export class PermissionsModule {}
+60 -12
View File
@@ -7,20 +7,27 @@ import {
import { ClientGrpc } from '@nestjs/microservices';
import { lastValueFrom } from 'rxjs';
import { ProtoServices } from 'protospack-v2/dist/lib/Duc';
import { PermissionsProtoService as PermissionsServiceInterface } from 'protospack-v2/dist/lib/Duc/interfaces/write-service';
import { InjectPermissionsRequest } from 'protospack-v2/dist/lib/Duc/interfaces/messages';
import { DadosferaLogger } from 'dadosfera-logs';
import { Permissions } from '../../authentication/permissions.enum';
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
import { PermissionsProtoService as PermissionsServiceInterface } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import { InjectPermissionsRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
PERMISSIONS,
PermissionUsages,
PermissionsObjectGroup,
PermissionsObjectPermissionTranslated,
} from '../../authentication/permissions.enum';
import ErrorBuilder from '../../utils/ErrorBuilder';
import { PermissionGroupPermission, PermissionGroup } from './dto/entities';
import { LanguageEnum } from 'src/utils/languages.enum';
@Injectable()
export class PermissionsClientService
export class PermissionsService
implements OnModuleInit, OnApplicationBootstrap
{
logger: any;
logger: DadosferaLogger;
private permissionsService: PermissionsServiceInterface;
private permissionsClientService: PermissionsServiceInterface;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@@ -30,7 +37,7 @@ export class PermissionsClientService
}
onModuleInit() {
this.permissionsService =
this.permissionsClientService =
this.grpcClient.getService<PermissionsServiceInterface>(
ProtoServices.PermissionsProtoService,
);
@@ -40,8 +47,8 @@ export class PermissionsClientService
async onApplicationBootstrap() {
this.logger.info('sending permissions to DUC...');
const permissions = Object.values(Permissions).flatMap((namespace) =>
Object.values(namespace),
const permissions = Object.values(PERMISSIONS).flatMap((namespace) =>
Object.values(namespace.permissions),
);
return this.injectPermissions({
@@ -64,7 +71,48 @@ export class PermissionsClientService
this.logger.info('InjectPermissions');
return lastValueFrom(
this.permissionsService.InjectPermissions({ permissions }),
this.permissionsClientService.InjectPermissions({ permissions }),
);
}
getPermissionsGrouped(
permissionUsage?: PermissionUsages,
language: LanguageEnum = LanguageEnum.ptbr,
): PermissionGroup[] {
const groups: PermissionGroup[] = [];
for (const key in PERMISSIONS) {
const permissionGroup: PermissionsObjectGroup = PERMISSIONS[key];
const title = permissionGroup.title[language];
const permissions: PermissionGroupPermission[] = Object.values(
permissionGroup.permissions,
)
.filter(({ usage }) =>
permissionUsage ? usage === permissionUsage : true,
)
.map(({ seqid, name }) => ({ id: seqid, name: name[language] }));
if (permissions?.length)
groups.push({
title,
permissions,
});
}
return groups;
}
getAllPermissions(language: LanguageEnum = LanguageEnum.ptbr) {
let permissions: PermissionsObjectPermissionTranslated[] = [];
for (const key in PERMISSIONS) {
const permissionGroup: PermissionsObjectGroup = PERMISSIONS[key];
const permissionGroupPermissions: PermissionsObjectPermissionTranslated[] =
Object.values(permissionGroup.permissions).map((p) => ({
...p,
name: p.name[language],
}));
permissions = permissions.concat(permissionGroupPermissions);
}
return permissions;
}
getPermissionsBySeqId(seqIds: number[]) {
const permissionGroupPermissions = this.getAllPermissions();
return permissionGroupPermissions.filter((p) => seqIds.includes(p.seqid));
}
}
@@ -11,9 +11,9 @@ import {
import { ApiTags } from '@nestjs/swagger';
import { PipelinesClientService } from 'src/clients/pipelines/client.service';
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
import { Permissions } from '../../authentication/permissions.enum';
import { PERMISSIONS } from '../../authentication/permissions.enum';
import { PipelinesService } from './pipelines.service';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
@ApiTags('Pipelines')
@Controller('pipelines')
@@ -33,10 +33,12 @@ import { DadosferaLogger } from 'dadosfera-logs';
action = req.method;
}
return user.permissions.includes(Permissions.PIPELINE[action].seqid);
return user.permissions.includes(
PERMISSIONS.PIPELINE.permissions[action].seqid,
);
})
export class PipelinesController {
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
+115
View File
@@ -0,0 +1,115 @@
import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger';
import { PermissionDto } from 'src/modules/permissions/dto/entities';
import {
Customer,
UserNoRolesAndCustomer,
} from 'src/modules/users/dtos/entities';
export class RoleDto {
@ApiProperty()
id: string;
@ApiProperty()
name: string;
@ApiProperty()
description: string;
@ApiPropertyOptional({ type: () => [PermissionDto] })
permissions: PermissionDto[];
@ApiPropertyOptional({ type: () => [UserNoRolesAndCustomer] })
users?: UserNoRolesAndCustomer[];
@ApiProperty()
createdAt: string;
@ApiProperty()
updatedAt: string;
@ApiProperty()
isPublic: boolean;
@ApiProperty({ type: () => Customer })
customer?: Customer;
}
export class CustomerRole extends OmitType(RoleDto, ['customer']) {}
export class UserRole extends OmitType(RoleDto, ['users']) {}
export enum TrueOrFalseEnum {
true = 'true',
false = 'false',
}
export class GetRolesByCustomerReq {
@ApiPropertyOptional()
permissionId: string[];
@ApiPropertyOptional({ enum: TrueOrFalseEnum })
getUsers: TrueOrFalseEnum;
}
export class GetRolesByCustomerRes {
@ApiProperty({ type: [RoleDto] })
roles: RoleDto[];
}
export class CreateRoleReq {
@ApiProperty()
name: string;
@ApiProperty()
permissionIds: number[];
@ApiPropertyOptional()
userIds: string[];
@ApiProperty()
description: string;
}
export class CreateRoleRes {
@ApiProperty()
role: CustomerRole;
}
export class GrantPermissionsToRoleReq {
@ApiProperty()
roleId: string;
@ApiProperty()
permissionIds: number[];
}
export class GrantPermissionsToRoleRes {
@ApiProperty()
role: RoleDto;
}
export class RevokePermissionsFromRoleReq {
@ApiProperty()
roleId: string;
@ApiProperty()
permissionIds: number[];
}
export class RevokePermissionsFromRoleRes {
@ApiProperty()
role: RoleDto;
}
export class UpdateRoleReq {
@ApiPropertyOptional()
name: string;
@ApiPropertyOptional()
description: string;
}
export class UpdateRoleRes {
@ApiProperty()
role: RoleDto;
}
export class SetRolePermissionsReq {
@ApiProperty()
roleId: string;
@ApiProperty()
permissionIds: number[];
}
export class SetRolePermissionsRes {
@ApiProperty()
role: RoleDto;
}
export class SetRoleUsersReq {
@ApiProperty()
roleId: string;
@ApiProperty()
userIds: string[];
}
export class SetRoleUsersRes {
@ApiProperty()
role: RoleDto;
}
@@ -0,0 +1,46 @@
import { ClientsModule } from '@nestjs/microservices';
import { Test, TestingModule } from '@nestjs/testing';
import { DucClient } from 'src/clients/duc/client.config';
import { RolesController } from './roles.controller';
import { RolesService } from './roles.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { PermissionsModule } from '../permissions/permissions.module';
const ducClient = new DucClient();
const logger = {
info: (...args) => args,
error: (...args) => args,
};
describe('RolesController', () => {
let controller: RolesController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ClientsModule.register([
{
name: 'DUC_PACKAGE',
...ducClient.config(),
},
]),
PermissionsModule,
],
controllers: [RolesController],
providers: [
RolesService,
{
provide: DadosferaLogger,
useValue: { logger },
},
],
exports: [RolesService],
}).compile();
controller = module.get<RolesController>(RolesController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
+186
View File
@@ -0,0 +1,186 @@
import {
Body,
Controller,
Delete,
Get,
Headers,
Inject,
Param,
Patch,
Post,
Put,
Query,
UseFilters,
} from '@nestjs/common';
import {
ApiCreatedResponse,
ApiHeader,
ApiOkResponse,
ApiTags,
} from '@nestjs/swagger';
import { RequestUser, User } from 'src/authentication/user.decorator';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { RolesService } from './roles.service';
import {
GrantPermissionsToRoleReq,
GrantPermissionsToRoleRes,
CreateRoleReq,
CreateRoleRes,
GetRolesByCustomerReq,
GetRolesByCustomerRes,
RevokePermissionsFromRoleReq,
RevokePermissionsFromRoleRes,
UpdateRoleReq,
UpdateRoleRes,
RoleDto,
SetRolePermissionsReq,
SetRolePermissionsRes,
SetRoleUsersRes,
SetRoleUsersReq,
} from './dto/entities';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { PERMISSIONS } from 'src/authentication/permissions.enum';
import {
Authenticated,
RequireAllPermissions,
} from 'src/authentication/authentication.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
@ApiTags('Roles')
@ApiHeader({
name: 'dadosfera-lang',
enum: LanguageEnum,
required: false,
description: 'Defaults to pt-br',
})
@UseFilters(new GrpcToHttpExceptionFilter())
@Authenticated()
@Controller('roles')
export class RolesController {
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private rolesService: RolesService,
) {
this.logger = dadosferaLogger.logger;
}
@Get()
@ApiOkResponse({ type: GetRolesByCustomerRes })
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
async searchRoles(
@User() user: RequestUser,
@Query() filters: GetRolesByCustomerReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('searchRoles', { user });
this.rolesService.setLanguage(language);
return await this.rolesService.roleSearch(filters, user);
}
@Post()
@ApiCreatedResponse({ type: CreateRoleRes })
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
async createRole(
@User() user: RequestUser,
@Body() body: CreateRoleReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('createRole', { user });
this.rolesService.setLanguage(language);
return await this.rolesService.createRole(body, user);
}
@Put('permissions')
@ApiOkResponse({ type: GrantPermissionsToRoleRes })
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
async grantPermissionsToRole(
@User() user: RequestUser,
@Body() body: GrantPermissionsToRoleReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('grantPermissionsToRole', { user });
this.rolesService.setLanguage(language);
return await this.rolesService.grantPermissionsToRole(body, user);
}
@Delete('permissions')
@ApiOkResponse({ type: RevokePermissionsFromRoleRes })
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
async revokePermissionsFromRole(
@User() user: RequestUser,
@Body() body: RevokePermissionsFromRoleReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('revokePermissionsToRole', { user });
this.rolesService.setLanguage(language);
return await this.rolesService.revokePermissionFromRole(body, user);
}
@Patch('permissions')
@ApiOkResponse({ type: SetRolePermissionsRes })
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
async setRolePermissions(
@User() user: RequestUser,
@Body() body: SetRolePermissionsReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('setRolePermissions', { user });
this.rolesService.setLanguage(language);
return this.rolesService.setRolePermissions(body, user);
}
@Patch('users')
@ApiOkResponse({ type: SetRoleUsersRes })
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
async setRoleUsers(
@User() user: RequestUser,
@Body() body: SetRoleUsersReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('setRoleUsers', { user });
this.rolesService.setLanguage(language);
return this.rolesService.setRoleUsers(body, user);
}
@Get(':id')
@ApiOkResponse({ type: RoleDto })
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
async getRoleById(
@User() user: RequestUser,
@Param('id') id: string,
@Headers('dadosfera-lang') language,
) {
this.logger.info('getRoleById', { user });
this.rolesService.setLanguage(language);
return await this.rolesService.getRolesById(id, user);
}
@Delete(':id')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
async deleteRole(
@User() user: RequestUser,
@Param('id') id: string,
@Headers('dadosfera-lang') language,
) {
this.logger.info('deleteRole', { user });
this.rolesService.setLanguage(language);
return await this.rolesService.removeRole(id, user);
}
@Patch(':id')
@ApiOkResponse({ type: UpdateRoleRes })
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
async updateRole(
@User() user: RequestUser,
@Param('id') id: string,
@Body() body: UpdateRoleReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('updateRole', { user });
this.rolesService.setLanguage(language);
return this.rolesService.updateRole(id, body, user);
}
}
+25
View File
@@ -0,0 +1,25 @@
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { DucClient } from 'src/clients/duc/client.config';
import { RolesController } from './roles.controller';
import { RolesService } from './roles.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { PermissionsModule } from '../permissions/permissions.module';
const ducClient = new DucClient();
@Module({
imports: [
ClientsModule.register([
{
name: 'DUC_PACKAGE',
...ducClient.config(),
},
]),
PermissionsModule,
],
controllers: [RolesController],
providers: [RolesService, DadosferaLogger],
exports: [RolesService],
})
export class RolesModule {}
+46
View File
@@ -0,0 +1,46 @@
import { ClientsModule } from '@nestjs/microservices';
import { Test, TestingModule } from '@nestjs/testing';
import { RolesController } from './roles.controller';
import { RolesService } from './roles.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { DucClient } from 'src/clients/duc/client.config';
import { PermissionsModule } from '../permissions/permissions.module';
const ducClient = new DucClient();
const logger = {
info: (...args) => args,
error: (...args) => args,
};
describe('RolesService', () => {
let service: RolesService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ClientsModule.register([
{
name: 'DUC_PACKAGE',
...ducClient.config(),
},
]),
PermissionsModule,
],
controllers: [RolesController],
providers: [
RolesService,
{
provide: DadosferaLogger,
useValue: { logger },
},
],
exports: [RolesService],
}).compile();
service = module.get<RolesService>(RolesService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
+260
View File
@@ -0,0 +1,260 @@
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
import { RolesProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import {
CreateRoleReq,
GetRolesByCustomerReq,
GrantPermissionsToRoleReq,
RevokePermissionsFromRoleReq,
RoleDto,
SetRolePermissionsReq,
SetRoleUsersReq,
UpdateRoleReq,
} from './dto/entities';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { Metadata } from '@grpc/grpc-js';
import { ClientGrpc } from '@nestjs/microservices';
import { Permission } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/entities';
import { lastValueFrom } from 'rxjs';
import { PermissionsService } from '../permissions/permissions.service';
import { LanguageEnum } from 'src/utils/languages.enum';
import { RequestUser } from 'src/authentication/user.decorator';
interface GetRolesPermissionsName {
id: string;
name: string;
description: string;
permissions: Permission[];
isPublic?: boolean;
createdAt: string;
updatedAt: string;
}
@Injectable()
export class RolesService {
logger: DadosferaLogger;
private language: LanguageEnum = LanguageEnum.ptbr;
private rolesClientService: RolesProtoService;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject('DUC_PACKAGE') private readonly grpcClient: ClientGrpc,
private permissionsService: PermissionsService,
) {
this.logger = dadosferaLogger.logger;
}
setLanguage(lang: LanguageEnum = LanguageEnum.ptbr) {
if (Object.values(LanguageEnum).includes(lang)) this.language = lang;
this.language = lang;
}
onModuleInit() {
this.rolesClientService = this.grpcClient.getService<RolesProtoService>(
ProtoServices.RolesProtoService,
);
}
async getRolesById(id: string, { access_token }: RequestUser) {
const meta = new Metadata();
meta.add('access_token', access_token);
const role = await lastValueFrom(
this.rolesClientService.RoleFindOneById(
{
id,
},
meta,
),
);
const [roleTreated] = this.getRolesPermissionsName([role.role]);
return { role: roleTreated };
}
async roleSearch(
filters: GetRolesByCustomerReq,
{ customer_id, access_token }: RequestUser,
) {
const meta = new Metadata();
meta.add('access_token', access_token);
let { permissionId: permissionIds } = filters;
if (permissionIds && !Array.isArray(permissionIds))
permissionIds = [permissionIds];
const { getUsers } = filters;
const isGetUsers = getUsers === 'true';
const permissionSeqIds =
permissionIds?.map((id) => {
const seqId = Number(id);
if (isNaN(seqId))
throw new BadRequestException(`permissionId ${id} not valid`);
return seqId;
}) || [];
const roles = await lastValueFrom(
this.rolesClientService.RoleSearch(
{
customerId: customer_id,
getUsers: isGetUsers,
permissionIds: [],
permissionSeqIds,
},
meta,
),
);
const rolesTreated = this.getRolesPermissionsName(roles.roles);
return { roles: rolesTreated };
}
async createRole(
data: CreateRoleReq,
{ customer_id, access_token }: RequestUser,
) {
const meta = new Metadata();
meta.add('access_token', access_token);
const { description, name, permissionIds, userIds } = data;
const role = await lastValueFrom(
this.rolesClientService.RoleCreate(
{
customerId: customer_id,
name,
permissionIds: [],
permissionSeqIds: permissionIds,
description,
userIds,
},
meta,
),
);
const [roleTreated] = this.getRolesPermissionsName([role.role]);
return { role: roleTreated };
}
async removeRole(id: string, { access_token }: RequestUser) {
const meta = new Metadata();
meta.add('access_token', access_token);
return lastValueFrom(this.rolesClientService.RoleRemove({ id }, meta));
}
async grantPermissionsToRole(
{ roleId, permissionIds }: GrantPermissionsToRoleReq,
{ access_token }: RequestUser,
) {
const meta = new Metadata();
meta.add('access_token', access_token);
const role = await lastValueFrom(
this.rolesClientService.GrantPermissionsToRole(
{
permissionIds: [],
roleId,
permissionSeqIds: permissionIds,
},
meta,
),
);
const [roleTreated] = this.getRolesPermissionsName([role.role]);
return { role: roleTreated };
}
async revokePermissionFromRole(
{ roleId, permissionIds }: RevokePermissionsFromRoleReq,
{ access_token }: RequestUser,
) {
const meta = new Metadata();
meta.add('access_token', access_token);
const role = await lastValueFrom(
this.rolesClientService.RevokePermissionsFromRole(
{
permissionIds: [],
roleId,
permissionSeqIds: permissionIds,
},
meta,
),
);
const [roleTreated] = this.getRolesPermissionsName([role.role]);
return { role: roleTreated };
}
async updateRole(
id: string,
req: UpdateRoleReq,
{ access_token }: RequestUser,
) {
const meta = new Metadata();
meta.add('access_token', access_token);
const { description, name } = req;
const role = await lastValueFrom(
this.rolesClientService.RoleUpdate(
{
id,
description,
name,
},
meta,
),
);
const [roleTreated] = this.getRolesPermissionsName([role.role]);
return { role: roleTreated };
}
async setRolePermissions(
req: SetRolePermissionsReq,
{ access_token }: RequestUser,
) {
const meta = new Metadata();
meta.add('access_token', access_token);
const { roleId: id, permissionIds } = req;
const clearPermissions =
Array.isArray(permissionIds) && permissionIds.length === 0;
const role = await lastValueFrom(
this.rolesClientService.RoleSetPermissions(
{
id,
permissionSeqIds: permissionIds,
clearPermissions,
},
meta,
),
);
const [roleTreated] = this.getRolesPermissionsName([role.role]);
return { role: roleTreated };
}
async setRoleUsers(req: SetRoleUsersReq, { access_token }: RequestUser) {
const meta = new Metadata();
meta.add('access_token', access_token);
const { roleId: id, userIds } = req;
const clearUsers = Array.isArray(userIds) && userIds.length === 0;
const role = await lastValueFrom(
this.rolesClientService.RoleSetUsers(
{
id,
userIds,
clearUsers,
},
meta,
),
);
const [roleTreated] = this.getRolesPermissionsName([role.role]);
return { role: roleTreated };
}
getRolesPermissionsName(roles: GetRolesPermissionsName[]): RoleDto[] {
const newRoles: RoleDto[] = [];
for (const role of roles) {
const allPermissions = this.permissionsService.getAllPermissions(
this.language,
);
const newPermissions = role.permissions.map((p) => {
const permission = allPermissions.find((per) => per.seqid === p.seqid);
return {
...p,
name: permission.name,
id: p.seqid,
};
});
const newRole: RoleDto = {
...role,
permissions: newPermissions,
isPublic: role.isPublic,
};
newRoles.push(newRole);
}
return newRoles;
}
}
@@ -0,0 +1,56 @@
import {
Body,
Controller,
Post,
Get,
HttpCode,
HttpStatus,
Inject,
Headers,
UseFilters,
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { TermsOfUseAcceptRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { TermsOfUseClientService } from './termsOfUse.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { GrpcToHttpExceptionFilter } from '../../error/grpc-to-http-exception.filter';
import { RequestUser, User } from '../../authentication/user.decorator';
@ApiTags('TermsOfUse')
@UseFilters(new GrpcToHttpExceptionFilter())
@Controller('terms-of-use')
export class TermsOfUseController {
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private termsOfUseClient: TermsOfUseClientService,
) {
this.logger = dadosferaLogger.logger;
}
@Get('token')
async getToken(@User({ required: true }) user: RequestUser) {
this.logger.info('/terms-of-use - get token');
return this.termsOfUseClient.getToken({ userId: user.user_id });
}
@Post('accept')
@HttpCode(HttpStatus.OK)
async accept(@Body() body: TermsOfUseAcceptRequest, @Headers() headers) {
this.logger.info('/terms-of-use - accept');
const { token } = body;
const fingerprint = {
ip: process.env.ENV !== 'local' ? headers['x-forwarded-for'] : '0.0.0.0',
port: process.env.ENV !== 'local' ? headers['x-forwarded-port'] : '00000',
userAgent: headers['user-agent'],
};
return this.termsOfUseClient.accept({ token, fingerprint });
}
}
@@ -0,0 +1,24 @@
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { TermsOfUseController } from './termsOfUse.controller';
import { TermsOfUseClientService } from './termsOfUse.service';
import { DucClient } from '../../clients/duc/client.config';
const ducClient = new DucClient();
@Module({
imports: [
ClientsModule.register([
{
name: 'DUC_PACKAGE',
...ducClient.config(),
},
]),
],
controllers: [TermsOfUseController],
providers: [TermsOfUseClientService, DadosferaLogger],
exports: [TermsOfUseClientService],
})
export class TermsOfUseModule {}
@@ -0,0 +1,40 @@
import { OnModuleInit, Inject, Injectable } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { lastValueFrom } from 'rxjs';
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
import { TermsOfUseProtoService as TermsOfUseServiceInterface } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import {
TermsOfUseGetTokenRequest,
TermsOfUseAcceptRequest,
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
@Injectable()
export class TermsOfUseClientService implements OnModuleInit {
logger: DadosferaLogger;
private termsOfUseService: TermsOfUseServiceInterface;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject('DUC_PACKAGE') private readonly grpcClient: ClientGrpc,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.termsOfUseService =
this.grpcClient.getService<TermsOfUseServiceInterface>(
ProtoServices.TermsOfUseProtoService,
);
}
async getToken(data: TermsOfUseGetTokenRequest) {
return lastValueFrom(this.termsOfUseService.TermsOfUseGetToken(data));
}
async accept(data: TermsOfUseAcceptRequest) {
return lastValueFrom(this.termsOfUseService.TermsOfUseAccept(data));
}
}
@@ -13,9 +13,9 @@ import { ApiTags } from '@nestjs/swagger';
import { TransformationsClientService } from 'src/clients/transformations/client.service';
import { IIdRequest } from 'src/clients/transformations/interfaces';
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
import { Permissions } from '../../authentication/permissions.enum';
import { PERMISSIONS } from '../../authentication/permissions.enum';
import { TransformationsService } from './transformations.service';
import { DadosferaLogger } from 'dadosfera-logs';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
@ApiTags('Transformations')
@Controller('transformations')
@@ -35,10 +35,12 @@ import { DadosferaLogger } from 'dadosfera-logs';
action = req.method;
}
return user.permissions.includes(Permissions.TRANSFORMATIONS[action].seqid);
return user.permissions.includes(
PERMISSIONS.TRANSFORMATIONS.permissions[action].seqid,
);
})
export class TransformationsController {
logger: any;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
+171
View File
@@ -0,0 +1,171 @@
import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger';
import { UserRole } from 'src/modules/roles/dto/entities';
export class Hierarchy {
@ApiProperty()
id: string;
@ApiProperty()
name: string;
}
export class Customer {
@ApiProperty()
id: string;
@ApiProperty()
name: string;
@ApiProperty()
domain: string;
@ApiProperty()
tier: string;
@ApiProperty()
info: string;
@ApiProperty()
createdAt: string;
@ApiProperty()
updatedAt: string;
}
export class User {
@ApiProperty()
id: string;
@ApiProperty()
name: string;
@ApiProperty()
email: string;
@ApiProperty({ type: [UserRole] })
roles: UserRole[];
@ApiProperty()
jobTitle?: string;
@ApiProperty()
department?: string;
@ApiProperty()
hierarchy?: string;
@ApiPropertyOptional()
customer?: Customer;
@ApiProperty()
mfaStatus: string;
@ApiProperty()
createdAt: string;
@ApiProperty()
updatedAt: string;
@ApiProperty()
status: string;
@ApiPropertyOptional()
lastLogin?: string;
}
export class UserNoRoles extends OmitType(User, ['roles']) {}
export class UserNoRolesAndCustomer extends OmitType(UserNoRoles, [
'customer',
]) {}
export class UserByCustomer extends OmitType(User, ['customer']) {}
export class CreateUserReq {
@ApiPropertyOptional()
name?: string;
@ApiProperty()
email: string;
@ApiPropertyOptional()
roleNames?: string[];
@ApiPropertyOptional()
jobTitle?: string;
@ApiPropertyOptional()
department?: string;
@ApiPropertyOptional()
hierarchy?: string;
}
export class CreateUserRes {
@ApiProperty()
user: User;
}
export class BatchCreateUserReq {
@ApiProperty({ type: [CreateUserReq] })
users: CreateUserReq[];
}
class ErrorUsers {
@ApiProperty()
user: CreateUserReq;
@ApiProperty()
error: string;
}
export class BatchCreateUserRes {
@ApiProperty({ type: [User] })
usersCreated: User[];
@ApiProperty({ type: [ErrorUsers] })
errorUsers: ErrorUsers[];
}
export class UpdateUserReq {
@ApiPropertyOptional()
name?: string;
@ApiPropertyOptional()
jobTitle?: string;
@ApiPropertyOptional()
department?: string;
@ApiPropertyOptional()
hierarchy?: string;
@ApiPropertyOptional()
roleNames?: string[];
}
export class UpdateUserRes {
@ApiProperty()
user: UserByCustomer;
}
export class GetAllUsersByCustomerIdRes {
@ApiProperty({ type: [UserByCustomer] })
users: UserByCustomer[];
@ApiProperty()
departments: string[];
@ApiProperty()
jobTitles: string[];
}
export class AssignRoleToUserReq {
@ApiProperty()
userId: string;
@ApiProperty()
roleId: string;
}
export class UnassignRoleToUserReq {
@ApiProperty()
userId: string;
@ApiProperty()
roleId: string;
}
export class DeleteUserRes {
@ApiProperty()
id: string;
}
export class SearchUserReq {
@ApiProperty()
s: string;
}
export class SetUserRolesReq {
@ApiProperty()
userId: string;
@ApiProperty()
roleNames: string[];
}
export class SetUserRolesRes {
@ApiProperty()
user: UserByCustomer;
}
export class SearchUserRes extends GetAllUsersByCustomerIdRes {}
export class GetAllHierarchiesRes {
@ApiProperty({ type: [Hierarchy] })
hierarchies: Hierarchy[];
}
export class GetAllJobTitlesRes {
@ApiProperty()
jobTitles: string[];
}
export class GetAllDepartmentsRes {
@ApiProperty()
departments: string[];
}
+79
View File
@@ -0,0 +1,79 @@
import { LanguageEnum } from 'src/utils/languages.enum';
interface InternalHierarchyType {
name: Record<LanguageEnum, string>;
}
export const HIERARCHIES: Record<string, InternalHierarchyType> = {
presidence: {
name: {
'pt-br': 'Presidência',
'en-us': 'Presidence',
},
},
directory: {
name: {
'pt-br': 'Diretoria',
'en-us': 'Directory',
},
},
management: {
name: {
'pt-br': 'Gerência',
'en-us': 'Management',
},
},
specialist: {
name: {
'pt-br': 'Especialista',
'en-us': 'Specialist',
},
},
supervisor: {
name: {
'pt-br': 'Coordenador ou Supervisor',
'en-us': 'Supervisor',
},
},
trainee: {
name: {
'pt-br': 'Trainee',
'en-us': 'Trainee',
},
},
'senior-analyst': {
name: {
'pt-br': 'Analista Sênior',
'en-us': 'Senior Analyst',
},
},
'midlevel-analyst': {
name: {
'pt-br': 'Analista Pleno',
'en-us': 'Mid-level Analyst',
},
},
'junior-analyst': {
name: {
'pt-br': 'Analista Júnior',
'en-us': 'Junior Analyst',
},
},
intern: {
name: {
'pt-br': 'Estagiário',
'en-us': 'Intern',
},
},
'youth-apprentice': {
name: {
'pt-br': 'Jovem aprendiz',
'en-us': 'Youth Apprentice',
},
},
other: {
name: {
'pt-br': 'Outro',
'en-us': 'Other',
},
},
};
@@ -0,0 +1,47 @@
import { ClientsModule } from '@nestjs/microservices';
import { Test, TestingModule } from '@nestjs/testing';
import { DucClient } from 'src/clients/duc/client.config';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { RolesModule } from '../roles/roles.module';
import { PermissionsModule } from '../permissions/permissions.module';
const ducClient = new DucClient();
const logger = {
info: (...args) => args,
error: (...args) => args,
};
describe('UsersController', () => {
let controller: UsersController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ClientsModule.register([
{
name: 'DUC_PACKAGE',
...ducClient.config(),
},
]),
RolesModule,
PermissionsModule,
],
controllers: [UsersController],
providers: [
UsersService,
{
provide: DadosferaLogger,
useValue: { logger },
},
],
}).compile();
controller = module.get<UsersController>(UsersController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
+238
View File
@@ -0,0 +1,238 @@
import {
Body,
Controller,
Delete,
Get,
Headers,
HttpCode,
HttpStatus,
Inject,
Param,
Patch,
Post,
Put,
Req,
UseFilters,
} from '@nestjs/common';
import {
ApiCreatedResponse,
ApiHeader,
ApiOkResponse,
ApiTags,
} from '@nestjs/swagger';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { RequireAllPermissions } from 'src/authentication/authentication.decorator';
import { PERMISSIONS } from 'src/authentication/permissions.enum';
import { RequestUser, User } from 'src/authentication/user.decorator';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { LanguageEnum } from 'src/utils/languages.enum';
import {
AssignRoleToUserReq,
BatchCreateUserReq,
BatchCreateUserRes,
CreateUserReq,
CreateUserRes,
DeleteUserRes,
GetAllDepartmentsRes,
GetAllHierarchiesRes,
GetAllJobTitlesRes,
GetAllUsersByCustomerIdRes,
SetUserRolesReq,
SetUserRolesRes,
UnassignRoleToUserReq,
UpdateUserReq,
UpdateUserRes,
UserByCustomer,
} from './dtos/entities';
import { UsersService } from './users.service';
// TODO GET de hierarquias e do PATCH em usuário
@ApiTags('Users')
@Controller('users')
@ApiHeader({
name: 'dadosfera-lang',
enum: LanguageEnum,
required: false,
description: 'Defaults to pt-br',
})
@UseFilters(GrpcToHttpExceptionFilter)
export class UsersController {
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private userService: UsersService,
) {
this.logger = dadosferaLogger.logger;
}
@Get()
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
@ApiOkResponse({
type: GetAllUsersByCustomerIdRes,
description: 'List of users of a customer',
})
async getAllUsersByCustomerId(
@User() user: RequestUser,
@Headers('dadosfera-lang') language,
): Promise<GetAllUsersByCustomerIdRes> {
this.logger.info('getAllUsersByCustomerId', { user });
this.userService.setLanguage(language);
return await this.userService.findAllUsersByCustomerId(user.customer_id);
}
@Get('hierarchies')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
@ApiOkResponse({ type: GetAllHierarchiesRes })
async getAllHierarchies(
@User() user: RequestUser,
@Headers('dadosfera-lang') language,
): Promise<GetAllHierarchiesRes> {
this.logger.info('getAllHierarchies', { user });
this.userService.setLanguage(language);
return await this.userService.getAllHierarchies();
}
@Get('departments')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
@ApiOkResponse({ type: GetAllDepartmentsRes })
async getAllDepartments(
@User() user: RequestUser,
@Headers('dadosfera-lang') language,
): Promise<GetAllDepartmentsRes> {
this.logger.info('getAllHierarchies', { user });
this.userService.setLanguage(language);
return await this.userService.getAllDepartments(user.customer_id);
}
@Get('jobTitles')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
@ApiOkResponse({ type: GetAllDepartmentsRes })
async getAllJobTitles(
@User() user: RequestUser,
@Headers('dadosfera-lang') language,
): Promise<GetAllJobTitlesRes> {
this.logger.info('getAllHierarchies', { user });
this.userService.setLanguage(language);
return await this.userService.getAllJobTitles(user.customer_id);
}
@Get(':id')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
@ApiOkResponse({
type: UserByCustomer,
})
async findOneById(
@User() user: RequestUser,
@Param('id') id: string,
@Headers('dadosfera-lang') language,
) {
this.logger.info('findOneById', { user });
this.userService.setLanguage(language);
return await this.userService.findOneById(id);
}
@Post()
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
@ApiCreatedResponse({ type: CreateUserRes })
async createUser(
@User() user: RequestUser,
@Body() body: CreateUserReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('createUser', { user });
this.userService.setLanguage(language);
return await this.userService.createUser(body);
}
@Post('batch')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
@ApiCreatedResponse({ type: BatchCreateUserRes })
async batchCreateUser(
@User() user: RequestUser,
@Body() body: BatchCreateUserReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('batchCreateUser', { user });
this.userService.setLanguage(language);
return await this.userService.batchCreateUser(body);
}
@Post(':id/resend-invite')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
@HttpCode(HttpStatus.OK)
async resendInvite(
@User() user: RequestUser,
@Param('id') id: string,
@Headers('dadosfera-lang') language,
) {
this.logger.info('resendInvite', { user });
this.userService.setLanguage(language);
return this.userService.resendInvite({ id });
}
@Delete(':id')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
@ApiOkResponse({ type: DeleteUserRes })
async deleteUser(
@User() user: RequestUser,
@Param('id') id: string,
@Headers('dadosfera-lang') language,
) {
this.logger.info('deleteUser', { user });
this.userService.setLanguage(language);
return await this.userService.deleteUser({ id });
}
@Patch('roles')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
@ApiOkResponse({ type: SetUserRolesRes })
async setUserRoles(
@User() user: RequestUser,
@Body() body: SetUserRolesReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('createUser', { user });
this.userService.setLanguage(language);
return await this.userService.setRoles(body, user.customer_id);
}
@Put('role')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
async assignRoleToUser(
@User() user: RequestUser,
@Body() body: AssignRoleToUserReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('assignRoleToUser', { user });
this.userService.setLanguage(language);
return await this.userService.assignRoleToUser(body);
}
@Delete('role')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
async unassignRoleToUser(
@User() user: RequestUser,
@Req() request: UnassignRoleToUserReq,
@Headers('dadosfera-lang') language,
) {
this.logger.info('unassignRoleToUser', { user });
this.userService.setLanguage(language);
return await this.userService.unassignRoleToUser(request);
}
@Patch(':id')
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
@ApiOkResponse({ type: UpdateUserRes })
async updateUser(
@User() user: RequestUser,
@Body() body: UpdateUserReq,
@Param('id') id: string,
@Headers('dadosfera-lang') language,
) {
this.logger.info('updateUser', { user });
this.userService.setLanguage(language);
return await this.userService.updateUser(body, id, user.customer_id);
}
}
+26
View File
@@ -0,0 +1,26 @@
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { ClientsModule } from '@nestjs/microservices';
import { DucClient } from 'src/clients/duc/client.config';
import { RolesModule } from '../roles/roles.module';
import { PermissionsModule } from '../permissions/permissions.module';
const ducClient = new DucClient();
@Module({
imports: [
ClientsModule.register([
{
name: 'DUC_PACKAGE',
...ducClient.config(),
},
]),
RolesModule,
PermissionsModule,
],
controllers: [UsersController],
providers: [UsersService, DadosferaLogger],
})
export class UsersModule {}
+47
View File
@@ -0,0 +1,47 @@
import { ClientsModule } from '@nestjs/microservices';
import { Test, TestingModule } from '@nestjs/testing';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { DucClient } from 'src/clients/duc/client.config';
import { RolesModule } from '../roles/roles.module';
import { PermissionsModule } from '../permissions/permissions.module';
const ducClient = new DucClient();
const logger = {
info: (...args) => args,
error: (...args) => args,
};
describe('UsersService', () => {
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ClientsModule.register([
{
name: 'DUC_PACKAGE',
...ducClient.config(),
},
]),
RolesModule,
PermissionsModule,
],
controllers: [UsersController],
providers: [
UsersService,
{
provide: DadosferaLogger,
useValue: { logger },
},
],
}).compile();
service = module.get<UsersService>(UsersService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
+202
View File
@@ -0,0 +1,202 @@
import { OnModuleInit, Inject, Injectable } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
import { UsersProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import {
AssignRoleToUserRequest,
IdRequest,
UnassignRoleFromUserRequest,
UserCreateRequest,
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { lastValueFrom } from 'rxjs';
import {
BatchCreateUserReq,
CreateUserReq,
Hierarchy,
SetUserRolesReq,
UpdateUserReq,
} from './dtos/entities';
import { RolesService } from '../roles/roles.service';
import { HIERARCHIES } from './hierarchies';
import { LanguageEnum } from 'src/utils/languages.enum';
import { UserByCustomer } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/entities';
import { EnrichErrorCode } from 'src/utils/ErrorBuilder';
@Injectable()
export class UsersService implements OnModuleInit {
logger: DadosferaLogger;
language: LanguageEnum = LanguageEnum.ptbr;
private usersClientService: UsersProtoService;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject('DUC_PACKAGE') private readonly grpcClient: ClientGrpc,
private rolesService: RolesService,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.usersClientService = this.grpcClient.getService<UsersProtoService>(
ProtoServices.UsersProtoService,
);
}
setLanguage(l: LanguageEnum = LanguageEnum.ptbr) {
if (Object.values(LanguageEnum).includes(l)) this.language = l;
this.rolesService.setLanguage(l);
}
async findAllUsersByCustomerId(customerId: string) {
const { users } = await lastValueFrom(
this.usersClientService.UserFindAllByCustomerId({ customerId }),
);
const usersAdj = this.adjustUsersPayload(users);
const dintinctDepartments = [...new Set(usersAdj.map((u) => u.department))]
.filter((value) => value != null)
.sort();
const dintinctJobTitles = [...new Set(usersAdj.map((u) => u.jobTitle))]
.filter((value) => value != null)
.sort();
return {
users: usersAdj,
jobTitles: dintinctJobTitles,
departments: dintinctDepartments,
};
}
async findOneById(id: string) {
const { user } = await lastValueFrom(
this.usersClientService.UserFindOneById({ id }),
);
return { user: this.adjustUsersPayload([user])[0] };
}
async createUser(req: CreateUserReq) {
const { email, department, hierarchy, jobTitle, name, roleNames } = req;
const { user } = await lastValueFrom(
this.usersClientService.UserCreate({
email,
hierarchy,
name,
roleIds: [],
department,
jobTitle,
roleNames,
}),
);
return { user: this.adjustUsersPayload([user])[0] };
}
async updateUser(req: UpdateUserReq, id: string, customerId: string) {
const { department, hierarchy, jobTitle, name, roleNames } = req;
if (roleNames) {
await this.setRoles({ roleNames, userId: id }, customerId);
}
const { user } = await lastValueFrom(
this.usersClientService.UserUpdate({
name,
customerId,
id,
department,
hierarchy,
jobTitle,
}),
);
return { user: this.adjustUsersPayload([user])[0] };
}
async setRoles(body: SetUserRolesReq, customerId: string) {
const { roleNames, userId } = body;
const clearRoles = roleNames && roleNames.length === 0;
const { user } = await lastValueFrom(
this.usersClientService.UserSetRoles({
userId,
roleIds: [],
roleNames,
clearRoles,
customerId,
}),
);
return { user: this.adjustUsersPayload([user])[0] };
}
async batchCreateUser({ users }: BatchCreateUserReq) {
const usersToCreate: UserCreateRequest[] = users.map(
({ roleNames, email, department, hierarchy, jobTitle, name }) => ({
roleNames,
email,
department,
hierarchy,
jobTitle,
name,
roleIds: [],
}),
);
const { errorUsers, usersCreated } = await lastValueFrom(
this.usersClientService.UserBatchCreate({
users: usersToCreate,
}),
);
return {
usersCreated: this.adjustUsersPayload(usersCreated),
// mapping error code to message
errorUsers: errorUsers.map(({ user, error }) => ({
user,
error: EnrichErrorCode(error).message,
})),
};
}
async resendInvite(body: IdRequest) {
return lastValueFrom(this.usersClientService.UserResendInvite(body));
}
async assignRoleToUser(body: AssignRoleToUserRequest) {
return lastValueFrom(this.usersClientService.AssignRoleToUser(body));
}
async unassignRoleToUser(body: UnassignRoleFromUserRequest) {
return lastValueFrom(this.usersClientService.UnassignRoleFromUser(body));
}
async deleteUser(id: IdRequest) {
return lastValueFrom(this.usersClientService.UserRemove(id));
}
getAllHierarchies(): { hierarchies: Hierarchy[] } {
const hierarchies = Object.keys(HIERARCHIES).map((key) => ({
id: key,
name: HIERARCHIES[key]?.name[this.language] || key,
}));
return { hierarchies };
}
async getAllDepartments(customerId?: string) {
return lastValueFrom(
this.usersClientService.FindDistinctDepartments({ customerId }),
);
}
async getAllJobTitles(customerId?: string) {
return lastValueFrom(
this.usersClientService.FindDistinctJobTitles({ customerId }),
);
}
private adjustUsersPayload(users: UserByCustomer[]) {
return users.map((u) => {
if (!u) return undefined;
return {
...u,
roles: this.rolesService.getRolesPermissionsName(u.roles),
hierarchy: HIERARCHIES[u.hierarchy]?.name[this.language] || u.hierarchy,
};
});
}
}
+70 -4
View File
@@ -86,6 +86,22 @@ export function EnrichErrorCode(code: string) {
code,
};
case ErrorCodes.TERMS_OF_USE.UP_TO_DATE:
return {
statusCode: HttpStatus.FORBIDDEN,
error: 'Não autorizado',
message: 'Você já assinou os termos de uso',
code,
};
case ErrorCodes.TERMS_OF_USE.REQUIRED:
return {
statusCode: HttpStatus.FORBIDDEN,
error: 'Não autorizado',
message: 'É necessário aceitar os termos de uso para prosseguir',
code,
};
case ErrorCodes.RATE_LIMIT:
return {
statusCode: HttpStatus.TOO_MANY_REQUESTS,
@@ -112,6 +128,49 @@ export function EnrichErrorCode(code: string) {
code,
};
case ErrorCodes.ROLE.ALREADY_EXIST:
return {
statusCode: HttpStatus.BAD_REQUEST,
error: 'Grupo já existe',
message: 'Não é possível criar um grupo com esse nome',
code,
};
case ErrorCodes.ROLE.NOT_FOUND:
case ErrorCodes.ROLE.FROM_ANOTHER_CUSTOMER:
return {
statusCode: HttpStatus.NOT_FOUND,
error: 'Grupo não encontrado',
message: 'Grupo solicitado não encontrado',
code: ErrorCodes.ROLE.NOT_FOUND,
};
case ErrorCodes.ROLE.CANNOT_EDIT_PUBLIC_ROLE:
return {
statusCode: HttpStatus.FORBIDDEN,
error: 'Grupo público',
message: 'Você não tem permissão para editar esse grupo',
code,
};
case ErrorCodes.USER.NOT_FOUND:
return {
statusCode: HttpStatus.NOT_FOUND,
error: 'Usuário não encontrado',
message: 'Usuário solicitado não encontrado',
code,
};
case ErrorCodes.USER.ALREADY_EXISTS:
return {
statusCode: HttpStatus.NOT_FOUND,
error: 'Usuário encontrado',
message: 'Nome de usuário já existente',
code,
};
case ErrorCodes.USER.INVALID_ID:
return {
statusCode: HttpStatus.NOT_FOUND,
error: 'Id inválido',
message: 'Id encontra-se num formato inválido',
code,
};
case ErrorCodes.INTERNAL:
case ErrorCodes.UNKNOWN:
default:
@@ -121,6 +180,7 @@ export function EnrichErrorCode(code: string) {
message:
'Erro desconhecido. Tente novamente ou entre em contato com o suporte',
code: ErrorCodes.UNKNOWN,
details: code,
};
}
}
@@ -130,16 +190,22 @@ const logger = new Logger();
export default class ErrorBuilder extends HttpException {
code: string;
constructor(code: string | RpcException) {
constructor(code: string | RpcException = '') {
if (typeof code !== 'string') {
logger.log(code.stack);
logger.log(code?.stack);
code = (code as any).details as string;
} else {
logger.log(code);
}
const { statusCode, message, error, code: rCode } = EnrichErrorCode(code);
super({ statusCode, message, error, code: rCode }, statusCode);
const {
statusCode,
message,
error,
code: rCode,
details,
} = EnrichErrorCode(code);
super({ statusCode, message, error, code: rCode, details }, statusCode);
this.code = code;
}
}
+28
View File
@@ -13,12 +13,40 @@ export const AUTH = {
RESET_PASSWORD_CODE_INVALID: 'AUTH.RESET_PASSWORD_CODE_INVALID',
WEAK_NEW_PASSWORD: 'AUTH.WEAK_NEW_PASSWORD',
};
export const ROLE = {
ALREADY_EXIST: 'ROLE.ALREADY_EXISTS',
NOT_FOUND: 'ROLE.NOT_FOUND',
FROM_ANOTHER_CUSTOMER: 'ROLE.FROM_ANOTHER_CUSTOMER',
CANNOT_EDIT_PUBLIC_ROLE: 'ROLE.CANNOT_EDIT_PUBLIC_ROLE',
};
export const USER = {
INVALID_ID: 'USER.INVALID_ID',
NOT_FOUND: 'USER.NOT_FOUND',
ALREADY_EXISTS: 'USER.ALREADY_EXISTS',
ROLE_ALREADY_ASSIGNED: 'USER.ROLE_ALREADY_ASSIGNED',
ROLE_NOT_ASSIGNED: 'USER.ROLE_NOT_ASSIGNED',
};
export const CUSTOMER = {
INVALID_ID: 'CUSTOMER.INVALID_ID',
NOT_FOUND: 'CUSTOMER.NOT_FOUND',
ALREADY_EXISTS: 'CUSTOMER.ALREADY_EXISTS',
};
const TERMS_OF_USE = {
UP_TO_DATE: 'TERMS_OF_USE.UP_TO_DATE',
REQUIRED: 'TERMS_OF_USE.REQUIRED',
};
const ErrorCodes = {
UNKNOWN: 'UNKNOWN',
RATE_LIMIT: 'RATE_LIMIT',
INTERNAL: 'INTERNAL',
AUTH,
ROLE,
TERMS_OF_USE,
USER,
CUSTOMER,
};
export default ErrorCodes;
+4
View File
@@ -0,0 +1,4 @@
export enum LanguageEnum {
ptbr = 'pt-br',
enus = 'en-us',
}