mirror of
https://github.com/dadosfera/maestro.git
synced 2026-09-01 04:08:16 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5122dbde85 | ||
|
|
dcb3094890 | ||
|
|
c8e4e90100 | ||
|
|
09cff9fa15 | ||
|
|
79b624cea6 | ||
|
|
5356334258 | ||
|
|
5f6e4e88a5 | ||
|
|
32ab834834 | ||
|
|
ebbfe0abff | ||
|
|
d76a2b4ab8 | ||
|
|
047737eadd | ||
|
|
e20ae71672 | ||
|
|
97b6047cf1 | ||
|
|
9c6d2b499a | ||
|
|
f43c270c33 | ||
|
|
b0ccb39f2c | ||
|
|
9f900882cb | ||
|
|
a24abed9ed | ||
|
|
b04a38bcc0 | ||
|
|
67706c5fc3 | ||
|
|
0fa4dcb933 | ||
|
|
04284e1feb | ||
|
|
3e7581e20c | ||
|
|
c61c37b268 | ||
|
|
5ce8099c25 | ||
|
|
9cdb0a5d38 | ||
|
|
57f47d3b2e | ||
|
|
4ab955a7a1 | ||
|
|
1ca3694b75 | ||
|
|
a6a35c507a | ||
|
|
cfcb8c1c42 | ||
|
|
a55da8e20f | ||
|
|
82310c6314 | ||
|
|
52f9d06ad0 | ||
|
|
a83f91a720 | ||
|
|
ccef7628f9 | ||
|
|
a5e424c854 | ||
|
|
339432df16 | ||
|
|
7dfa10d5ee | ||
|
|
c87ceec77b | ||
|
|
071c524a2e | ||
|
|
1de4eedd38 | ||
|
|
29276d6070 | ||
|
|
344a251de1 | ||
|
|
3b95032554 | ||
|
|
a0089b5d5d | ||
|
|
1b620b6fb0 | ||
|
|
1eb76d4b1c | ||
|
|
314bc5a562 | ||
|
|
d01b0357ed | ||
|
|
a206aad8db | ||
|
|
de57bfed1e | ||
|
|
14cfc46492 | ||
|
|
9d10e58b1b | ||
|
|
6dec2c1e56 | ||
|
|
23a65d18a6 | ||
|
|
d5cd562860 | ||
|
|
f5064420a7 | ||
|
|
bb5befb2f6 | ||
|
|
398360fadb | ||
|
|
13072978f5 | ||
|
|
b9612609d9 | ||
|
|
e7b37629a1 | ||
|
|
5b6994b247 | ||
|
|
039f837914 | ||
|
|
83dcc7942c | ||
|
|
5c079bb4f5 | ||
|
|
f383cf49d5 | ||
|
|
db78ee3d8b | ||
|
|
730fab8ed1 | ||
|
|
04e1e1ff2b | ||
|
|
d309c20cbf | ||
|
|
4cff593695 | ||
|
|
03d42df3fd | ||
|
|
ea04f59e04 | ||
|
|
d1961a9710 | ||
|
|
358dd6a15a | ||
|
|
cc3b34d87e | ||
|
|
88bc7fe625 | ||
|
|
137f129e13 | ||
|
|
f49c5ac1c1 | ||
|
|
d288a4f357 | ||
|
|
9f009309cb | ||
|
|
395db05b05 | ||
|
|
2d69e6db70 | ||
|
|
9514134db1 | ||
|
|
0c69cebca1 | ||
|
|
9791b8bdb0 | ||
|
|
6803ab3420 | ||
|
|
6aa7be98d2 | ||
|
|
f5665557c9 | ||
|
|
366987399e | ||
|
|
4356adea65 | ||
|
|
4c49dbc0ea | ||
|
|
59ffb24985 | ||
|
|
1cd191e970 | ||
|
|
7e92ca2c36 | ||
|
|
12af510d36 | ||
|
|
61871958aa | ||
|
|
64d84f9148 | ||
|
|
2c099ba491 | ||
|
|
65b2578f15 | ||
|
|
68e5534edb | ||
|
|
51c7bdfebf | ||
|
|
9f6d2c7c9c | ||
|
|
02955b99fe | ||
|
|
93505d3268 | ||
|
|
e5bf5b23e7 | ||
|
|
0d20b4dcab | ||
|
|
bb5cf0494d | ||
|
|
c058e54c90 | ||
|
|
e82166f2c7 | ||
|
|
8d23eaeea2 | ||
|
|
f8b495036d | ||
|
|
b03b8f3b08 | ||
|
|
aaf99b8c2e | ||
|
|
426ff2141f | ||
|
|
8060cd769f | ||
|
|
ac021ea7e1 | ||
|
|
89eed11561 | ||
|
|
abe78bff9b | ||
|
|
3a26c5bebf | ||
|
|
2af0b3af52 | ||
|
|
c28bc74b3f | ||
|
|
7eae88b5cd | ||
|
|
1e66065a21 | ||
|
|
1e407f081e | ||
|
|
31c507d667 | ||
|
|
a39622bfa1 | ||
|
|
0895922b1c | ||
|
|
1cc094d40b | ||
|
|
d13aa2b72a | ||
|
|
a98ec4bf32 | ||
|
|
d5b1cb51b4 | ||
|
|
5d3510639e | ||
|
|
bc0d456c2a | ||
|
|
b658eacce6 | ||
|
|
9c124be191 | ||
|
|
dd4a1ecd46 | ||
|
|
6eaaa746d0 | ||
|
|
df79fbdab0 | ||
|
|
0f34152b02 | ||
|
|
08d576432e | ||
|
|
ce0362217c | ||
|
|
3399f125e5 | ||
|
|
9e0a58b4b7 | ||
|
|
49a9c93d6f | ||
|
|
ddb81d3082 | ||
|
|
9d9d903335 | ||
|
|
3ddbfcebeb | ||
|
|
e1d0aa00f5 | ||
|
|
4209b5b23e | ||
|
|
d6607b1d9f | ||
|
|
0eef0713df | ||
|
|
17d629b430 | ||
|
|
5362a99b24 | ||
|
|
1566efc490 | ||
|
|
91aefe7d61 | ||
|
|
b584c80e24 | ||
|
|
74a6eb8132 | ||
|
|
7012a6e2ac | ||
|
|
2f5d940b46 | ||
|
|
06a0ed3299 | ||
|
|
f08e9dc1a8 | ||
|
|
a0c19b587c | ||
|
|
3fe91ad4e8 | ||
|
|
07f2d56b9b | ||
|
|
1668d55679 | ||
|
|
498ea61c06 | ||
|
|
07f123e528 | ||
|
|
e439823319 | ||
|
|
55fb1a8b34 | ||
|
|
8ce7a692b6 | ||
|
|
c7997ed8aa | ||
|
|
89b571d7f8 | ||
|
|
7776d61f82 | ||
|
|
0e2f935807 | ||
|
|
9bf7cc660e | ||
|
|
d6552678a8 | ||
|
|
9e25a1ffa7 | ||
|
|
44ceee0671 | ||
|
|
a42ec164c3 | ||
|
|
6abc2595cb | ||
|
|
b850596d64 | ||
|
|
da3b7f3096 | ||
|
|
3c660738f4 | ||
|
|
ff2b8fae6f | ||
|
|
44337dc4ed | ||
|
|
80e7aa7b9c | ||
|
|
6f4bb9ce6d | ||
|
|
4df23c917a | ||
|
|
92afbb5e55 | ||
|
|
6c3eef7181 | ||
|
|
072a9fee8e | ||
|
|
953c0ac41e | ||
|
|
8c91551cdc | ||
|
|
668c5fe6a5 | ||
|
|
d057495b2e | ||
|
|
2f1c71eefe | ||
|
|
3bb014e714 | ||
|
|
2ff2aa049f | ||
|
|
f6c7420f59 | ||
|
|
abbe88f45b | ||
|
|
383741f48c | ||
|
|
ff663e7166 | ||
|
|
305daeeeab | ||
|
|
852ddbf88b | ||
|
|
4dfe6c3870 | ||
|
|
2710159a73 | ||
|
|
1ab5d49908 | ||
|
|
43372b5a0f | ||
|
|
0ac2dd2de7 | ||
|
|
0342386a76 | ||
|
|
28e55f8fd9 | ||
|
|
4e2e296442 | ||
|
|
d89573bc79 | ||
|
|
03ed4d88a0 | ||
|
|
329bbf2e4d | ||
|
|
ae069a0e00 | ||
|
|
d3bcbdc43c |
@@ -0,0 +1,3 @@
|
||||
container_commands:
|
||||
01setup_swap:
|
||||
command: "bash .ebextensions/setup_swap.sh"
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
SWAPFILE=/var/swapfile
|
||||
SWAP_MEGABYTES=1024
|
||||
|
||||
if [ -f $SWAPFILE ]; then
|
||||
echo "Swapfile $SWAPFILE found, assuming already setup"
|
||||
exit;
|
||||
fi
|
||||
|
||||
/bin/dd if=/dev/zero of=$SWAPFILE bs=1M count=$SWAP_MEGABYTES
|
||||
/bin/chmod 600 $SWAPFILE
|
||||
/sbin/mkswap $SWAPFILE
|
||||
/sbin/swapon $SWAPFILE
|
||||
@@ -6,6 +6,7 @@ module.exports = {
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
|
||||
@@ -35,9 +35,7 @@ jobs:
|
||||
semantic_release:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
#new_release_published: ${{ steps.semantic.outputs.last_release_version != steps.semantic.outputs.new_release_version }}
|
||||
new_release_published: ${{ steps.semantic.outputs.new_release_published }}
|
||||
#new_release_version: ${{ steps.semantic.outputs.new_release_version }}
|
||||
new_release_version: ${{ (steps.semantic.outputs.new_release_published == 'true' && steps.semantic.outputs.new_release_version) || (github.event_name == 'workflow_dispatch' && '0.0.0') }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -57,7 +55,6 @@ jobs:
|
||||
]
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
deploy:
|
||||
if: ${{ github.event_name == 'workflow_dispatch' || needs.semantic_release.outputs.new_release_published == 'true' }}
|
||||
@@ -107,9 +104,8 @@ jobs:
|
||||
ENV: ${{ needs.extract_environment.outputs.environment }}
|
||||
IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
|
||||
ACCOUNT_ID: ${{ steps.aws.outputs.aws-account-id }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: |
|
||||
docker-compose -f build.docker-compose.yml build --build-arg NPM_TOKEN=${NPM_TOKEN}
|
||||
docker-compose -f build.docker-compose.yml build
|
||||
docker-compose -f build.docker-compose.yml push
|
||||
|
||||
- name: Create ZIP file to Deploy AWS Beanstalk
|
||||
@@ -134,7 +130,7 @@ jobs:
|
||||
eb deploy
|
||||
|
||||
- name: Remove Docker's Trash
|
||||
continue-on-error: true
|
||||
if: always()
|
||||
run: |
|
||||
docker system prune
|
||||
docker rmi -f $(docker images -aq)
|
||||
docker system prune --volumes -a -f
|
||||
docker system df
|
||||
|
||||
@@ -2,8 +2,6 @@ name: Test
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- alpha
|
||||
- beta
|
||||
- main
|
||||
|
||||
jobs:
|
||||
@@ -13,34 +11,21 @@ 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
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
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 --build-arg NPM_TOKEN=${NPM_TOKEN}
|
||||
docker build -t $APP_NAME --target test .
|
||||
|
||||
- name: Run Test
|
||||
env:
|
||||
ENV: test
|
||||
IMAGE_TAG: test
|
||||
ACCOUNT_ID: test
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
APP_NAME: ${{ github.event.repository.name }}
|
||||
run: |
|
||||
export ENV=test
|
||||
export IMAGE_TAG=test
|
||||
docker-compose -f build.docker-compose.yml run -e NPM_TOKEN=${NPM_TOKEN} --rm --entrypoint="npm run test" maestro
|
||||
docker run --rm --entrypoint="npm" $APP_NAME run test
|
||||
|
||||
- name: Remove Docker's Trash
|
||||
continue-on-error: true
|
||||
if: always()
|
||||
run: |
|
||||
docker system prune
|
||||
docker rmi -f $(docker images -aq)
|
||||
docker system prune --volumes -a -f
|
||||
docker system df
|
||||
|
||||
+6
-7
@@ -5,18 +5,18 @@
|
||||
{
|
||||
"preset": "eslint",
|
||||
"releaseRules": [
|
||||
{ "tag": "FIX", "release": "patch" },
|
||||
{ "tag": "BREAKING", "release": "major" },
|
||||
{ "tag": "FEAT", "release": "minor" },
|
||||
{ "tag": "UPDATE", "release": "minor" },
|
||||
{ "tag": "UPGRADE", "release": "minor" },
|
||||
{ "tag": "BREAKING", "release": "major" },
|
||||
{ "tag": "DOCS", "release": "patch" },
|
||||
{ "tag": "CHORE", "release": "minor" },
|
||||
{ "tag": "BUILD", "release": "patch" },
|
||||
{ "tag": "REFACTOR", "release": "minor" },
|
||||
{ "tag": "PERF", "release": "minor" },
|
||||
{ "tag": "FIX", "release": "patch" },
|
||||
{ "tag": "DOCS", "release": "patch" },
|
||||
{ "tag": "BUILD", "release": "patch" },
|
||||
{ "tag": "TEST", "release": "patch" },
|
||||
{ "tag": "CI", "release": "patch" },
|
||||
{ "tag": "PERF", "release": "minor" }
|
||||
{ "tag": "CI", "release": "patch" }
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -26,7 +26,6 @@
|
||||
"preset": "eslint"
|
||||
}
|
||||
],
|
||||
"@semantic-release/npm",
|
||||
"@semantic-release/github"
|
||||
]
|
||||
}
|
||||
|
||||
+16
-11
@@ -1,16 +1,21 @@
|
||||
FROM node:14.15.4-alpine3.12 as packages
|
||||
WORKDIR /packages
|
||||
ARG NPM_TOKEN
|
||||
COPY package.json .
|
||||
COPY .npmrc .
|
||||
RUN npm install
|
||||
RUN rm -f ./.npmrc
|
||||
# install all dependencies
|
||||
FROM node:18.3.0-alpine3.15 as packages
|
||||
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
|
||||
|
||||
FROM node:14.15.4-alpine3.12
|
||||
# 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/
|
||||
ARG NPM_TOKEN
|
||||
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
|
||||
ENTRYPOINT npm run start
|
||||
|
||||
Binary file not shown.
@@ -1,50 +1,301 @@
|
||||
# Maestro
|
||||
<p align="center">
|
||||
<image src="./assets/maestro.svg" style="width:10rem">
|
||||
<h1 align="center">Maestro</h1>
|
||||
</p>
|
||||
This is the Dadosfera´s gateway repository, it´s responsable for the communication between frontend application and Dadosfera´s mirosservices.
|
||||
|
||||
## 💻 Requirements
|
||||
# Maestro
|
||||
Maestro is the Dadosfera's gateway, it's responsible for the communication between the frontend application and Dadosfera's microservices.
|
||||
|
||||
Before you start, make sure you have done the following steps:
|
||||
## 🚀 Starting
|
||||
These instructions will allow you to get a working copy of the project on your local machine for development and testing purposes.
|
||||
|
||||
* Installed Nodejs version 16.14.2
|
||||
* Installed latest NPM version
|
||||
### 📋 Requirements
|
||||
- [NodeJS v18.3.0 LTS / NPM v8.11](https://nodejs.org/pt-br/download/) (you can opt to use [NVM](https://github.com/nvm-sh/nvm) to easily manage node versions)
|
||||
- Request access to AWS Console dev account for **all services** (avoid gradually asking for each needed service. it will slow down your development cycle)
|
||||
- Create your Access Key on the "Security credentials" menu
|
||||
- Set the Access Key on your local development machine
|
||||
- Request access to the dev, stg and prd VPNs
|
||||
|
||||
### 🔧 Installation<a id="installation"></a>
|
||||
- Clone the repository
|
||||
- SSH
|
||||
```
|
||||
git clone git@github.com:dadosfera/maestro.git
|
||||
```
|
||||
or
|
||||
|
||||
- HTTPS
|
||||
```
|
||||
git clone https://github.com/dadosfera/maestro.git
|
||||
```
|
||||
|
||||
## 🚀 Installing Maestro
|
||||
- Select the correct node version (optional, only if using [NVM](https://github.com/nvm-sh/nvm)):
|
||||
```sh
|
||||
nvm use
|
||||
```
|
||||
|
||||
First of all clone the repository:
|
||||
* SSH:
|
||||
```
|
||||
git clone git@github.com:dadosfera/maestro.git
|
||||
```
|
||||
* HTTPS:
|
||||
```
|
||||
git clone git@github.com:dadosfera/maestro.git
|
||||
- Install the project dependencies:
|
||||
```sh
|
||||
npm i
|
||||
```
|
||||
|
||||
- Setup the following enviroment variables:
|
||||
```
|
||||
ENV=
|
||||
DUC_URL=
|
||||
INFACTORY_URL=
|
||||
OTFACTORY_URL=
|
||||
PIFACTORY_URL=
|
||||
SM_OAUTH_PATH=
|
||||
```
|
||||
|
||||
- Start the server:
|
||||
```sh
|
||||
# dev mode
|
||||
npm run start:dev
|
||||
|
||||
# or in debug mode
|
||||
npm run start:debug
|
||||
```
|
||||
The service should start successfully.
|
||||
|
||||
## Authentication decorators
|
||||
Maestro have utilities to ease the user authentication on every controller and route. The following decorators are available:
|
||||
|
||||
### `@Authenticated`
|
||||
If the user must be authenticated to make request, we can use the `@Authenticated` decorator in the controller or route, as needed.
|
||||
|
||||
```ts
|
||||
import { Authenticated } from '../../authentication/authentication.decorator';
|
||||
|
||||
@Controller('foo')
|
||||
@Authenticated()
|
||||
class FooController {
|
||||
/* ... */
|
||||
|
||||
@Get('bar')
|
||||
async getBar() {
|
||||
this.logger.info('user is authenticated!');
|
||||
|
||||
return { authenticated: true };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Enviroment variables
|
||||
```ts
|
||||
import { Authenticated } from '../../authentication/authentication.decorator';
|
||||
|
||||
Here is a list of enviroment variables needed in order to run the application correctly.
|
||||
@Controller('foo')
|
||||
class FooController {
|
||||
/* ... */
|
||||
|
||||
```
|
||||
ENV=
|
||||
DUC_URL=
|
||||
INFACTORY_URL=
|
||||
TRFACTORY_URL=
|
||||
OTFACTORY_URL=
|
||||
PIFACTORY_URL=
|
||||
JWT_PRIVATE_KEY=
|
||||
AWS_IDENTITY_POOL_ID=
|
||||
@Get('bar')
|
||||
@Authenticated()
|
||||
async getBar() {
|
||||
this.logger.info('user is authenticated!');
|
||||
|
||||
return { authenticated: true };
|
||||
}
|
||||
|
||||
@Post('bar')
|
||||
async postBar() {
|
||||
this.logger.info('user is NOT authenticated!');
|
||||
|
||||
return { authenticated: false };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Running Maestro
|
||||
### `@RequireAllPermissions`
|
||||
This decorator requires that **all permissions** listed are granted to the requesting user.
|
||||
|
||||
In order to run Maestro just run the following command:
|
||||
```ts
|
||||
import { RequireAllPermissions } from '../../authentication/authentication.decorator';
|
||||
import { Permissions } from '../../authentication/permissions.enum';
|
||||
|
||||
@Controller('foo')
|
||||
class FooController {
|
||||
/* ... */
|
||||
|
||||
@Get('bar')
|
||||
@RequireAllPermissions(Permissions.BAR.MANAGE, Permissions.BAR.CREATE)
|
||||
async getBar() {
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
```
|
||||
npm run start:dev
|
||||
|
||||
### `@RequireSomePermission`
|
||||
In the following case, the user is required to have **at least one** listed permission.
|
||||
|
||||
```ts
|
||||
import { RequireSomePermission } from '../../authentication/authentication.decorator';
|
||||
import { Permissions } from '../../authentication/permissions.enum';
|
||||
|
||||
@Controller('foo')
|
||||
@RequireSomePermission(Permissions.FOO.MANAGE, Permissions.FOO.CREATE)
|
||||
class FooController {
|
||||
/* ... */
|
||||
|
||||
@Get('bar')
|
||||
async getBar() {
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
```
|
||||
If everything is fine the Maestro will start and be ready to receive HTTP requests
|
||||
|
||||
### `@AuthenticateCondition`
|
||||
If a more complicated authentication check needs to be done, we can use the `@AuthenticateCondition` decorator to define it. The custom function must return `true` to authenticate the request.
|
||||
|
||||
In the following example:
|
||||
- all routes on the `FooController` controller can only be requested from localhost
|
||||
- `POST /foo/bar` can only be requested from localhost **and** by users from customer id `111...eef`
|
||||
|
||||
```ts
|
||||
import { AuthenticateCondition } from '../../authentication/authentication.decorator';
|
||||
|
||||
@Controller('foo')
|
||||
// allow requests only from localhost
|
||||
@AuthenticateCondition((request: Request) => request.ip === '::ffff:127.0.0.1')
|
||||
class FooController {
|
||||
/* ... */
|
||||
|
||||
@Get('bar')
|
||||
async getBar() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
@Post('bar')
|
||||
// allow requests only from a specific customer
|
||||
@AuthenticateCondition((request: Request, user: RequestUser) => user.customer_id === '1113e943-2187-4fdd-9c2c-54338fedaeef')
|
||||
async postBar() {
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Note on authentication decorators
|
||||
- The old authentication method placed the user data in the `request.body.info` field, this imposes certain issues regarding the request body because this data should be from the frontend without any modification by Maestro. Now this usage is ⚠️ **DEPRECATED** ⚠️. We are working to migrate to the `@User` parameter decorator. The old method is working while the migration is in progress.
|
||||
|
||||
- Authentication decorators can be used together and all of them **must** pass to the request be authenticated, but in the general case you don't need to (*and wouldn't like to...*) use all of them together, as you can code all of the authentication logic in the `@AuthenticateCondition` decorator.
|
||||
|
||||
```ts
|
||||
import {
|
||||
RequireAllPermissions,
|
||||
RequireSomePermission,
|
||||
AuthenticateCondition,
|
||||
} from '../../authentication/authentication.decorator';
|
||||
import { Permissions } from '../../authentication/permissions.enum';
|
||||
import { Waa, Baz } from './authenticationFunctions'
|
||||
|
||||
@Controller('foo')
|
||||
@RequireAllPermissions(Permissions.FOO.USE, Permissions.FOO.REQUEST)
|
||||
@RequireSomePermission(Permissions.FOO.MANAGE, Permissions.FOO.ADMIN)
|
||||
@AuthenticateCondition(Waa)
|
||||
@AuthenticateCondition(Baz)
|
||||
class FooController {
|
||||
/* ... */
|
||||
|
||||
@Get('bar')
|
||||
async getBar() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
@Post('bar')
|
||||
@RequireAllPermissions(Permissions.BAR.MANAGE, Permissions.BAR.USE, Permissions.BAR.REQUEST)
|
||||
async postBar() {
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- If `@RequireAllPermissions` and `@RequireSomePermission` are used with **only a single permission**, they present the **exactly same behavior**.
|
||||
|
||||
```ts
|
||||
// same behavior
|
||||
@RequireAllPermissions(Permissions.BAR.MANAGE)
|
||||
@RequireSomePermission(Permissions.BAR.MANAGE)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## `@User` parameter decorator
|
||||
The requesting user data can be obtained using the @User parameter decorator, like in the following snippet:
|
||||
|
||||
```ts
|
||||
import { User, RequestUser } from '../../authentication/user.decorator';
|
||||
|
||||
@Controller('foo')
|
||||
class FooController {
|
||||
/* ... */
|
||||
|
||||
@Get('bar')
|
||||
async getBar(@User() user: RequestUser) {
|
||||
this.logger.info(user);
|
||||
|
||||
return { user };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the user is required to be logged in, set `required` to `true`, as you would want in the `change-password` operation:
|
||||
|
||||
```ts
|
||||
import { User, RequestUser } from '../../authentication/user.decorator';
|
||||
|
||||
@Controller('user')
|
||||
class UserController {
|
||||
/* ... */
|
||||
|
||||
@Post('change-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async changePassword(
|
||||
@User({ required: true }) user: RequestUser,
|
||||
@Body() body: AuthChangePasswordRequest,
|
||||
) {
|
||||
const { oldPassword, newPassword } = body;
|
||||
|
||||
return this.authClient.changePassword({
|
||||
userId: user.user_id,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📦 Development
|
||||
### ⌨️ Coding Style
|
||||
By default, we use [ESLint](https://eslint.org/) + [Prettier](https://prettier.io/) with default settings.
|
||||
|
||||
**We recommend using Visual Studio Code and installing the recommended extensions to ease the development process.**
|
||||
|
||||
### Commits pattern
|
||||
Our workflow pipeline follows the conventional commits specs ([cheat sheets](https://cheatography.com/albelop/cheat-sheets/conventional-commits/)) to release versions accordingly.
|
||||
|
||||
Format: `<type>[optional scope]: <description>`
|
||||
|
||||
Example: `FIX: ensure Range headers adhere more closely to RFC 2616`
|
||||
|
||||
### Branching naming convention
|
||||
- **Feature**: Any code changes for a new module or use case should be done on a feature branch. This branch is created based on the `main` branch. When all changes are done, a Pull Request/Merge Request is needed to put all of these changes back to the `main` branch. Examples: `feature/integrate-swagger`, `feature/JIRA-1234`, `feature/JIRA-1234_support-dark-theme`.
|
||||
|
||||
**It is recommended to use all lower caps letters and hyphen (-) to separate words unless it is a specific item name or ID. Underscore (_) could be used to separate the ID and description.**
|
||||
|
||||
- **Bug Fix**: If the code changes made from the feature branch were rejected after a release, sprint or demo, any necessary fixes after that should be done on the bugfix branch. Examples: `bugfix/more-gray-shades`, `bugfix/JIRA-1444_gray-on-blur-fix`.
|
||||
|
||||
- **Hot Fix**: If there is a need to fix a blocker, do a temporary patch, apply a critical framework or configuration change that should be handled immediately, it should be created as a Hotfix. Examples: `hotfix/disable-endpoint-zero-day-exploit`, `hotfix/increase-scaling-threshold`.
|
||||
|
||||
- **Experimental**: A branch for playing around. Any new feature or idea that is not part of a release or a sprint. Example: `experimental/dark-theme-support`.
|
||||
|
||||
### Making a Pull Request
|
||||
1. Commit your changes
|
||||
2. Open the Pull Request on GitHub
|
||||
3. Send Pull Request link in microsfera Google Chat Group for review and possible approval
|
||||
|
||||
## 🛠️ Built with
|
||||
Some technologies used in this project:
|
||||
|
||||
- [NestJS](https://docs.nestjs.com) - Framework for building efficient and scalable NodeJS server-side applications
|
||||
|
||||
## ⚙️ Back-end Architecture
|
||||
The architecture can be found at [this link](https://sites.google.com/dadosfera.ai/wikidoproduto/time/back-end).
|
||||
|
||||
+11
-2
@@ -2,7 +2,16 @@
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"assets": ["**/*.proto"],
|
||||
"assets": [
|
||||
"**/*.proto",
|
||||
{
|
||||
"include": "i18n/**/*",
|
||||
"watchAssets": true
|
||||
}
|
||||
],
|
||||
"plugins": [
|
||||
"@nestjs/swagger"
|
||||
],
|
||||
"watchAssets": true
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+6574
-4225
File diff suppressed because it is too large
Load Diff
+52
-27
@@ -1,10 +1,13 @@
|
||||
{
|
||||
"name": "grpc-server-and-client-nestjs-micro-pattern",
|
||||
"name": "maestro",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"engines": {
|
||||
"node": "18.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "nest build",
|
||||
@@ -21,53 +24,69 @@
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.5.10",
|
||||
"@grpc/proto-loader": "^0.6.9",
|
||||
"@nestjs/common": "^8.4.3",
|
||||
"@aws-sdk/client-secrets-manager": "^3.112.0",
|
||||
"@grpc/grpc-js": "^1.6.7",
|
||||
"@grpc/proto-loader": "^0.6.13",
|
||||
"@nestjs/common": "^8.4.7",
|
||||
"@nestjs/config": "^1.2.1",
|
||||
"@nestjs/core": "^8.4.3",
|
||||
"@nestjs/core": "^8.4.7",
|
||||
"@nestjs/mapped-types": "*",
|
||||
"@nestjs/microservices": "^8.4.3",
|
||||
"@nestjs/platform-express": "^8.4.3",
|
||||
"@nestjs/microservices": "^8.4.7",
|
||||
"@nestjs/passport": "^8.2.2",
|
||||
"@nestjs/platform-express": "^8.4.7",
|
||||
"@nestjs/schedule": "^1.1.0",
|
||||
"@nestjs/swagger": "^5.2.1",
|
||||
"@victorradael/protospack": "2.4.2-1",
|
||||
"axios": "^0.25.0",
|
||||
"dotenv": "^14.2.0",
|
||||
"helmet": "^5.0.2",
|
||||
"cron-parser": "^4.4.0",
|
||||
"@dadosfera/dadosfera-logs": "^1.0.0-beta.3",
|
||||
"dotenv": "^14.3.2",
|
||||
"elastic-apm-node": "^3.36.0",
|
||||
"helmet": "^5.1.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jwk-to-pem": "^2.0.5",
|
||||
"passport": "^0.6.0",
|
||||
"passport-facebook": "^3.0.0",
|
||||
"passport-forcedotcom": "^0.1.5",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-hubspot-oauth2": "^1.0.3",
|
||||
"passport-mailchimp": "^1.1.0",
|
||||
"protospack": "2.5.2",
|
||||
"@dadosfera/protospack-v2": "3.10.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.5.5",
|
||||
"swagger-ui-express": "^4.3.0"
|
||||
"swagger-ui-express": "^4.4.0"
|
||||
},
|
||||
"overrides":{
|
||||
"busboy@<1.6.0":"1.6.0"
|
||||
"overrides": {
|
||||
"multer": "1.4.5-lts.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^8.2.4",
|
||||
"@nestjs/schematics": "^8.0.8",
|
||||
"@nestjs/testing": "^8.4.3",
|
||||
"@nestjs/cli": "^8.2.8",
|
||||
"@nestjs/schematics": "^8.0.11",
|
||||
"@nestjs/testing": "^8.4.7",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/jwk-to-pem": "^2.0.1",
|
||||
"@types/node": "^16.11.26",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^16.11.41",
|
||||
"@types/passport-facebook": "^2.1.11",
|
||||
"@types/passport-google-oauth20": "^2.0.11",
|
||||
"@types/passport-oauth2": "^1.4.11",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"@typescript-eslint/eslint-plugin": "^5.17.0",
|
||||
"@typescript-eslint/parser": "^5.17.0",
|
||||
"eslint": "^8.12.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.29.0",
|
||||
"@typescript-eslint/parser": "^5.29.0",
|
||||
"eslint": "^8.18.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.5.1",
|
||||
"nock": "^13.2.4",
|
||||
"prettier": "^2.6.1",
|
||||
"nock": "^13.2.7",
|
||||
"prettier": "^2.7.1",
|
||||
"source-map-support": "^0.5.20",
|
||||
"supertest": "^6.1.3",
|
||||
"ts-jest": "^27.1.4",
|
||||
"ts-loader": "^9.2.7",
|
||||
"ts-node": "^10.7.0",
|
||||
"supertest": "^6.2.3",
|
||||
"ts-jest": "^27.1.5",
|
||||
"ts-loader": "^9.3.1",
|
||||
"ts-node": "^10.8.1",
|
||||
"tsconfig-paths": "^3.14.1",
|
||||
"typescript": "^4.6.3"
|
||||
},
|
||||
@@ -77,7 +96,13 @@
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"rootDir": ".",
|
||||
"modulePaths": [
|
||||
"<rootDir>"
|
||||
],
|
||||
"moduleDirectories": [
|
||||
"node_modules"
|
||||
],
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
|
||||
+40
-30
@@ -1,38 +1,48 @@
|
||||
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ClientsModule } from '@nestjs/microservices';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
import { LoggerMiddleware } from './middlewares/authentication';
|
||||
|
||||
import { InputsController } from './modules/inputs/inputs.controller';
|
||||
import { TransformationsController } from './modules/transformations/transformations.controller';
|
||||
import { OutputsController } from './modules/outputs/outputs.controllers';
|
||||
import { PipelinesController } from './modules/pipelines/pipelines.controller';
|
||||
import { AuthController } from './modules/auth/auth.controller';
|
||||
import { HealthController } from './modules/health/health.controller';
|
||||
|
||||
import { InputsService } from './modules/inputs/inputs.service';
|
||||
import { TransformationsService } from './modules/transformations/transformations.service';
|
||||
import { OutputsService } from './modules/outputs/outputs.service';
|
||||
import { PipelinesService } from './modules/pipelines/pipelines.service';
|
||||
// import { AuthService } from './modules/auth/auth.service';
|
||||
import { HealthService } from './modules/health/health.service';
|
||||
|
||||
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 { AuthClientService } from './clients/auth/client.service';
|
||||
|
||||
import { OutputsClientConfiguration } from './clients/outputs/client.config';
|
||||
import { TransformationsClientConfiguration } from './clients/transformations/client.config';
|
||||
import { AuthClient } from './clients/auth/client.config';
|
||||
import { InputsClientConfiguration } from './clients/inputs/client.config';
|
||||
import { PipelinesClientConfiguration } from './clients/pipelines/client.config';
|
||||
import { CatalogController } from './modules/catalog/catalog.controller';
|
||||
import { CatalogService } from './modules/catalog/catalog.service';
|
||||
import { OauthController } from './modules/oauth/oauth.controller';
|
||||
import { getOauthSecrets } from './utils/OauthSecrets';
|
||||
import { HubspotStrategy } from './modules/oauth/passport-strategies/hubspot-strategy';
|
||||
import { FacebookStrategy } from './modules/oauth/passport-strategies/facebook-strategy';
|
||||
import { AuthenticationGuard } from './authentication/authentication.guard';
|
||||
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 { 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 authClient = new AuthClient();
|
||||
const inputClient = new InputsClientConfiguration();
|
||||
const outputClient = new OutputsClientConfiguration();
|
||||
const pipelineClient = new PipelinesClientConfiguration();
|
||||
@@ -44,35 +54,47 @@ const transformationClient = new TransformationsClientConfiguration();
|
||||
TransformationsController,
|
||||
OutputsController,
|
||||
PipelinesController,
|
||||
AuthController,
|
||||
HealthController,
|
||||
CatalogController,
|
||||
OauthController,
|
||||
],
|
||||
providers: [
|
||||
DadosferaLogger,
|
||||
{ provide: 'OAUTH_SECRETS', useValue: getOauthSecrets() },
|
||||
InputsService,
|
||||
TransformationsService,
|
||||
OutputsService,
|
||||
PipelinesService,
|
||||
// AuthService,
|
||||
HealthService,
|
||||
InputsClientService,
|
||||
TransformationsClientService,
|
||||
OutputsClientService,
|
||||
PipelinesClientService,
|
||||
AuthClientService,
|
||||
CatalogService,
|
||||
HubspotStrategy,
|
||||
FacebookStrategy,
|
||||
GoogleStrategy,
|
||||
MailChimpStrategy,
|
||||
SalesforceStrategy,
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: AuthenticationGuard,
|
||||
},
|
||||
],
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
}),
|
||||
ConnectorModule,
|
||||
AuthModule,
|
||||
PermissionsModule,
|
||||
TermsOfUseModule,
|
||||
|
||||
ClientsModule.register([
|
||||
{
|
||||
name: 'INPUTS_PACKAGE',
|
||||
...inputClient.config(),
|
||||
},
|
||||
|
||||
{
|
||||
name: 'TRANSFORMATIONS_PACKAGE',
|
||||
...transformationClient.config(),
|
||||
@@ -81,27 +103,15 @@ const transformationClient = new TransformationsClientConfiguration();
|
||||
name: 'OUTPUTS_PACKAGE',
|
||||
...outputClient.config(),
|
||||
},
|
||||
{
|
||||
name: 'AUTH_PACKAGE',
|
||||
...authClient.config(),
|
||||
},
|
||||
{
|
||||
name: 'PIPELINES_PACKAGE',
|
||||
...pipelineClient.config(),
|
||||
},
|
||||
]),
|
||||
|
||||
UsersModule,
|
||||
|
||||
RolesModule,
|
||||
],
|
||||
})
|
||||
export class AppModule implements NestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply(LoggerMiddleware)
|
||||
.forRoutes(
|
||||
InputsController,
|
||||
TransformationsController,
|
||||
OutputsController,
|
||||
PipelinesController,
|
||||
CatalogController,
|
||||
);
|
||||
}
|
||||
}
|
||||
export class AppModule {}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Request } from 'express';
|
||||
import { CustomDecorator } from '@nestjs/common';
|
||||
import { PermissionsObjectPermission } from './permissions.enum';
|
||||
import { RequestUser } from './user.decorator';
|
||||
|
||||
export const AUTH_FUNCTION_KEY = '__AUTH_FUNCTION__';
|
||||
|
||||
export type AuthenticationFunction = (req: Request, user: any) => boolean;
|
||||
|
||||
// implementation copied from SetMetadata, but tweaked to get existing values
|
||||
// of the metadataKey and accumulate it with the new metadataValue
|
||||
function SetMultipleMetadata(metadataKey, metadataValue): CustomDecorator {
|
||||
const decoratorFactory = (target, key, descriptor) => {
|
||||
// .start: tweak
|
||||
// descriptor?.value = function decorator; target = class decorator
|
||||
const accumulatedVal =
|
||||
Reflect.getMetadata(metadataKey, descriptor?.value ?? target) ?? [];
|
||||
accumulatedVal.push(metadataValue);
|
||||
// .end: tweak
|
||||
|
||||
if (descriptor) {
|
||||
Reflect.defineMetadata(metadataKey, accumulatedVal, descriptor.value);
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
Reflect.defineMetadata(metadataKey, accumulatedVal, target);
|
||||
return target;
|
||||
};
|
||||
|
||||
decoratorFactory.KEY = metadataKey;
|
||||
return decoratorFactory as any;
|
||||
}
|
||||
|
||||
export function RequireAllPermissions(
|
||||
...permissions: PermissionsObjectPermission[]
|
||||
) {
|
||||
return SetMultipleMetadata(AUTH_FUNCTION_KEY, (req, user: RequestUser) =>
|
||||
permissions.every(({ seqid }) => user.permissions.includes(seqid)),
|
||||
);
|
||||
}
|
||||
|
||||
export function RequireSomePermission(
|
||||
...permissions: PermissionsObjectPermission[]
|
||||
) {
|
||||
return SetMultipleMetadata(AUTH_FUNCTION_KEY, (req, user: RequestUser) =>
|
||||
permissions.some(({ seqid }) => user.permissions.includes(seqid)),
|
||||
);
|
||||
}
|
||||
|
||||
export function AuthenticateCondition(func: AuthenticationFunction) {
|
||||
return SetMultipleMetadata(AUTH_FUNCTION_KEY, func);
|
||||
}
|
||||
|
||||
export function Authenticated() {
|
||||
return SetMultipleMetadata(AUTH_FUNCTION_KEY, () => true);
|
||||
}
|
||||
@@ -0,0 +1,457 @@
|
||||
import request from 'supertest';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { HttpStatus, INestApplication } from '@nestjs/common';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
import { Body, Controller, Get } from '@nestjs/common';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
import {
|
||||
AuthenticateCondition,
|
||||
Authenticated,
|
||||
RequireAllPermissions,
|
||||
RequireSomePermission,
|
||||
} from './authentication.decorator';
|
||||
import { AuthenticationGuard } from './authentication.guard';
|
||||
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')
|
||||
async getBody(@Body() body) {
|
||||
return { body };
|
||||
}
|
||||
|
||||
@Get('authenticated')
|
||||
@Authenticated()
|
||||
async mustBeAuthenticated(@Body() body) {
|
||||
return { body };
|
||||
}
|
||||
|
||||
@Get('required-permission')
|
||||
@RequireSomePermission(PERMISSIONS.ZENDESK.permissions.OPEN)
|
||||
async requiredPermission(@Body() body) {
|
||||
return { body };
|
||||
}
|
||||
|
||||
@Get('has-all-permissions')
|
||||
@RequireAllPermissions(
|
||||
PERMISSIONS.ZENDESK.permissions.OPEN,
|
||||
PERMISSIONS.METABASE.permissions.OPEN,
|
||||
)
|
||||
async hasAllPermissions(@Body() body) {
|
||||
return { body };
|
||||
}
|
||||
|
||||
@Get('has-some-permission')
|
||||
@RequireSomePermission(
|
||||
PERMISSIONS.ZENDESK.permissions.OPEN,
|
||||
PERMISSIONS.METABASE.permissions.OPEN,
|
||||
)
|
||||
async hasSomePermission(@Body() body) {
|
||||
return { body };
|
||||
}
|
||||
}
|
||||
|
||||
@Controller('class-auth-condition')
|
||||
@AuthenticateCondition((req) => req.get('x-on-class') === 'ok')
|
||||
@RequireSomePermission(PERMISSIONS.METABASE.permissions.OPEN)
|
||||
class ClassAuthConditionController {
|
||||
@Get('body')
|
||||
async getBody(@Body() body) {
|
||||
return { body };
|
||||
}
|
||||
|
||||
@Get('authenticated')
|
||||
@Authenticated()
|
||||
async mustBeAuthenticated(@Body() body) {
|
||||
return { body };
|
||||
}
|
||||
|
||||
@Get('required-permission')
|
||||
@AuthenticateCondition((req) => req.get('x-on-route') === 'ok')
|
||||
@RequireSomePermission(PERMISSIONS.ZENDESK.permissions.OPEN)
|
||||
async requiredPermission(@Body() body) {
|
||||
return { body };
|
||||
}
|
||||
}
|
||||
|
||||
describe('authentication.guard', () => {
|
||||
let app: INestApplication;
|
||||
const jwtSecretA = {
|
||||
kid: 'token-a-testing-shared-key',
|
||||
pem: '$tr0ng-SH4Red-secr3t!!!~gl0ba1~]',
|
||||
};
|
||||
const jwtSecretB = {
|
||||
kid: 'token-b',
|
||||
pem: 'another-shared-token',
|
||||
};
|
||||
const fakeUserPayload = {
|
||||
user_id: 'd50d33c7-6c2b-463c-861f-e21667e7c125',
|
||||
username: 'super.admin',
|
||||
customer_id: '9d18e8ae-24b9-41a3-9e8f-a25ce57555b11',
|
||||
customer_name: 'dadosfera',
|
||||
customer_tier: 'BASIC',
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: AuthClientService,
|
||||
useValue: {
|
||||
getPublicKeys: async () => ({
|
||||
keys: [jwtSecretA, jwtSecretB],
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: DadosferaLogger,
|
||||
useValue: { logger },
|
||||
},
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: AuthenticationGuard,
|
||||
},
|
||||
],
|
||||
controllers: [NoClassAuthController, ClassAuthConditionController],
|
||||
}).compile();
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
function AssertBodyNoAuth(res: request.Response) {
|
||||
expect(res.statusCode).toBe(HttpStatus.OK);
|
||||
expect(res.body).toStrictEqual({ body: {} });
|
||||
}
|
||||
|
||||
function AssertBodyWithAuth(res: request.Response) {
|
||||
expect(res.statusCode).toBe(HttpStatus.OK);
|
||||
expect(res.body).toStrictEqual({
|
||||
body: {
|
||||
info: {
|
||||
customer: 'dadosfera',
|
||||
customer_id: '9d18e8ae-24b9-41a3-9e8f-a25ce57555b11',
|
||||
customer_tier: 'BASIC',
|
||||
user_id: 'd50d33c7-6c2b-463c-861f-e21667e7c125',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function AssertUnauthorized(res: request.Response) {
|
||||
expect(res.statusCode).toBe(HttpStatus.UNAUTHORIZED);
|
||||
expect(res.body).toStrictEqual({
|
||||
code: ErrorCodes.AUTH.UNAUTHORIZED,
|
||||
error: 'Não autenticado',
|
||||
message: 'É necessário estar logado para realizar essa operação',
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
});
|
||||
}
|
||||
|
||||
function AssertForbidden(res: request.Response) {
|
||||
expect(res.statusCode).toBe(HttpStatus.FORBIDDEN);
|
||||
expect(res.body).toStrictEqual({
|
||||
code: ErrorCodes.AUTH.FORBIDDEN,
|
||||
error: 'Não autorizado',
|
||||
message:
|
||||
'Você não tem permissões suficientes para realizar essa operação',
|
||||
statusCode: HttpStatus.FORBIDDEN,
|
||||
});
|
||||
}
|
||||
|
||||
function NoClassAuthTest(accessToken: string, tokenName: string[]) {
|
||||
const route = '/no-class-auth';
|
||||
|
||||
describe(`${route}, ${tokenName?.length ? tokenName : 'none'} auth`, () => {
|
||||
it('should GET /body with user data (if available)', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/body`)
|
||||
.set({ Authorization: accessToken });
|
||||
|
||||
if (tokenName?.length) {
|
||||
AssertBodyWithAuth(res);
|
||||
} else {
|
||||
AssertBodyNoAuth(res);
|
||||
}
|
||||
});
|
||||
|
||||
it('should GET /authenticated if authenticated', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/authenticated`)
|
||||
.set({ Authorization: accessToken });
|
||||
|
||||
if (tokenName?.length) {
|
||||
AssertBodyWithAuth(res);
|
||||
} else {
|
||||
AssertUnauthorized(res);
|
||||
}
|
||||
});
|
||||
|
||||
it('should GET /required-permission if authenticated', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/required-permission`)
|
||||
.set({ Authorization: accessToken });
|
||||
|
||||
// zendesk required
|
||||
if (tokenName?.includes('zendesk')) {
|
||||
AssertBodyWithAuth(res);
|
||||
} else if (tokenName?.length) {
|
||||
AssertForbidden(res);
|
||||
} else {
|
||||
AssertUnauthorized(res);
|
||||
}
|
||||
});
|
||||
|
||||
it('should GET /has-all-permissions if authenticated', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/has-all-permissions`)
|
||||
.set({ Authorization: accessToken });
|
||||
|
||||
// zendesk and metabase
|
||||
if (tokenName?.includes('zendesk') && tokenName?.includes('metabase')) {
|
||||
AssertBodyWithAuth(res);
|
||||
} else if (tokenName?.length) {
|
||||
AssertForbidden(res);
|
||||
} else {
|
||||
AssertUnauthorized(res);
|
||||
}
|
||||
});
|
||||
|
||||
it('should GET /has-some-permission if authenticated', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/has-some-permission`)
|
||||
.set({ Authorization: accessToken });
|
||||
|
||||
// zendesk or metabase
|
||||
if (tokenName?.includes('zendesk') || tokenName?.includes('metabase')) {
|
||||
AssertBodyWithAuth(res);
|
||||
} else if (tokenName?.length) {
|
||||
AssertForbidden(res);
|
||||
} else {
|
||||
AssertUnauthorized(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function ClassAuthConditionTest(accessToken: string, tokenName: string[]) {
|
||||
const route = '/class-auth-condition';
|
||||
|
||||
describe(`${route}, ${tokenName?.length ? tokenName : 'none'} auth`, () => {
|
||||
describe('GET /body', () => {
|
||||
it('should GET /body if authenticated and with correct headers', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/body`)
|
||||
.set({
|
||||
'x-on-class': 'ok',
|
||||
Authorization: accessToken,
|
||||
});
|
||||
|
||||
// metabase required
|
||||
if (tokenName?.includes('metabase')) {
|
||||
AssertBodyWithAuth(res);
|
||||
} else if (tokenName?.length) {
|
||||
AssertForbidden(res);
|
||||
} else {
|
||||
AssertUnauthorized(res);
|
||||
}
|
||||
});
|
||||
|
||||
it('should not GET /body without correct headers', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/body`)
|
||||
.set({ Authorization: accessToken });
|
||||
|
||||
if (tokenName?.length) {
|
||||
AssertForbidden(res);
|
||||
} else {
|
||||
AssertUnauthorized(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /authenticated', () => {
|
||||
it('should GET /authenticated if authenticated and with correct headers', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/authenticated`)
|
||||
.set({
|
||||
'x-on-class': 'ok',
|
||||
Authorization: accessToken,
|
||||
});
|
||||
|
||||
// metabase required
|
||||
if (tokenName?.includes('metabase')) {
|
||||
AssertBodyWithAuth(res);
|
||||
} else if (tokenName?.length) {
|
||||
AssertForbidden(res);
|
||||
} else {
|
||||
AssertUnauthorized(res);
|
||||
}
|
||||
});
|
||||
|
||||
it('should not GET /authenticated without correct class headers', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/authenticated`)
|
||||
.set({ Authorization: accessToken });
|
||||
|
||||
if (tokenName?.length) {
|
||||
AssertForbidden(res);
|
||||
} else {
|
||||
AssertUnauthorized(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /required-permission', () => {
|
||||
it('should GET /required-permission if authenticated and with correct headers', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/required-permission`)
|
||||
.set({
|
||||
'x-on-class': 'ok',
|
||||
'x-on-route': 'ok',
|
||||
Authorization: accessToken,
|
||||
});
|
||||
|
||||
// zendesk and metabase
|
||||
if (
|
||||
tokenName?.includes('zendesk') &&
|
||||
tokenName?.includes('metabase')
|
||||
) {
|
||||
AssertBodyWithAuth(res);
|
||||
} else if (tokenName?.length) {
|
||||
AssertForbidden(res);
|
||||
} else {
|
||||
AssertUnauthorized(res);
|
||||
}
|
||||
});
|
||||
|
||||
it('should not GET /required-permission without correct route headers', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/required-permission`)
|
||||
.set({
|
||||
'x-on-class': 'ok',
|
||||
Authorization: accessToken,
|
||||
});
|
||||
|
||||
if (tokenName?.length) {
|
||||
AssertForbidden(res);
|
||||
} else {
|
||||
AssertUnauthorized(res);
|
||||
}
|
||||
});
|
||||
|
||||
it('should not GET /required-permission without correct class headers', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`${route}/required-permission`)
|
||||
.set({
|
||||
'x-on-route': 'ok',
|
||||
Authorization: accessToken,
|
||||
});
|
||||
|
||||
if (tokenName?.length) {
|
||||
AssertForbidden(res);
|
||||
} else {
|
||||
AssertUnauthorized(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('token validation', () => {
|
||||
it('should accept token kid A', async () => {
|
||||
const Authorization = CreateToken([], {
|
||||
jwt: {
|
||||
kid: jwtSecretA.kid,
|
||||
pem: jwtSecretA.pem,
|
||||
},
|
||||
});
|
||||
|
||||
const res = await request(app.getHttpServer())
|
||||
.get('/no-class-auth/authenticated')
|
||||
.set({
|
||||
Authorization,
|
||||
});
|
||||
|
||||
AssertBodyWithAuth(res);
|
||||
});
|
||||
|
||||
it('should accept token kid B', async () => {
|
||||
const Authorization = CreateToken([], {
|
||||
jwt: {
|
||||
kid: jwtSecretB.kid,
|
||||
pem: jwtSecretB.pem,
|
||||
},
|
||||
});
|
||||
|
||||
const res = await request(app.getHttpServer())
|
||||
.get('/no-class-auth/authenticated')
|
||||
.set({
|
||||
Authorization,
|
||||
});
|
||||
|
||||
AssertBodyWithAuth(res);
|
||||
});
|
||||
|
||||
it('should not accept unknown kid', async () => {
|
||||
const Authorization = CreateToken([], {
|
||||
jwt: {
|
||||
kid: 'unknown-key-id',
|
||||
pem: jwtSecretA.pem,
|
||||
},
|
||||
});
|
||||
|
||||
const res = await request(app.getHttpServer())
|
||||
.get('/no-class-auth/authenticated')
|
||||
.set({
|
||||
Authorization,
|
||||
});
|
||||
|
||||
AssertUnauthorized(res);
|
||||
});
|
||||
});
|
||||
|
||||
function CreateToken(permissions, overrides?: { user?: any; jwt?: any }) {
|
||||
const tokenPayload = {
|
||||
...fakeUserPayload,
|
||||
permissions: permissions.map(({ seqid }) => seqid),
|
||||
token_use: 'access',
|
||||
...overrides?.user,
|
||||
};
|
||||
|
||||
return jwt.sign(tokenPayload, overrides?.jwt?.pem ?? jwtSecretA.pem, {
|
||||
keyid: overrides?.jwt?.kid ?? jwtSecretA.kid,
|
||||
});
|
||||
}
|
||||
|
||||
NoClassAuthTest(null, null);
|
||||
ClassAuthConditionTest(null, null);
|
||||
|
||||
const tokenZ = CreateToken([PERMISSIONS.ZENDESK.permissions.OPEN]);
|
||||
NoClassAuthTest(tokenZ, ['zendesk']);
|
||||
ClassAuthConditionTest(tokenZ, ['zendesk']);
|
||||
|
||||
const tokenM = CreateToken([PERMISSIONS.METABASE.permissions.OPEN]);
|
||||
NoClassAuthTest(tokenM, ['metabase']);
|
||||
ClassAuthConditionTest(tokenM, ['metabase']);
|
||||
|
||||
const tokenZM = CreateToken([
|
||||
PERMISSIONS.ZENDESK.permissions.OPEN,
|
||||
PERMISSIONS.METABASE.permissions.OPEN,
|
||||
]);
|
||||
NoClassAuthTest(tokenZM, ['zendesk', 'metabase']);
|
||||
ClassAuthConditionTest(tokenZM, ['zendesk', 'metabase']);
|
||||
});
|
||||
@@ -0,0 +1,136 @@
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
OnApplicationBootstrap,
|
||||
ExecutionContext,
|
||||
Inject,
|
||||
} from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import assert from 'assert';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
import { AuthClientService } from '../modules/auth/auth.service';
|
||||
import {
|
||||
AuthenticationFunction,
|
||||
AUTH_FUNCTION_KEY,
|
||||
} from './authentication.decorator';
|
||||
import { RequestUser } from './user.decorator';
|
||||
import ErrorBuilder from '../utils/ErrorBuilder';
|
||||
import ErrorCodes from '../utils/errorCodes';
|
||||
|
||||
@Injectable()
|
||||
export class AuthenticationGuard
|
||||
implements CanActivate, OnApplicationBootstrap
|
||||
{
|
||||
logger: DadosferaLogger;
|
||||
|
||||
pems: Map<string, string>;
|
||||
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
private reflector: Reflector,
|
||||
private authClient: AuthClientService,
|
||||
) {
|
||||
this.pems = new Map();
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
async onApplicationBootstrap() {
|
||||
return this.loadDucJWKS();
|
||||
}
|
||||
|
||||
private async loadDucJWKS() {
|
||||
const { keys } = await this.authClient.getPublicKeys();
|
||||
|
||||
keys.forEach((key) => {
|
||||
this.pems.set(key.kid, key.pem);
|
||||
});
|
||||
}
|
||||
|
||||
canActivate(ctx: ExecutionContext): boolean {
|
||||
const authFunctions = this.reflector.getAllAndMerge<
|
||||
AuthenticationFunction[]
|
||||
>(AUTH_FUNCTION_KEY, [ctx.getClass(), ctx.getHandler()]);
|
||||
const mustBeAuthenticated = authFunctions.length > 0;
|
||||
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
const accessToken = this.validateToken(request, mustBeAuthenticated);
|
||||
|
||||
if (!mustBeAuthenticated) {
|
||||
// no need to be authenticated
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
// couldn't load valid token
|
||||
throw new ErrorBuilder(ErrorCodes.AUTH.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
// every authentication function must return true to authenticate
|
||||
if (!authFunctions.every((func) => func(request, accessToken))) {
|
||||
throw new ErrorBuilder(ErrorCodes.AUTH.FORBIDDEN);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private validateToken(
|
||||
request,
|
||||
mustBeAuthenticated: boolean,
|
||||
): RequestUser | false {
|
||||
const accessToken = request.get('Authorization');
|
||||
let accessTokenPayload: RequestUser;
|
||||
|
||||
// If the user isn't authenticated, an error will occurr anywhere here.
|
||||
// Fancy error avoidance isn't performed by purpose, such as avoiding to access null values.
|
||||
try {
|
||||
const accessTokenDecoded: any = jwt.decode(accessToken, {
|
||||
complete: true,
|
||||
});
|
||||
|
||||
/*assert(
|
||||
accessTokenDecoded.payload.iss === 'duc',
|
||||
'token should be issued by DUC',
|
||||
);*/
|
||||
|
||||
assert(
|
||||
accessTokenDecoded?.payload?.token_use === 'access',
|
||||
'should be an access token',
|
||||
);
|
||||
|
||||
const { kid } = accessTokenDecoded.header;
|
||||
const pemValue: string = this.pems.get(kid);
|
||||
|
||||
jwt.verify(accessToken, pemValue);
|
||||
accessTokenPayload = accessTokenDecoded.payload;
|
||||
} catch (err) {
|
||||
// log errors if authentication is required
|
||||
if (mustBeAuthenticated) {
|
||||
this.logger.error('Unable to verify duc access token\n' + err?.stack);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
request.accessTokenPayload = accessTokenPayload;
|
||||
request.user = {
|
||||
user_id: accessTokenPayload.user_id,
|
||||
username: accessTokenPayload.username,
|
||||
permissions: accessTokenPayload.permissions,
|
||||
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 = {
|
||||
user_id: accessTokenPayload.user_id,
|
||||
customer_id: accessTokenPayload.customer_id,
|
||||
customer: accessTokenPayload.customer_name,
|
||||
customer_tier: accessTokenPayload.customer_tier,
|
||||
};
|
||||
|
||||
return accessTokenPayload;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import assert from 'assert';
|
||||
import { LanguageEnum } from 'src/utils/languages.enum';
|
||||
|
||||
const logger = new Logger();
|
||||
|
||||
export enum PermissionUsages {
|
||||
PUBLIC = 'public',
|
||||
INTERNAL = 'internal',
|
||||
}
|
||||
|
||||
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>;
|
||||
}
|
||||
|
||||
/*
|
||||
- like field numbers in gRPC, avoid to change (or reuse previously used) seqids once its deployed to DUC
|
||||
- it is possible to update claim and usage, not seqid
|
||||
*/
|
||||
|
||||
/*export const Permissions: {
|
||||
[P in keyof any]: { [Q in keyof any]: Permission };
|
||||
} = {*/
|
||||
export const PERMISSIONS = {
|
||||
AUTH: {
|
||||
title: {
|
||||
'pt-br': 'Autenticação',
|
||||
'en-us': 'Authentication',
|
||||
},
|
||||
permissions: {
|
||||
CREATE: {
|
||||
seqid: 18,
|
||||
claim: 'POST /auth',
|
||||
usage: PermissionUsages.INTERNAL,
|
||||
name: {
|
||||
'pt-br': '',
|
||||
'en-us': '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
PIPELINE: {
|
||||
title: {
|
||||
'pt-br': 'Pipelines',
|
||||
'en-us': 'Pipelines',
|
||||
},
|
||||
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: {
|
||||
title: {
|
||||
'pt-br': 'Conexões',
|
||||
'en-us': 'Connections',
|
||||
},
|
||||
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: {
|
||||
title: {
|
||||
'pt-br': 'Output',
|
||||
'en-us': 'Output',
|
||||
},
|
||||
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: {
|
||||
title: {
|
||||
'pt-br': 'Micro-transformações',
|
||||
'en-us': 'Micro-transformations',
|
||||
},
|
||||
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: {
|
||||
title: {
|
||||
'pt-br': 'Catálogo',
|
||||
'en-us': 'Catalog',
|
||||
},
|
||||
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: {
|
||||
title: {
|
||||
'pt-br': 'Conectores',
|
||||
'en-us': 'Connectors',
|
||||
},
|
||||
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: {
|
||||
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: {
|
||||
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: {
|
||||
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: {
|
||||
title: {
|
||||
'pt-br': 'Orchest',
|
||||
'en-us': 'Orchest',
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
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.permissions).map(({ seqid }) => seqid),
|
||||
);
|
||||
const claims = Object.values(PERMISSIONS).flatMap((namespace) =>
|
||||
Object.values(namespace.permissions).map(({ claim }) => 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`);
|
||||
|
||||
const claimsCount = claims.filter((x) => x === claim).length;
|
||||
assert(claimsCount === 1, `claim '${claim}' count is not 1`);
|
||||
nextAvailableSeqid = Math.max(nextAvailableSeqid, seqid);
|
||||
}),
|
||||
);
|
||||
|
||||
logger.log(`next available seqid ${nextAvailableSeqid + 1}`);
|
||||
@@ -0,0 +1,182 @@
|
||||
import request from 'supertest';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { HttpStatus, INestApplication } from '@nestjs/common';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
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';
|
||||
|
||||
const logger = {
|
||||
info: (...args) => args,
|
||||
error: (...args) => args,
|
||||
};
|
||||
@Controller('user')
|
||||
class UserController {
|
||||
@Get()
|
||||
public getUser(@User() user) {
|
||||
return { user };
|
||||
}
|
||||
|
||||
@Get('options')
|
||||
public getUserWithOptions(@User({}) user) {
|
||||
return { user };
|
||||
}
|
||||
|
||||
@Get('not-required')
|
||||
public getUserNotRequired(@User({ required: false }) user) {
|
||||
return { user };
|
||||
}
|
||||
|
||||
@Get('required')
|
||||
public getUserRequired(@User({ required: true }) user) {
|
||||
return { user };
|
||||
}
|
||||
}
|
||||
|
||||
describe('user.decorator', () => {
|
||||
let app: INestApplication;
|
||||
const jwt_secret = {
|
||||
kid: 'token-testing-shared-key',
|
||||
pem: '$tr0ng-SH4Red-secr3t!!!~gl0ba1~]',
|
||||
};
|
||||
const fakeUserPayload = {
|
||||
user_id: 'd50d33c7-6c2b-463c-861f-e21667e7c125',
|
||||
username: 'super.admin',
|
||||
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 () => {
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: AuthClientService,
|
||||
useValue: {
|
||||
getPublicKeys: async () => ({
|
||||
keys: [jwt_secret],
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: DadosferaLogger,
|
||||
useValue: { logger },
|
||||
},
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: AuthenticationGuard,
|
||||
},
|
||||
],
|
||||
controllers: [UserController],
|
||||
}).compile();
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
function AssertNoAuth(res: request.Response) {
|
||||
expect(res.statusCode).toBe(HttpStatus.OK);
|
||||
expect(res.body).toStrictEqual({});
|
||||
}
|
||||
|
||||
function AssertWithAuth(res: request.Response) {
|
||||
expect(res.statusCode).toBe(HttpStatus.OK);
|
||||
expect(res.body).toStrictEqual({ user: fakeUserPayload });
|
||||
}
|
||||
|
||||
function AssertRequiredNoAuth(res: request.Response) {
|
||||
expect(res.statusCode).toBe(HttpStatus.UNAUTHORIZED);
|
||||
expect(res.body).toStrictEqual({
|
||||
code: ErrorCodes.AUTH.UNAUTHORIZED,
|
||||
error: 'Não autenticado',
|
||||
message: 'É necessário estar logado para realizar essa operação',
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
});
|
||||
}
|
||||
|
||||
function UserTest(accessToken: string) {
|
||||
describe(`with${accessToken ? '' : 'out'} token`, () => {
|
||||
it('should GET /user', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get('/user')
|
||||
.set({ Authorization: accessToken });
|
||||
|
||||
if (accessToken) {
|
||||
AssertWithAuth(res);
|
||||
} else {
|
||||
AssertNoAuth(res);
|
||||
}
|
||||
});
|
||||
|
||||
it('should GET /user/options', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get('/user/options')
|
||||
.set({ Authorization: accessToken });
|
||||
|
||||
if (accessToken) {
|
||||
AssertWithAuth(res);
|
||||
} else {
|
||||
AssertNoAuth(res);
|
||||
}
|
||||
});
|
||||
|
||||
it('should GET /user/not-required', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get('/user/not-required')
|
||||
.set({ Authorization: accessToken });
|
||||
|
||||
if (accessToken) {
|
||||
AssertWithAuth(res);
|
||||
} else {
|
||||
AssertNoAuth(res);
|
||||
}
|
||||
});
|
||||
|
||||
it('should GET /user/required if logged', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get('/user/required')
|
||||
.set({ Authorization: accessToken });
|
||||
|
||||
if (accessToken) {
|
||||
AssertWithAuth(res);
|
||||
} else {
|
||||
AssertRequiredNoAuth(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function CreateToken(overrides?) {
|
||||
const tokenPayload = {
|
||||
...fakeUserPayload,
|
||||
token_use: 'access',
|
||||
...overrides?.user,
|
||||
};
|
||||
|
||||
return jwt.sign(tokenPayload, jwt_secret.pem, {
|
||||
keyid: jwt_secret.kid,
|
||||
...overrides?.jwt,
|
||||
});
|
||||
}
|
||||
|
||||
UserTest(null);
|
||||
|
||||
const token = CreateToken();
|
||||
fakeUserPayload.access_token = token;
|
||||
UserTest(token);
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||
|
||||
import ErrorBuilder from '../utils/ErrorBuilder';
|
||||
import ErrorCodes from '../utils/errorCodes';
|
||||
|
||||
export interface RequestUser {
|
||||
user_id: string;
|
||||
username: string;
|
||||
permissions: number[];
|
||||
customer_id: string;
|
||||
customer_name: string;
|
||||
customer_tier: string;
|
||||
access_token: string;
|
||||
}
|
||||
|
||||
export const User: (options?: { required?: boolean }) => ParameterDecorator =
|
||||
createParamDecorator((options: any, ctx: ExecutionContext) => {
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
|
||||
if (!request.user && options?.required) {
|
||||
throw new ErrorBuilder(ErrorCodes.AUTH.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
return request.user;
|
||||
});
|
||||
@@ -1,259 +0,0 @@
|
||||
import { OnModuleInit, Inject } from '@nestjs/common';
|
||||
import { ClientGrpc } from '@nestjs/microservices';
|
||||
|
||||
import {
|
||||
DucServicesNames,
|
||||
AuthServiceInterface,
|
||||
AuthSignInRequest,
|
||||
AuthRefreshAccessTokenRequest,
|
||||
AuthConfirmationRequest,
|
||||
AuthResendConfirmationCodeRequest,
|
||||
AuthEnableTotpMfaRequest,
|
||||
AuthDisableTotpMfaRequest,
|
||||
AuthDismissTotpMfaRequest,
|
||||
AuthVerifyTotpMfaRequest,
|
||||
AuthChangePasswordRequest,
|
||||
AuthResetPasswordRequest,
|
||||
AuthVerifyResetPasswordCodeRequest,
|
||||
AuthConfirmResetPasswordRequest,
|
||||
} from '@victorradael/protospack';
|
||||
|
||||
export class AuthClientService implements OnModuleInit {
|
||||
private authService: AuthServiceInterface;
|
||||
constructor(
|
||||
@Inject('AUTH_PACKAGE') private readonly grpcClient: ClientGrpc,
|
||||
) {}
|
||||
|
||||
onModuleInit() {
|
||||
this.authService = this.grpcClient.getService<AuthServiceInterface>(
|
||||
DucServicesNames.AuthProtoService,
|
||||
);
|
||||
}
|
||||
|
||||
async signIn({ username, password, totp }: AuthSignInRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'SignIn');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.signIn({ username, password, totp }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async enableTotpMFA({
|
||||
accessToken,
|
||||
password,
|
||||
}: AuthEnableTotpMfaRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'enableTotpMFA');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.enableTotpMfa({ accessToken, password }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async disableTotpMFA({
|
||||
accessToken,
|
||||
password,
|
||||
}: AuthDisableTotpMfaRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'disableTotpMFA');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.disableTotpMfa({ accessToken, password }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async dismissTotpMFA({
|
||||
accessToken,
|
||||
}: AuthDismissTotpMfaRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'dismissTotpMFA');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.dismissTotpMfa({ accessToken }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async verifyTotp({
|
||||
accessToken,
|
||||
totp,
|
||||
}: AuthVerifyTotpMfaRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'disableTotpMFA');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.verifyTotp({ accessToken, totp }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async confirmRegister({
|
||||
username,
|
||||
code,
|
||||
}: AuthConfirmationRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'confirmRegister');
|
||||
|
||||
const tokens = await new Promise((resolve, reject) => {
|
||||
this.authService.confirmRegister({ username, code }).subscribe({
|
||||
next(x) {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.catch((err) => {
|
||||
throw new Error(err);
|
||||
});
|
||||
return tokens;
|
||||
}
|
||||
|
||||
async resendeConfirmationCode({
|
||||
username,
|
||||
}: AuthResendConfirmationCodeRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'resendConfirmationCode');
|
||||
|
||||
const authResendConfirmationCodeRequestReturn = await new Promise(
|
||||
(resolve, reject) => {
|
||||
this.authService.resendConfirmationCode({ username }).subscribe({
|
||||
next(x) {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
},
|
||||
)
|
||||
.then((res) => res)
|
||||
.catch((err) => {
|
||||
throw new Error(err);
|
||||
});
|
||||
return authResendConfirmationCodeRequestReturn;
|
||||
}
|
||||
|
||||
async refreshAccessToken({
|
||||
refreshToken,
|
||||
}: AuthRefreshAccessTokenRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'RefreshAccessToken');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.refreshAccessToken({ refreshToken }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async changePassword({
|
||||
accessToken,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
}: AuthChangePasswordRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'ChangePassword');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService
|
||||
.changePassword({
|
||||
accessToken,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
})
|
||||
.subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async resetPassword({ username }: AuthResetPasswordRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'resetPassword');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.resetPassword({ username }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async verifyResetPasswordCode({
|
||||
username,
|
||||
code,
|
||||
}: AuthVerifyResetPasswordCodeRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'verifyResetPasswordCode');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService.verifyResetPasswordCode({ username, code }).subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async confirmResetPassword({
|
||||
username,
|
||||
code,
|
||||
newPassword,
|
||||
}: AuthConfirmResetPasswordRequest): Promise<any> {
|
||||
console.log('AuthClientService', 'confirmResetPassword');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.authService
|
||||
.confirmResetPassword({ username, code, newPassword })
|
||||
.subscribe({
|
||||
next: resolve,
|
||||
error: (err) => reject(err.details),
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
import { ClientOptions, Transport } from '@nestjs/microservices';
|
||||
import { ProtoPackages, ProtoPaths } from '@dadosfera/protospack-v2/dist/lib/Duc';
|
||||
|
||||
import { DucProtoFilePath, DucPackages } from '@victorradael/protospack';
|
||||
|
||||
export class AuthClient {
|
||||
export class DucClient {
|
||||
config(): ClientOptions {
|
||||
return {
|
||||
transport: Transport.GRPC,
|
||||
options: {
|
||||
url: process.env.DUC_URL,
|
||||
package: DucPackages,
|
||||
package: ProtoPackages.WritePackage,
|
||||
// credentials: credentials.createSsl(),
|
||||
credentials: process.env.LOCAL_ENV
|
||||
? undefined
|
||||
: credentials.createSsl(),
|
||||
protoPath: DucProtoFilePath,
|
||||
protoPath: ProtoPaths.WriteFilePath,
|
||||
loader: {
|
||||
keepCase: true,
|
||||
enums: String,
|
||||
objects: true,
|
||||
arrays: true,
|
||||
@@ -1,7 +1,7 @@
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
import { ClientOptions, Transport } from '@nestjs/microservices';
|
||||
|
||||
import { InputPackages, InputProtoFilePath } from '@victorradael/protospack';
|
||||
import { InputPackages, InputProtoFilePath } from 'protospack';
|
||||
|
||||
export class InputsClientConfiguration {
|
||||
config(): ClientOptions {
|
||||
|
||||
@@ -1,148 +1,145 @@
|
||||
import { OnModuleInit, Inject } from '@nestjs/common';
|
||||
import {
|
||||
OnModuleInit,
|
||||
Inject,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
NotFoundException,
|
||||
InternalServerErrorException,
|
||||
} from '@nestjs/common';
|
||||
import { ClientGrpc } from '@nestjs/microservices';
|
||||
import {
|
||||
InputCreateResponse,
|
||||
DecodeGrpcStruct,
|
||||
EncodeJsonToGrpcStruct,
|
||||
InputCreateS3Request,
|
||||
InputNewCreateRequest,
|
||||
InputService,
|
||||
TestConnectionGetColumnsRequest,
|
||||
TestConnectionRequest,
|
||||
} from '@victorradael/protospack';
|
||||
} from 'protospack';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { InputModel } from 'src/modules/inputs/dtos/input.model';
|
||||
import {
|
||||
objectCamelToSnake,
|
||||
objectSnakeToCamel,
|
||||
} from 'src/utils/CaseConverter';
|
||||
import { mustache } from 'src/utils/mustache';
|
||||
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: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
@Inject('INPUTS_PACKAGE') private readonly grpcClient: ClientGrpc,
|
||||
) {}
|
||||
@Inject('OAUTH_SECRETS') private readonly oauthSecrets: OauthSecrets,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
onModuleInit() {
|
||||
async onModuleInit() {
|
||||
this.inputService =
|
||||
this.grpcClient.getService<InputService>('InputService');
|
||||
}
|
||||
|
||||
async create(createInputDto: InputNewCreateRequest) {
|
||||
console.log('InputClientService', 'Create');
|
||||
async newCreate(createInputDto: InputNewCreateRequest) {
|
||||
this.logger.info('InputClientService - Create');
|
||||
|
||||
const createInputResponse = await new Promise((resolve, reject) => {
|
||||
this.inputService
|
||||
.NewCreate(objectSnakeToCamel(createInputDto))
|
||||
.subscribe({
|
||||
next(x) {
|
||||
resolve(objectCamelToSnake(x));
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
}).catch((err) => {
|
||||
throw new Error(err);
|
||||
const input = await lastValueFrom(
|
||||
this.inputService.NewCreate(objectSnakeToCamel(createInputDto)),
|
||||
);
|
||||
objectCamelToSnake(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
async createGeneric(createInputGeneric) {
|
||||
this.logger.info('InputClientService - Create');
|
||||
const { info, ...data } = createInputGeneric;
|
||||
// data.credentials = await this.getInputTokens(data);
|
||||
if (data.options) delete data.options;
|
||||
if (data.credentials.oauth_code) delete data.credentials.oauth_code;
|
||||
const grpcPayload = {
|
||||
input: EncodeJsonToGrpcStruct(data),
|
||||
info,
|
||||
};
|
||||
objectSnakeToCamel(grpcPayload);
|
||||
|
||||
const structReturn = await lastValueFrom(
|
||||
this.inputService.Create(grpcPayload),
|
||||
).catch((err: { details: string }) => {
|
||||
if (err.details === 'Item Not found!') {
|
||||
throw new NotFoundException('Input not found');
|
||||
}
|
||||
throw new InternalServerErrorException(err.details);
|
||||
});
|
||||
|
||||
return createInputResponse;
|
||||
objectCamelToSnake(structReturn);
|
||||
const inputCreated = DecodeGrpcStruct(structReturn.input);
|
||||
return { input: inputCreated };
|
||||
}
|
||||
|
||||
async createS3Inputs(createInputDto: InputCreateS3Request) {
|
||||
console.log('InputClientService', 'Create');
|
||||
|
||||
const createInputResponse = await new Promise((resolve, reject) => {
|
||||
this.inputService.CreateS3(objectSnakeToCamel(createInputDto)).subscribe({
|
||||
next(x) {
|
||||
resolve(objectCamelToSnake(x));
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.catch((err) => {
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
this.logger.info('InputClientService - Create');
|
||||
objectSnakeToCamel(createInputDto);
|
||||
const createInputResponse = await lastValueFrom(
|
||||
this.inputService.CreateS3(createInputDto),
|
||||
).catch((e) => {
|
||||
throw new HttpException(e.details, 500);
|
||||
});
|
||||
this.logger.info('Done');
|
||||
objectCamelToSnake(createInputResponse);
|
||||
return createInputResponse;
|
||||
}
|
||||
|
||||
async findOne(data: IIdRequest) {
|
||||
console.log('InputClientService', 'FindOne');
|
||||
|
||||
const findOneInputResponse = await new Promise((resolve, reject) => {
|
||||
this.inputService.FindOne(objectSnakeToCamel(data)).subscribe({
|
||||
next(x) {
|
||||
resolve(objectCamelToSnake(x));
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.catch((err) => {
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
this.logger.info('InputClientService - FindOne');
|
||||
const findOneInputResponse = await lastValueFrom(
|
||||
this.inputService.FindOne(objectSnakeToCamel(data)),
|
||||
).catch((e) => {
|
||||
this.logger.error(e.details);
|
||||
throw new HttpException(e.details, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
objectCamelToSnake(findOneInputResponse);
|
||||
return findOneInputResponse;
|
||||
}
|
||||
|
||||
async findAll(data) {
|
||||
console.log('InputClientService', 'FindAll');
|
||||
|
||||
const findAllInputResponse = await new Promise((resolve, reject) => {
|
||||
this.inputService.FindAll(objectSnakeToCamel(data)).subscribe({
|
||||
next(x) {
|
||||
resolve(objectCamelToSnake(x));
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.catch((err) => {
|
||||
throw new Error(err);
|
||||
});
|
||||
this.logger.info('InputClientService - FindAll');
|
||||
const findAllInputResponse = await lastValueFrom(
|
||||
this.inputService.FindAll(objectSnakeToCamel(data)),
|
||||
).catch((e) => {
|
||||
this.logger.error(e.details);
|
||||
throw new HttpException(e.details, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
findAllInputResponse.inputs.forEach((input) => objectCamelToSnake(input));
|
||||
|
||||
return findAllInputResponse;
|
||||
}
|
||||
|
||||
async update(updateInputDTO: UpdateInputRequest) {
|
||||
console.log('InputClientService', 'Update');
|
||||
this.logger.info('InputClientService - Update');
|
||||
const updateInputResponse = await new Promise((resolve, reject) => {
|
||||
this.inputService.Update(objectSnakeToCamel(updateInputDTO)).subscribe({
|
||||
next(x) {
|
||||
resolve(objectCamelToSnake(x));
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -150,7 +147,7 @@ export class InputsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async remove(idRequest: IIdRequest) {
|
||||
console.log('InputClientService', 'Remove');
|
||||
this.logger.info('InputClientService - Remove');
|
||||
|
||||
const removeInputResponse = await new Promise((resolve, reject) => {
|
||||
this.inputService.Remove(objectSnakeToCamel(idRequest)).subscribe({
|
||||
@@ -158,16 +155,19 @@ export class InputsClientService implements OnModuleInit {
|
||||
resolve(objectCamelToSnake(x));
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -175,23 +175,26 @@ export class InputsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async testConnection(data: TestConnectionRequest) {
|
||||
console.log('InputClientService', 'TestConnection');
|
||||
this.logger.info('InputClientService - TestConnection');
|
||||
const testConnectionResponse = await new Promise((resolve, reject) => {
|
||||
this.inputService.NewTestConnection(objectSnakeToCamel(data)).subscribe({
|
||||
next(x) {
|
||||
resolve(objectCamelToSnake(x));
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -199,26 +202,109 @@ export class InputsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async getColumns(data: TestConnectionGetColumnsRequest) {
|
||||
console.log('InputClientService', 'TestConnection/Get-Columns');
|
||||
this.logger.info('InputClientService - TestConnection/Get-Columns');
|
||||
const getColumnsResponse = await new Promise((resolve, reject) => {
|
||||
this.inputService.GetColumns(objectSnakeToCamel(data)).subscribe({
|
||||
next(x) {
|
||||
resolve(objectCamelToSnake(x));
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
return getColumnsResponse;
|
||||
}
|
||||
async getAuthSecrets(plugin) {
|
||||
switch (plugin) {
|
||||
case 'hubspot':
|
||||
return {
|
||||
client_id: this.oauthSecrets.hubspot.client_id || '',
|
||||
client_secret: this.oauthSecrets.hubspot.client_secret || '',
|
||||
};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
async getInputTokens(input: InputModel) {
|
||||
const { credentials } = input;
|
||||
let credentialsTokens = {};
|
||||
switch (credentials.connection_type) {
|
||||
case 'oauth':
|
||||
credentialsTokens = await this.getOauthTokens(input);
|
||||
break;
|
||||
}
|
||||
return { ...credentials, ...credentialsTokens };
|
||||
}
|
||||
async getOauthTokens(input: InputModel) {
|
||||
const tokens: Record<string, any> = {};
|
||||
const { options, credentials } = input;
|
||||
const { oauth } = options;
|
||||
const { get_tokens_url_params, get_tokens_set_response } = oauth;
|
||||
const secrets = await this.getAuthSecrets(input.plugin);
|
||||
const get_tokens_url_params_string = mustache(get_tokens_url_params, {
|
||||
...secrets,
|
||||
...credentials,
|
||||
});
|
||||
let get_tokens_url_params_obj: Record<string, any> = {};
|
||||
try {
|
||||
get_tokens_url_params_obj = JSON.parse(get_tokens_url_params_string);
|
||||
} catch (error) {
|
||||
throw new HttpException('Erro transformando get_tokens_url_params', 400);
|
||||
}
|
||||
let params = {};
|
||||
switch (oauth.content_type) {
|
||||
case 'application/x-www-form-urlencoded':
|
||||
params = new URLSearchParams(get_tokens_url_params_obj);
|
||||
params = params.toString();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const axiosRequestConfig: AxiosRequestConfig = {
|
||||
url: oauth.get_tokens_url,
|
||||
method: oauth.get_tokens_method || 'POST',
|
||||
data: params,
|
||||
headers: { 'content-type': oauth.content_type || 'application/json' },
|
||||
};
|
||||
|
||||
const { data } = await axios(axiosRequestConfig).catch((err) => {
|
||||
this.logger.error(err.response.data);
|
||||
throw new HttpException(err.response.data.message, err.response.status);
|
||||
});
|
||||
for (const key in get_tokens_set_response) {
|
||||
const responseKey = get_tokens_set_response[key];
|
||||
tokens[key] = data[responseKey];
|
||||
}
|
||||
const { redirect_uri } = get_tokens_url_params_obj;
|
||||
tokens.redirect_uri = redirect_uri;
|
||||
return tokens;
|
||||
}
|
||||
|
||||
async getAvailableEntities(data) {
|
||||
this.logger.info('InputClientService - GetAvailableEntities');
|
||||
objectSnakeToCamel(data);
|
||||
const response = await lastValueFrom(
|
||||
this.inputService.GetAvailableEntities(data),
|
||||
).catch((err) => {
|
||||
this.logger.info(err.details);
|
||||
if (err.details && err.details.includes('400'))
|
||||
throw new HttpException('Plugin inválido', HttpStatus.BAD_REQUEST);
|
||||
throw new HttpException(err.details, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
objectCamelToSnake(response);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+3
-1
@@ -29,8 +29,10 @@ interface Cron {
|
||||
}
|
||||
|
||||
export interface Info {
|
||||
costumer_id: string;
|
||||
customer_id: string;
|
||||
user_id: string;
|
||||
customer: string;
|
||||
customer_tier: string;
|
||||
}
|
||||
|
||||
export interface ICreateInputRequest {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OutputPackages, OutputProtoFilePath } from '@victorradael/protospack';
|
||||
import { OutputPackages, OutputProtoFilePath } from 'protospack';
|
||||
import { ClientOptions, Transport } from '@nestjs/microservices';
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Controller, Inject, OnModuleInit } from '@nestjs/common';
|
||||
import { ClientGrpc, Payload } from '@nestjs/microservices';
|
||||
import {
|
||||
OutputServicesNames,
|
||||
OutputsServiceInterface,
|
||||
} from '@victorradael/protospack';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
import { OutputServicesNames, OutputsServiceInterface } from 'protospack';
|
||||
import {
|
||||
objectCamelToSnake,
|
||||
objectSnakeToCamel,
|
||||
@@ -17,9 +15,14 @@ import {
|
||||
@Controller('output')
|
||||
export class OutputsClientService implements OnModuleInit {
|
||||
private outputService: OutputsServiceInterface;
|
||||
logger: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
@Inject('OUTPUTS_PACKAGE') private readonly grpcClient: ClientGrpc,
|
||||
) {}
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
onModuleInit() {
|
||||
this.outputService = this.grpcClient.getService<OutputsServiceInterface>(
|
||||
@@ -28,7 +31,7 @@ export class OutputsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async create(@Payload() createOutputDto: ICreateOutputRequest) {
|
||||
console.log('OutputClientService', 'Create');
|
||||
this.logger.info('OutputClientService - Create');
|
||||
|
||||
const createOutputResponse = await new Promise((resolve, reject) => {
|
||||
this.outputService.Create(objectSnakeToCamel(createOutputDto)).subscribe({
|
||||
@@ -36,16 +39,19 @@ export class OutputsClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -55,7 +61,7 @@ export class OutputsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async findAll(data) {
|
||||
console.log('OutputClientService', 'FindAll');
|
||||
this.logger.info('OutputClientService - FindAll');
|
||||
|
||||
const findAllOutputResponse = await new Promise((resolve, reject) => {
|
||||
this.outputService.FindAll(objectSnakeToCamel(data)).subscribe({
|
||||
@@ -63,16 +69,19 @@ export class OutputsClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -84,7 +93,7 @@ export class OutputsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async findOne(data: IIdRequest) {
|
||||
console.log('OutputClientService', 'FindOne');
|
||||
this.logger.info('OutputClientService - FindOne');
|
||||
|
||||
const findOneOutputResponse = await new Promise((resolve, reject) => {
|
||||
this.outputService.FindOne(objectSnakeToCamel(data)).subscribe({
|
||||
@@ -92,16 +101,19 @@ export class OutputsClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -109,7 +121,7 @@ export class OutputsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async update(updateOutPutDTO: IOutputUpdateRequest) {
|
||||
console.log('OutputClientService', 'Update');
|
||||
this.logger.info('OutputClientService - Update');
|
||||
|
||||
const updateOutputResponse = await new Promise((resolve, reject) => {
|
||||
this.outputService.Update(objectSnakeToCamel(updateOutPutDTO)).subscribe({
|
||||
@@ -117,16 +129,19 @@ export class OutputsClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -134,7 +149,7 @@ export class OutputsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async remove(data: IIdRequest) {
|
||||
console.log('OutputClientService', 'Remove');
|
||||
this.logger.info('OutputClientService - Remove');
|
||||
|
||||
const removeOutputResponse = await new Promise((resolve, reject) => {
|
||||
this.outputService.Remove(objectSnakeToCamel(data)).subscribe({
|
||||
@@ -142,16 +157,19 @@ export class OutputsClientService implements OnModuleInit {
|
||||
resolve(objectCamelToSnake(x));
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { ClientOptions, Transport } from '@nestjs/microservices';
|
||||
import {
|
||||
PipelinePackages,
|
||||
PipelineProtoFilePath,
|
||||
} from '@victorradael/protospack';
|
||||
import { PipelinePackages, PipelineProtoFilePath } from 'protospack';
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
|
||||
export class PipelinesClientConfiguration {
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Inject, OnModuleInit } from '@nestjs/common';
|
||||
import { ClientGrpc, Payload } from '@nestjs/microservices';
|
||||
import {
|
||||
PipelineServicesNames,
|
||||
PipelinesServiceInterface,
|
||||
} from '@victorradael/protospack';
|
||||
BadRequestException,
|
||||
Inject,
|
||||
InternalServerErrorException,
|
||||
OnModuleInit,
|
||||
} from '@nestjs/common';
|
||||
import { ClientGrpc, Payload } from '@nestjs/microservices';
|
||||
import { PipelineServicesNames, PipelinesServiceInterface } from 'protospack';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import {
|
||||
objectCamelToSnake,
|
||||
objectSnakeToCamel,
|
||||
@@ -14,12 +17,18 @@ import {
|
||||
IIdRequest,
|
||||
IUpdatePipelineRequest,
|
||||
} from './interfaces';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
|
||||
export class PipelinesClientService implements OnModuleInit {
|
||||
private pipelineService: PipelinesServiceInterface;
|
||||
logger: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
@Inject('PIPELINES_PACKAGE') private readonly grpcClient: ClientGrpc,
|
||||
) {}
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
onModuleInit() {
|
||||
this.pipelineService =
|
||||
@@ -29,51 +38,47 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async create(@Payload() createPipelineDto: ICreatePipelineDto) {
|
||||
console.log('PipelinesClientService', 'Create');
|
||||
|
||||
const createPipelineResponse = await new Promise((resolve, reject) => {
|
||||
this.pipelineService
|
||||
.Create(objectSnakeToCamel(createPipelineDto))
|
||||
.subscribe({
|
||||
next(x) {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.catch((err) => {
|
||||
throw new Error(err);
|
||||
this.logger.info('PipelinesClientService - Create');
|
||||
const createPipelineResponse = await lastValueFrom(
|
||||
this.pipelineService.Create(objectSnakeToCamel(createPipelineDto)),
|
||||
)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((error: { details: string }) => {
|
||||
if (error.details.includes('INVALID_REQUEST')) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [errorType, message] = error.details.split('|');
|
||||
throw new BadRequestException(message);
|
||||
}
|
||||
throw new InternalServerErrorException(error.details);
|
||||
});
|
||||
|
||||
return createPipelineResponse;
|
||||
}
|
||||
|
||||
async findAll(data: IIdRequest) {
|
||||
console.log('PipelinesClientService', 'FindAll');
|
||||
|
||||
this.logger.info('PipelinesClientService - FindAll');
|
||||
const findAllPipelineResponse = await new Promise((resolve, reject) => {
|
||||
this.pipelineService.FindAll(objectSnakeToCamel(data)).subscribe({
|
||||
next(x) {
|
||||
resolve(x.pipelines);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -81,7 +86,7 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async findOne(data: IIdRequest) {
|
||||
console.log('PipelinesClientService', 'FindOne');
|
||||
this.logger.info('PipelinesClientService - FindOne');
|
||||
|
||||
const findOnePipelineResponse = await new Promise((resolve, reject) => {
|
||||
this.pipelineService.FindOne(objectSnakeToCamel(data)).subscribe({
|
||||
@@ -89,16 +94,19 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -106,7 +114,7 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async update(UpdatePipelineRequest: IUpdatePipelineRequest) {
|
||||
console.log('PipelinesClientService', 'Update');
|
||||
this.logger.info('PipelinesClientService - Update');
|
||||
|
||||
const updatePipelineResponse = await new Promise((resolve, reject) => {
|
||||
this.pipelineService
|
||||
@@ -116,16 +124,19 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
//console.log('done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -133,7 +144,7 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async remove(data: IIdRequest) {
|
||||
console.log('PipelinesClientService', 'Remove');
|
||||
this.logger.info('PipelinesClientService - Remove');
|
||||
|
||||
const removePipelineResponse = await new Promise((resolve, reject) => {
|
||||
this.pipelineService.remove(objectSnakeToCamel(data)).subscribe({
|
||||
@@ -141,16 +152,19 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
resolve(objectCamelToSnake(x));
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// this.logger.info('Done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -158,7 +172,7 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async getPipelineLogsMessages(data: IGetPipelineLogsRequest) {
|
||||
console.log('PipelinesClientService', 'GetPipelineLogsMessages');
|
||||
this.logger.info('PipelinesClientService - GetPipelineLogsMessages');
|
||||
|
||||
const logsPipelineResponse = await new Promise((resolve, reject) => {
|
||||
this.pipelineService.getPipelineLogs(data).subscribe({
|
||||
@@ -166,16 +180,19 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// this.logger.info('Done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -183,7 +200,7 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async getPipelineStatus(data) {
|
||||
console.log('PipelinesClientService', 'GetPipelineStatus');
|
||||
this.logger.info('PipelinesClientService - GetPipelineStatus');
|
||||
|
||||
const statusPipelineResponse = await new Promise((resolve, reject) => {
|
||||
this.pipelineService
|
||||
@@ -200,16 +217,19 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
resolve({ status: statusArray });
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// this.logger.info('Done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -217,7 +237,7 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async runPipeline({ id, info }: IIdRequest) {
|
||||
console.log('PipelinesClientService', 'RunPipeline');
|
||||
this.logger.info('PipelinesClientService - RunPipeline');
|
||||
const statusPipelineResponse = await new Promise((resolve, reject) => {
|
||||
this.pipelineService
|
||||
.triggerPipeline(objectSnakeToCamel({ id, info }))
|
||||
@@ -231,16 +251,20 @@ export class PipelinesClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
this.logger.error('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
//this.logger.info('Done');
|
||||
},
|
||||
});
|
||||
})
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ClientOptions, Transport } from '@nestjs/microservices';
|
||||
import {
|
||||
TransformationPackages,
|
||||
TransformationProtoFilePath,
|
||||
} from '@victorradael/protospack';
|
||||
} from 'protospack';
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
|
||||
export class TransformationsClientConfiguration {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Controller, Inject, OnModuleInit, Post } from '@nestjs/common';
|
||||
import { ClientGrpc } from '@nestjs/microservices';
|
||||
import { TransformationsServiceInterface } from '@victorradael/protospack';
|
||||
import { TransformationsServiceInterface } from 'protospack';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
import {
|
||||
objectCamelToSnake,
|
||||
objectSnakeToCamel,
|
||||
@@ -10,9 +11,14 @@ import { ICreateTransformationsRequest, IIdRequest } from './interfaces';
|
||||
@Controller('transformation')
|
||||
export class TransformationsClientService implements OnModuleInit {
|
||||
private transformationService: TransformationsServiceInterface;
|
||||
logger: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
@Inject('TRANSFORMATIONS_PACKAGE') private readonly grpcClient: ClientGrpc,
|
||||
) {}
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
onModuleInit() {
|
||||
this.transformationService =
|
||||
@@ -23,7 +29,7 @@ export class TransformationsClientService implements OnModuleInit {
|
||||
|
||||
@Post()
|
||||
async create(createTransformationsDto: ICreateTransformationsRequest) {
|
||||
console.log('TransformationClientService', 'Create');
|
||||
this.logger.info('TransformationClientService - Create');
|
||||
|
||||
const createTransformationResponse = await new Promise(
|
||||
(resolve, reject) => {
|
||||
@@ -34,17 +40,20 @@ export class TransformationsClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
},
|
||||
)
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -58,7 +67,7 @@ export class TransformationsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async findAll(data) {
|
||||
console.log('TransformationClientService', 'FindAll');
|
||||
this.logger.info('TransformationClientService - FindAll');
|
||||
|
||||
const findAllTransformationResponse = await new Promise(
|
||||
(resolve, reject) => {
|
||||
@@ -67,17 +76,20 @@ export class TransformationsClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
},
|
||||
)
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
const convertedTransforms = findAllTransformationResponse[
|
||||
@@ -89,7 +101,7 @@ export class TransformationsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async findOne(data: IIdRequest) {
|
||||
console.log('TransformationClientService', 'FindOne');
|
||||
this.logger.info('TransformationClientService - FindOne');
|
||||
|
||||
const findOneTransformationResponse = await new Promise(
|
||||
(resolve, reject) => {
|
||||
@@ -98,17 +110,20 @@ export class TransformationsClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
},
|
||||
)
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -116,7 +131,7 @@ export class TransformationsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async update(updateTransformationDTO) {
|
||||
console.log('TransformationClientService', 'Update');
|
||||
this.logger.info('TransformationClientService - Update');
|
||||
|
||||
const updateTransformationResponse = await new Promise(
|
||||
(resolve, reject) => {
|
||||
@@ -127,17 +142,20 @@ export class TransformationsClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
},
|
||||
)
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
@@ -145,7 +163,7 @@ export class TransformationsClientService implements OnModuleInit {
|
||||
}
|
||||
|
||||
async remove(data: IIdRequest) {
|
||||
console.log('TransformationClientService', 'Remove');
|
||||
this.logger.info('TransformationClientService - Remove');
|
||||
|
||||
const removeTransformationResponse = await new Promise(
|
||||
(resolve, reject) => {
|
||||
@@ -154,17 +172,20 @@ export class TransformationsClientService implements OnModuleInit {
|
||||
resolve(x);
|
||||
},
|
||||
error(err) {
|
||||
console.log('Observable Error');
|
||||
reject(err);
|
||||
},
|
||||
complete() {
|
||||
console.log('done');
|
||||
// console.log('done');
|
||||
},
|
||||
});
|
||||
},
|
||||
)
|
||||
.then((res) => res)
|
||||
.then((res) => {
|
||||
this.logger.info('Done');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err.message);
|
||||
throw new Error(err);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';
|
||||
import { Response } from 'express';
|
||||
|
||||
import ErrorBuilder from '../utils/ErrorBuilder';
|
||||
|
||||
@Catch(Error)
|
||||
export class GrpcToHttpExceptionFilter implements ExceptionFilter {
|
||||
catch(exception: any, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
|
||||
// return this exception directly if is already of type ErrorBuilder,
|
||||
// else transform it using the ErrorBuilder
|
||||
const err =
|
||||
exception.constructor.name === ErrorBuilder.name
|
||||
? exception
|
||||
: new ErrorBuilder(exception.details);
|
||||
|
||||
const { statusCode } = err.response;
|
||||
return response.status(statusCode).json(err.response);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { HttpExceptionFilter } from './http-exception.filter';
|
||||
|
||||
describe('HttpExceptionFilter', () => {
|
||||
it('should be defined', () => {
|
||||
expect(new HttpExceptionFilter()).toBeDefined();
|
||||
});
|
||||
});
|
||||
+23
-5
@@ -1,12 +1,21 @@
|
||||
if (process.env.ENV !== 'local') require('elastic-apm-node/start');
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import { writeFileSync } from 'fs';
|
||||
import helmet from 'helmet';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
|
||||
import documentEmpty from '../swagger_empty.json';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
DadosferaLogger.setupLogger({
|
||||
serviceName: 'maestro',
|
||||
serviceEnvironment: process.env.ENV,
|
||||
});
|
||||
const logger = new DadosferaLogger();
|
||||
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
logger,
|
||||
cors: {
|
||||
origin: '*',
|
||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
@@ -18,15 +27,24 @@ async function bootstrap() {
|
||||
app.use(helmet());
|
||||
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Maestro Grpc Documentation')
|
||||
.setTitle('Maestro')
|
||||
.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);
|
||||
writeFileSync('./swagger.json', JSON.stringify(document));
|
||||
SwaggerModule.setup('api', app, document);
|
||||
|
||||
if (process.env.ENV != 'stg' && process.env.ENV != 'prd')
|
||||
SwaggerModule.setup('api', app, document);
|
||||
|
||||
const swaggerDoc = process.env.ENV === 'dev' ? document : documentEmpty;
|
||||
SwaggerModule.setup('api', app, swaggerDoc);
|
||||
|
||||
await app.listen(3333);
|
||||
}
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
ForbiddenException,
|
||||
InternalServerErrorException,
|
||||
NestMiddleware,
|
||||
UnauthorizedException,
|
||||
UseFilters,
|
||||
} from '@nestjs/common';
|
||||
import jwkToPem from 'jwk-to-pem';
|
||||
import { decode, verify } from 'jsonwebtoken';
|
||||
import { HttpExceptionFilter } from '../error/http-exception.filter';
|
||||
|
||||
@UseFilters(new HttpExceptionFilter())
|
||||
export class LoggerMiddleware implements NestMiddleware {
|
||||
use = async (request: Request, response: Response, next: NextFunction) => {
|
||||
const idToken = request.get('Dadosfera-User');
|
||||
const accessToken = request.get('Authorization');
|
||||
const privateKey = process.env.JWT_PRIVATE_KEY;
|
||||
let requiredRoute = '';
|
||||
const requiredMethod = request.method.trim();
|
||||
if (request.route.path.split('/').length >= 2) {
|
||||
requiredRoute = request.route.path.split('/')[1].trim();
|
||||
}
|
||||
|
||||
verify(idToken, privateKey, (err) => {
|
||||
if (err) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
});
|
||||
const jwtDecoded: any = decode(idToken);
|
||||
|
||||
const permissions = jwtDecoded.user.permissions;
|
||||
|
||||
const clienId = jwtDecoded.user.customerId;
|
||||
const customer = jwtDecoded.user.customer;
|
||||
const userId = jwtDecoded.user.id;
|
||||
|
||||
await verifyToken(accessToken);
|
||||
|
||||
let havePermission = false;
|
||||
const permission = permissions.find((permission) => {
|
||||
permission = permission.split('/');
|
||||
const method = permission[0].trim();
|
||||
const route = permission[1].trim();
|
||||
|
||||
if (method === requiredMethod && route === requiredRoute) {
|
||||
return permission;
|
||||
}
|
||||
});
|
||||
|
||||
if (permission) {
|
||||
havePermission = true;
|
||||
}
|
||||
|
||||
if (havePermission) {
|
||||
request.body.info = {
|
||||
customer_id: clienId,
|
||||
user_id: userId,
|
||||
customer,
|
||||
};
|
||||
|
||||
next();
|
||||
} else {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let pems: { [key: string]: Record<string, unknown> }[];
|
||||
|
||||
const setUp = async (region: string, id: string) => {
|
||||
const URL = `https://cognito-idp.${region}.amazonaws.com/${id}/.well-known/jwks.json`;
|
||||
|
||||
try {
|
||||
const response = await axios.get(URL);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
const data = await response.data;
|
||||
const { keys } = data;
|
||||
|
||||
pems = keys.map((key: any) => {
|
||||
const modulus = key.n;
|
||||
const exponent = key.e;
|
||||
const keyType = key.kty;
|
||||
const jwk = { kty: keyType, n: modulus, e: exponent };
|
||||
const pem = jwkToPem(jwk);
|
||||
const keyId = key.kid;
|
||||
|
||||
return { [keyId]: pem };
|
||||
});
|
||||
} catch (error) {
|
||||
// console.log(error);
|
||||
// console.log('Error! Unable to download JWKs');
|
||||
}
|
||||
};
|
||||
|
||||
const verifyToken = async (accessToken: string) => {
|
||||
const awsRegion = process.env.AWS_REGION;
|
||||
const awsPoolId = process.env.AWS_IDENTITY_POOL_ID;
|
||||
|
||||
try {
|
||||
await setUp(awsRegion, awsPoolId);
|
||||
if (!accessToken) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
const user: any = decode(accessToken, { complete: true });
|
||||
|
||||
if (user === null) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
const { kid } = user.header;
|
||||
const pem = pems.filter((item: any) => item[kid]);
|
||||
const pemValue: any = pem[0][kid];
|
||||
|
||||
if (!pem) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
verify(accessToken, pemValue, (err: any) => {
|
||||
if (err) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
} catch (error) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
};
|
||||
@@ -5,56 +5,46 @@ import {
|
||||
Post,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Inject,
|
||||
UseFilters,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
AuthSignInRequest,
|
||||
AuthRefreshAccessTokenRequest,
|
||||
AuthEnableTotpMfaRequest,
|
||||
AuthDisableTotpMfaRequest,
|
||||
AuthVerifyTotpMfaRequest,
|
||||
AuthChangePasswordRequest,
|
||||
AuthResetPasswordRequest,
|
||||
AuthVerifyResetPasswordCodeRequest,
|
||||
AuthConfirmResetPasswordRequest,
|
||||
} from '@victorradael/protospack';
|
||||
AuthEnableTotpMfaRequest,
|
||||
AuthDisableTotpMfaRequest,
|
||||
AuthVerifyTotpMfaRequest,
|
||||
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
|
||||
|
||||
import { AuthClientService } from 'src/clients/auth/client.service';
|
||||
|
||||
import ErrorBuilder from '../../utils/ErrorBuilder';
|
||||
import { AuthClientService } from './auth.service';
|
||||
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 {
|
||||
constructor(private authClient: AuthClientService) {}
|
||||
logger: DadosferaLogger;
|
||||
|
||||
// DEPRECADO!
|
||||
@Post()
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async signIn_old(@Body() body: AuthSignInRequest) {
|
||||
console.log(`/auth`, 'SignIn_old');
|
||||
|
||||
return this.signIn(body);
|
||||
}
|
||||
|
||||
@Post('login')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async signIn_login(@Body() body: AuthSignInRequest) {
|
||||
console.log(`/auth`, 'SignIn_login');
|
||||
|
||||
return this.signIn(body);
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
private authClient: AuthClientService,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
@Post('sign-in')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async signIn(@Body() { username, password, totp }: AuthSignInRequest) {
|
||||
console.log(`/auth`, 'SignIn');
|
||||
this.logger.info('/auth - SignIn');
|
||||
|
||||
const tokens = await this.authClient
|
||||
.signIn({ username, password, totp })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return tokens;
|
||||
return this.authClient.signIn({ username, password, totp });
|
||||
}
|
||||
|
||||
@Post('refresh-access-token')
|
||||
@@ -62,15 +52,63 @@ export class AuthController {
|
||||
async refreshAccessToken(
|
||||
@Body() { refreshToken }: AuthRefreshAccessTokenRequest,
|
||||
) {
|
||||
console.log(`/auth`, 'RefreshAccessToken');
|
||||
this.logger.info('/auth - RefreshAccessToken');
|
||||
|
||||
const accessToken = await this.authClient
|
||||
.refreshAccessToken({ refreshToken })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
return this.authClient.refreshAccessToken({ refreshToken });
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
@Post('change-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async changePassword(
|
||||
@Body() body: AuthChangePasswordRequest,
|
||||
@Headers() headers,
|
||||
) {
|
||||
this.logger.info('/auth - change-password');
|
||||
|
||||
const { oldPassword, newPassword } = body;
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
return this.authClient.changePassword({
|
||||
accessToken,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
});
|
||||
}
|
||||
|
||||
@Post('reset-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async resetPassword(@Body() body: AuthResetPasswordRequest) {
|
||||
this.logger.info('/auth - reset-password');
|
||||
|
||||
const { username } = body;
|
||||
|
||||
return this.authClient.resetPassword({ username });
|
||||
}
|
||||
|
||||
@Post('verify-reset-password-code')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async verifyResetPasswordCode(
|
||||
@Body() body: AuthVerifyResetPasswordCodeRequest,
|
||||
) {
|
||||
this.logger.info('/auth - verify-reset-password-code');
|
||||
|
||||
const { username, code } = body;
|
||||
|
||||
return this.authClient.verifyResetPasswordCode({ username, code });
|
||||
}
|
||||
|
||||
@Post('confirm-reset-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async confirmResetPassword(@Body() body: AuthConfirmResetPasswordRequest) {
|
||||
this.logger.info('/auth - confirm-reset-password');
|
||||
|
||||
const { username, code, newPassword } = body;
|
||||
|
||||
return this.authClient.confirmResetPassword({
|
||||
username,
|
||||
code,
|
||||
newPassword,
|
||||
});
|
||||
}
|
||||
|
||||
@Post('enable-totp')
|
||||
@@ -79,18 +117,12 @@ export class AuthController {
|
||||
@Body() body: AuthEnableTotpMfaRequest,
|
||||
@Headers() headers,
|
||||
) {
|
||||
console.log(`/auth`, 'enable-totp');
|
||||
this.logger.info('/auth - enable-totp');
|
||||
|
||||
const { password } = body;
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
const response = await this.authClient
|
||||
.enableTotpMFA({ accessToken, password })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
return this.authClient.enableTotpMFA({ accessToken, password });
|
||||
}
|
||||
|
||||
@Post('disable-totp')
|
||||
@@ -99,129 +131,32 @@ export class AuthController {
|
||||
@Body() body: AuthDisableTotpMfaRequest,
|
||||
@Headers() headers,
|
||||
) {
|
||||
console.log(`/auth`, 'disable-totp');
|
||||
this.logger.info('/auth - disable-totp');
|
||||
|
||||
const { password } = body;
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
const response = await this.authClient
|
||||
.disableTotpMFA({ accessToken, password })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
return this.authClient.disableTotpMFA({ accessToken, password });
|
||||
}
|
||||
|
||||
@Post('dismiss-totp')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async dismissTotpMFA(@Headers() headers) {
|
||||
console.log(`/auth`, 'disable-totp');
|
||||
this.logger.info('/auth - disable-totp');
|
||||
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
const response = await this.authClient
|
||||
.dismissTotpMFA({ accessToken })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
return this.authClient.dismissTotpMFA({ accessToken });
|
||||
}
|
||||
|
||||
@Post('verify-totp')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async verifyTotp(
|
||||
@Body() body: AuthVerifyTotpMfaRequest,
|
||||
@Headers() headers,
|
||||
): Promise<any> {
|
||||
console.log(`/auth`, 'enable-totp');
|
||||
async verifyTotp(@Body() body: AuthVerifyTotpMfaRequest, @Headers() headers) {
|
||||
this.logger.info('/auth - enable-totp');
|
||||
|
||||
const { totp } = body;
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
const response = await this.authClient
|
||||
.verifyTotp({ accessToken, totp })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('change-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async changePassword(
|
||||
@Body() body: AuthChangePasswordRequest,
|
||||
@Headers() headers,
|
||||
): Promise<any> {
|
||||
console.log(`/auth`, 'change-password');
|
||||
|
||||
const { oldPassword, newPassword } = body;
|
||||
const { authorization: accessToken } = headers;
|
||||
|
||||
const response = await this.authClient
|
||||
.changePassword({
|
||||
accessToken,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
})
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('reset-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async resetPassword(@Body() body: AuthResetPasswordRequest): Promise<any> {
|
||||
console.log(`/auth`, 'reset-password');
|
||||
|
||||
const { username } = body;
|
||||
|
||||
const response = await this.authClient
|
||||
.resetPassword({ username })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('verify-reset-password-code')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async verifyResetPasswordCode(
|
||||
@Body() body: AuthVerifyResetPasswordCodeRequest,
|
||||
): Promise<any> {
|
||||
console.log(`/auth`, 'verify-reset-password-code');
|
||||
|
||||
const { username, code } = body;
|
||||
|
||||
const response = await this.authClient
|
||||
.verifyResetPasswordCode({ username, code })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('confirm-reset-password')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async confirmResetPassword(
|
||||
@Body() body: AuthConfirmResetPasswordRequest,
|
||||
): Promise<any> {
|
||||
console.log(`/auth`, 'confirm-reset-password');
|
||||
|
||||
const { username, code, newPassword } = body;
|
||||
|
||||
const response = await this.authClient
|
||||
.confirmResetPassword({ username, code, newPassword })
|
||||
.catch((err) => {
|
||||
throw ErrorBuilder(err);
|
||||
});
|
||||
|
||||
return response;
|
||||
return this.authClient.verifyTotp({ accessToken, totp });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ClientsModule } from '@nestjs/microservices';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
|
||||
import { AuthController } from './auth.controller';
|
||||
import { AuthClientService } from './auth.service';
|
||||
|
||||
import { DucClient } from '../../clients/duc/client.config';
|
||||
const ducClient = new DucClient();
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ClientsModule.register([
|
||||
{
|
||||
name: 'DUC_PACKAGE',
|
||||
...ducClient.config(),
|
||||
},
|
||||
]),
|
||||
],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthClientService, DadosferaLogger],
|
||||
exports: [AuthClientService],
|
||||
})
|
||||
export class AuthModule {}
|
||||
@@ -1,152 +0,0 @@
|
||||
import nock from 'nock';
|
||||
import { LoggerMiddleware } from '../../middlewares/authentication';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { ConsoleLogger, UnauthorizedException } from '@nestjs/common';
|
||||
|
||||
describe('PipelinesGrpcServerService', () => {
|
||||
// let mockRequest: Partial<Request>;
|
||||
// let mockResponse: Partial<Response>;
|
||||
// const nextFunction: NextFunction = jest.fn();
|
||||
|
||||
// beforeEach(() => {
|
||||
// mockRequest = {
|
||||
// headers: {},
|
||||
// body: {
|
||||
// info: {},
|
||||
// },
|
||||
// };
|
||||
// mockResponse = {
|
||||
// json: jest.fn(),
|
||||
// };
|
||||
// });
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(2 + 2).toBe(4);
|
||||
});
|
||||
|
||||
// it('Should be able to pass auth', async () => {
|
||||
// const awsRegion = process.env.AWS_REGION;
|
||||
// const awsPoolId = process.env.AWS_IDENTITY_POOL_ID;
|
||||
// const cognitoRes = {
|
||||
// keys: [
|
||||
// {
|
||||
// alg: 'RS256',
|
||||
// e: 'AQAB',
|
||||
// kid: 'z3VA3+i9JZY3JFDJTNWOap8RY+B7C6mFedaqCuvLvqA=',
|
||||
// kty: 'RSA',
|
||||
// n: 'vIGCQDkPA_eQUoaGDUsCyK_Whr76mNW0kZ1uac6ZnyY6hsjUIjvwehqv-ux3cQo4rQZQVFcoh8n9cK5jguz4GVdD972vIxoEdyv32nBFVr5e1PBunKJ2Y32GTR_Hl0XiE0hRe1v6cWuTqiC4qm1NY7tXYL3mI9L6s9ztNbhmG_V44y26PdhL4vRVrJaHOAmCs-77U-QYAC7Llkpmjh-8tG1zt9_FJ237cpBUVOuhD-7Nm32_eB9wddBGfBw0F10ko_KJoU-7683_Kv8k9coJzFUSONId-bfnLOzs8j1L6ZAHipXCR7rpmuRYzXztjp4Wmm2zPUToWLdMEy0Hq1OWmw',
|
||||
// use: 'sig',
|
||||
// },
|
||||
// {
|
||||
// alg: 'RS256',
|
||||
// e: 'AQAB',
|
||||
// kid: 'u+1W9pi+clp8LaPhZVrv4dXMqRkTRD02YWhbm0NvsUw=',
|
||||
// kty: 'RSA',
|
||||
// n: 'noGq1cRMAKJPWahqfC_zWasYovSUycaS1basMfEoh3ePLc9zRgmyfiVKYzLRosHMe1uk0Y5ekCBKnWA8Yl7I84Yt7IIIaE44oJjSEGBKT3m8i8YaXzawaNs63KPkRh8553o3KzL75bQWcI_ABKqkf-uAKSCl_XotBGkzLUl4hIOYtAtRGEfKaNMPqyTCT5Zn71pMd0isppaUiTW2T5QLsZV1IBp46aSrl_D5Q_FTsJT7feobQVoHp2zfIorCpkfTXBTBMQTEBFSDyPbc6cULl48VKxp0B0GEwR_kYCEHEVzf41LQcUWZUE0OdBychijkSc9MZJnBWUYQZyedJILWnQ',
|
||||
// use: 'sig',
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
// nock(
|
||||
// `https://cognito-idp.${awsRegion}.amazonaws.com/${awsPoolId}/.well-known/jwks.json`,
|
||||
// )
|
||||
// .persist()
|
||||
// .get('')
|
||||
// .reply(200, cognitoRes);
|
||||
|
||||
// mockRequest.headers['Dadosfera-User'] =
|
||||
// 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiZTljYjI1ZGEtMTQyOC00NDJjLTg5NjItYTdkNmQxNjhkODUyIiwibmFtZSI6InJvZHJpZ28uemFtYm9uaSIsInVzZXJuYW1lIjoicm9kcmlnby56YW1ib25pQGRhZG9zZmVyYS5haSIsInJ1bGVJZCI6IjIxNzdmZjg4LThmY2ItNDRlNS1hYzYxLWNjYmU2Y2M3MGU4ZCIsImN1c3RvbWVySWQiOiIxMTI5ODBhMy0wYTEyLTQxYzktYmZmZC03OTFjZjdjYTg3YTIiLCJtZmFTdGF0dXMiOiJwZW5kaW5nIiwiY3JlYXRlZEF0IjoiMjAyMi0wNS0xM1QxNTowOTowOS4xMzhaIiwidXBkYXRlZEF0IjoiMjAyMi0wNS0xM1QxNTowOTowOS4xMzhaIiwicGVybWlzc2lvbnMiOlsiUE9TVCAvcGlwZWxpbmVzIiwiR0VUIC9waXBlbGluZXMiLCJQVVQgL3BpcGVsaW5lcyIsIkRFTEVURSAvcGlwZWxpbmVzIiwiUE9TVCAvaW5wdXRzIiwiR0VUIC9pbnB1dHMiLCJQVVQgL2lucHV0cyIsIkRFTEVURSAvaW5wdXRzIiwiUE9TVCAvb3V0cHV0cyIsIkdFVCAvb3V0cHV0cyIsIlBVVCAvb3V0cHV0cyIsIkRFTEVURSAvb3V0cHV0cyIsIlBPU1QgL3RyYW5zZm9ybWF0aW9ucyIsIkdFVCAvdHJhbnNmb3JtYXRpb25zIiwiUFVUIC90cmFuc2Zvcm1hdGlvbnMiLCJERUxFVEUgL3RyYW5zZm9ybWF0aW9ucyIsIlBPU1QgL2NhdGFsb2ciLCJHRVQgL2NhdGFsb2ciLCJQVVQgL2NhdGFsb2ciLCJERUxFVEUgL2NhdGFsb2ciLCJQT1NUIC9hdXRoIiwiR0VUIC9tZXRhYmFzZSIsIkdFVCAvc25vd2ZsYWtlIl0sImN1c3RvbWVyIjoiZGFkb3NmZXJhIn0sImlhdCI6MTY1MzY1NjE2NywiZXhwIjoxNjUzNzQyNTY3fQ.eeNEEUk_95KOxM-objS7gXz-SCVkNFYpEP688MfLkdI';
|
||||
// mockRequest.headers['Authorization'] =
|
||||
// 'eyJraWQiOiJ1KzFXOXBpK2NscDhMYVBoWlZydjRkWE1xUmtUUkQwMllXaGJtME52c1V3PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiIxYjkyZmFkMC1iNWVlLTQwMzAtOGRmYy1hZWM0MGQzYzdkYmUiLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cL3VzLWVhc3QtMV9OVXY3WTJTeGoiLCJjbGllbnRfaWQiOiI0N3RrY3Jxc2NuajY3bWhnYmg3dXQ1YnJzNCIsIm9yaWdpbl9qdGkiOiJhZWJjNjA5NC1hYTJmLTRiZmUtOWExZi01ZWVhYTVmYzAwNDAiLCJldmVudF9pZCI6IjBhY2NjMzJiLTllNzktNGY2NS1iMDFiLWZhZGJlYzFhZWFiNiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE2NTM2NTYxNjcsImV4cCI6MTY1MzY1Nzk2NywiaWF0IjoxNjUzNjU2MTY3LCJqdGkiOiIyYTgzNDhmOC0xNDhlLTQ3NjctODcxYi00M2UyOGRjNjc2NTkiLCJ1c2VybmFtZSI6InJvZHJpZ28uemFtYm9uaUBkYWRvc2ZlcmEuYWkifQ.UgIgeGEH2olNgqPly8cymNgWIZJz5n9N4yeKgTusfGsu5h-TWP_jVhPoCvFoixO2XKzFjSCvKwlKQYZyB74jLQLs-j5C5KGBdOea1HX7pUnEfEVtBINd1kcHib5YpGYq3MHG1uVx8vNJS3XviwgYg4rgNWA4du-QbhMicdRyHolYM-dWxuxtkwTRWMe1Vw6KlTctFsvUWx1GMgjp37tsLONcp3B8OsYXfiR764K_pBNs5gMhJ39gJ7NHHLWkJrtrUTpIoHWaZS_HEgvVyQiJENQvK_bPDMPm_lyF1dW-JBgot4TpAxMmvV1XGabkzycIcAOwUaPsKBlMphgunz1Ffw';
|
||||
// mockRequest.method = 'GET';
|
||||
// mockRequest.route = {
|
||||
// path: '/inputs/',
|
||||
// stack: [
|
||||
// {
|
||||
// method: 'get',
|
||||
// },
|
||||
// ],
|
||||
// methods: {
|
||||
// get: true,
|
||||
// },
|
||||
// };
|
||||
// const authMiddleware = new LoggerMiddleware();
|
||||
|
||||
// await authMiddleware.useTest(
|
||||
// mockRequest as Request,
|
||||
// mockResponse as Response,
|
||||
// nextFunction,
|
||||
// );
|
||||
// expect(nextFunction).toHaveBeenCalled();
|
||||
// });
|
||||
|
||||
// it('Should not be able to pass auth', async () => {
|
||||
// const awsRegion = process.env.AWS_REGION;
|
||||
// const awsPoolId = process.env.AWS_IDENTITY_POOL_ID;
|
||||
// const cognitoRes = {
|
||||
// keys: [
|
||||
// {
|
||||
// alg: 'RS256',
|
||||
// e: 'AQAB',
|
||||
// kid: 'z3VA3+i9JZY3JFDJTNWOap8RY+B7C6mFedaqCuvLvqA=',
|
||||
// kty: 'RSA',
|
||||
// n: 'vIGCQDkPA_eQUoaGDUsCyK_Whr76mNW0kZ1uac6ZnyY6hsjUIjvwehqv-ux3cQo4rQZQVFcoh8n9cK5jguz4GVdD972vIxoEdyv32nBFVr5e1PBunKJ2Y32GTR_Hl0XiE0hRe1v6cWuTqiC4qm1NY7tXYL3mI9L6s9ztNbhmG_V44y26PdhL4vRVrJaHOAmCs-77U-QYAC7Llkpmjh-8tG1zt9_FJ237cpBUVOuhD-7Nm32_eB9wddBGfBw0F10ko_KJoU-7683_Kv8k9coJzFUSONId-bfnLOzs8j1L6ZAHipXCR7rpmuRYzXztjp4Wmm2zPUToWLdMEy0Hq1OWmw',
|
||||
// use: 'sig',
|
||||
// },
|
||||
// {
|
||||
// alg: 'RS256',
|
||||
// e: 'AQAB',
|
||||
// kid: 'u+1W9pi+clp8LaPhZVrv4dXMqRkTRD02YWhbm0NvsUw=',
|
||||
// kty: 'RSA',
|
||||
// n: 'noGq1cRMAKJPWahqfC_zWasYovSUycaS1basMfEoh3ePLc9zRgmyfiVKYzLRosHMe1uk0Y5ekCBKnWA8Yl7I84Yt7IIIaE44oJjSEGBKT3m8i8YaXzawaNs63KPkRh8553o3KzL75bQWcI_ABKqkf-uAKSCl_XotBGkzLUl4hIOYtAtRGEfKaNMPqyTCT5Zn71pMd0isppaUiTW2T5QLsZV1IBp46aSrl_D5Q_FTsJT7feobQVoHp2zfIorCpkfTXBTBMQTEBFSDyPbc6cULl48VKxp0B0GEwR_kYCEHEVzf41LQcUWZUE0OdBychijkSc9MZJnBWUYQZyedJILWnQ',
|
||||
// use: 'sig',
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
// nock(
|
||||
// `https://cognito-idp.${awsRegion}.amazonaws.com/${awsPoolId}/.well-known/jwks.json`,
|
||||
// )
|
||||
// .persist()
|
||||
// .get('')
|
||||
// .reply(200, cognitoRes);
|
||||
|
||||
// mockRequest.headers['Dadosfera-User'] =
|
||||
// 'asdeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiZTljYjI1ZGEtMTQyOC00NDJjLTg5NjItYTdkNmQxNjhkODUyIiwibmFtZSI6InJvZHJpZ28uemFtYm9uaSIsInVzZXJuYW1lIjoicm9kcmlnby56YW1ib25pQGRhZG9zZmVyYS5haSIsInJ1bGVJZCI6IjIxNzdmZjg4LThmY2ItNDRlNS1hYzYxLWNjYmU2Y2M3MGU4ZCIsImN1c3RvbWVySWQiOiIxMTI5ODBhMy0wYTEyLTQxYzktYmZmZC03OTFjZjdjYTg3YTIiLCJtZmFTdGF0dXMiOiJwZW5kaW5nIiwiY3JlYXRlZEF0IjoiMjAyMi0wNS0xM1QxNTowOTowOS4xMzhaIiwidXBkYXRlZEF0IjoiMjAyMi0wNS0xM1QxNTowOTowOS4xMzhaIiwicGVybWlzc2lvbnMiOlsiUE9TVCAvcGlwZWxpbmVzIiwiR0VUIC9waXBlbGluZXMiLCJQVVQgL3BpcGVsaW5lcyIsIkRFTEVURSAvcGlwZWxpbmVzIiwiUE9TVCAvaW5wdXRzIiwiR0VUIC9pbnB1dHMiLCJQVVQgL2lucHV0cyIsIkRFTEVURSAvaW5wdXRzIiwiUE9TVCAvb3V0cHV0cyIsIkdFVCAvb3V0cHV0cyIsIlBVVCAvb3V0cHV0cyIsIkRFTEVURSAvb3V0cHV0cyIsIlBPU1QgL3RyYW5zZm9ybWF0aW9ucyIsIkdFVCAvdHJhbnNmb3JtYXRpb25zIiwiUFVUIC90cmFuc2Zvcm1hdGlvbnMiLCJERUxFVEUgL3RyYW5zZm9ybWF0aW9ucyIsIlBPU1QgL2NhdGFsb2ciLCJHRVQgL2NhdGFsb2ciLCJQVVQgL2NhdGFsb2ciLCJERUxFVEUgL2NhdGFsb2ciLCJQT1NUIC9hdXRoIiwiR0VUIC9tZXRhYmFzZSIsIkdFVCAvc25vd2ZsYWtlIl0sImN1c3RvbWVyIjoiZGFkb3NmZXJhIn0sImlhdCI6MTY1MzY1NjE2NywiZXhwIjoxNjUzNzQyNTY3fQ.eeNEEUk_95KOxM-objS7gXz-SCVkNFYpEP688MfLkdI';
|
||||
// mockRequest.headers['Authorization'] =
|
||||
// 'asdeyJraWQiOiJ1KzFXOXBpK2NscDhMYVBoWlZydjRkWE1xUmtUUkQwMllXaGJtME52c1V3PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiIxYjkyZmFkMC1iNWVlLTQwMzAtOGRmYy1hZWM0MGQzYzdkYmUiLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cL3VzLWVhc3QtMV9OVXY3WTJTeGoiLCJjbGllbnRfaWQiOiI0N3RrY3Jxc2NuajY3bWhnYmg3dXQ1YnJzNCIsIm9yaWdpbl9qdGkiOiJhZWJjNjA5NC1hYTJmLTRiZmUtOWExZi01ZWVhYTVmYzAwNDAiLCJldmVudF9pZCI6IjBhY2NjMzJiLTllNzktNGY2NS1iMDFiLWZhZGJlYzFhZWFiNiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE2NTM2NTYxNjcsImV4cCI6MTY1MzY1Nzk2NywiaWF0IjoxNjUzNjU2MTY3LCJqdGkiOiIyYTgzNDhmOC0xNDhlLTQ3NjctODcxYi00M2UyOGRjNjc2NTkiLCJ1c2VybmFtZSI6InJvZHJpZ28uemFtYm9uaUBkYWRvc2ZlcmEuYWkifQ.UgIgeGEH2olNgqPly8cymNgWIZJz5n9N4yeKgTusfGsu5h-TWP_jVhPoCvFoixO2XKzFjSCvKwlKQYZyB74jLQLs-j5C5KGBdOea1HX7pUnEfEVtBINd1kcHib5YpGYq3MHG1uVx8vNJS3XviwgYg4rgNWA4du-QbhMicdRyHolYM-dWxuxtkwTRWMe1Vw6KlTctFsvUWx1GMgjp37tsLONcp3B8OsYXfiR764K_pBNs5gMhJ39gJ7NHHLWkJrtrUTpIoHWaZS_HEgvVyQiJENQvK_bPDMPm_lyF1dW-JBgot4TpAxMmvV1XGabkzycIcAOwUaPsKBlMphgunz1Ffw';
|
||||
// mockRequest.method = 'GET';
|
||||
// mockRequest.route = {
|
||||
// path: '/inputs/',
|
||||
// stack: [
|
||||
// {
|
||||
// method: 'get',
|
||||
// },
|
||||
// ],
|
||||
// methods: {
|
||||
// get: true,
|
||||
// },
|
||||
// };
|
||||
// const authMiddleware = new LoggerMiddleware();
|
||||
|
||||
// // const t = await authMiddleware.useTest(
|
||||
// // mockRequest as Request,
|
||||
// // mockResponse as Response,
|
||||
// // nextFunction,
|
||||
// // );
|
||||
// // expect(nextFunction).toHaveBeenCalled();
|
||||
|
||||
// expect(async () => {
|
||||
// const t = await authMiddleware.useTest(
|
||||
// mockRequest as Request,
|
||||
// mockResponse as Response,
|
||||
// nextFunction,
|
||||
// );
|
||||
// console.log(t);
|
||||
// }).toThrow('Unauthorized');
|
||||
|
||||
// expect(1).toBe(2);
|
||||
// // expect(t).toThrow(UnauthorizedException);
|
||||
// });
|
||||
});
|
||||
@@ -0,0 +1,140 @@
|
||||
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 { AuthProtoService as AuthServiceInterface } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
|
||||
import {
|
||||
AuthSignInRequest,
|
||||
AuthRefreshAccessTokenRequest,
|
||||
AuthEnableTotpMfaRequest,
|
||||
AuthDisableTotpMfaRequest,
|
||||
AuthDismissTotpMfaRequest,
|
||||
AuthVerifyTotpMfaRequest,
|
||||
AuthChangePasswordRequest,
|
||||
AuthResetPasswordRequest,
|
||||
AuthVerifyResetPasswordCodeRequest,
|
||||
AuthConfirmResetPasswordRequest,
|
||||
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
|
||||
|
||||
@Injectable()
|
||||
export class AuthClientService implements OnModuleInit {
|
||||
logger: DadosferaLogger;
|
||||
|
||||
private authService: AuthServiceInterface;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
@Inject('DUC_PACKAGE') private readonly grpcClient: ClientGrpc,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
onModuleInit() {
|
||||
this.authService = this.grpcClient.getService<AuthServiceInterface>(
|
||||
ProtoServices.AuthProtoService,
|
||||
);
|
||||
}
|
||||
|
||||
async getPublicKeys() {
|
||||
this.logger.info('GetPublicKeys');
|
||||
|
||||
return lastValueFrom(this.authService.AuthGetPublicKeys({}));
|
||||
}
|
||||
|
||||
async signIn({ username, password, totp }: AuthSignInRequest) {
|
||||
this.logger.info('SignIn');
|
||||
|
||||
return lastValueFrom(
|
||||
this.authService.AuthSignIn({ username, password, totp }),
|
||||
);
|
||||
}
|
||||
|
||||
async refreshAccessToken({ refreshToken }: AuthRefreshAccessTokenRequest) {
|
||||
this.logger.info('RefreshAccessToken');
|
||||
|
||||
return lastValueFrom(
|
||||
this.authService.AuthRefreshAccessToken({ refreshToken }),
|
||||
);
|
||||
}
|
||||
|
||||
async changePassword({
|
||||
accessToken,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
}: AuthChangePasswordRequest) {
|
||||
this.logger.info('ChangePassword');
|
||||
|
||||
return lastValueFrom(
|
||||
this.authService.AuthChangePassword({
|
||||
accessToken,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async resetPassword({ username }: AuthResetPasswordRequest) {
|
||||
this.logger.info('resetPassword');
|
||||
|
||||
return lastValueFrom(this.authService.AuthResetPassword({ username }));
|
||||
}
|
||||
|
||||
async verifyResetPasswordCode({
|
||||
username,
|
||||
code,
|
||||
}: AuthVerifyResetPasswordCodeRequest) {
|
||||
this.logger.info('verifyResetPasswordCode');
|
||||
|
||||
return lastValueFrom(
|
||||
this.authService.AuthVerifyResetPasswordCode({ username, code }),
|
||||
);
|
||||
}
|
||||
|
||||
async confirmResetPassword({
|
||||
username,
|
||||
code,
|
||||
newPassword,
|
||||
}: AuthConfirmResetPasswordRequest) {
|
||||
this.logger.info('confirmResetPassword');
|
||||
|
||||
return lastValueFrom(
|
||||
this.authService.AuthConfirmResetPassword({
|
||||
username,
|
||||
code,
|
||||
newPassword,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async enableTotpMFA({ accessToken, password }: AuthEnableTotpMfaRequest) {
|
||||
this.logger.info('enableTotpMFA');
|
||||
|
||||
return lastValueFrom(
|
||||
this.authService.AuthEnableTotpMfa({ accessToken, password }),
|
||||
);
|
||||
}
|
||||
|
||||
async disableTotpMFA({ accessToken, password }: AuthDisableTotpMfaRequest) {
|
||||
this.logger.info('disableTotpMFA');
|
||||
|
||||
return lastValueFrom(
|
||||
this.authService.AuthDisableTotpMfa({ accessToken, password }),
|
||||
);
|
||||
}
|
||||
|
||||
async dismissTotpMFA({ accessToken }: AuthDismissTotpMfaRequest) {
|
||||
this.logger.info('dismissTotpMFA');
|
||||
|
||||
return lastValueFrom(this.authService.AuthDismissTotpMfa({ accessToken }));
|
||||
}
|
||||
|
||||
async verifyTotp({ accessToken, totp }: AuthVerifyTotpMfaRequest) {
|
||||
this.logger.info('disableTotpMFA');
|
||||
|
||||
return lastValueFrom(
|
||||
this.authService.AuthVerifyTotpMfa({ accessToken, totp }),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class LoginDto {
|
||||
@ApiProperty()
|
||||
username: string;
|
||||
|
||||
@ApiProperty()
|
||||
password: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
totp: string;
|
||||
}
|
||||
@@ -3,19 +3,55 @@ import {
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AuthenticateCondition } from '../../authentication/authentication.decorator';
|
||||
import { PERMISSIONS } from '../../authentication/permissions.enum';
|
||||
import { CatalogService } from './catalog.service';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
|
||||
@ApiTags('Catalog')
|
||||
@Controller('catalog')
|
||||
@AuthenticateCondition((req, user) => {
|
||||
let action;
|
||||
|
||||
switch (req.method) {
|
||||
case 'POST':
|
||||
action = 'CREATE';
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
action = 'UPDATE';
|
||||
break;
|
||||
|
||||
default:
|
||||
action = req.method;
|
||||
}
|
||||
|
||||
return user.permissions.includes(
|
||||
PERMISSIONS.CATALOG.permissions[action].seqid,
|
||||
);
|
||||
})
|
||||
export class CatalogController {
|
||||
constructor(private catalogService: CatalogService) {}
|
||||
logger: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
private catalogService: CatalogService,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
@Get('all')
|
||||
async catalogAll(@Body() body) {
|
||||
console.log(`/catalog`, 'ON CATALOG ALL ROUTE');
|
||||
this.logger.info(`/catalog - ON CATALOG ALL ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.catalogAll(body);
|
||||
|
||||
@@ -24,7 +60,10 @@ export class CatalogController {
|
||||
|
||||
@Get('data_apps')
|
||||
async dataAppsAll(@Body() body) {
|
||||
console.log(`/catalog`, 'ON FIND ALL DATA APPS ROUTE');
|
||||
this.logger.info(`/catalog - ON FIND ALL DATA APPS ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.dataAppsAll(body);
|
||||
|
||||
@@ -33,7 +72,10 @@ export class CatalogController {
|
||||
|
||||
@Delete('data_apps/:id')
|
||||
async dataAppsOne(@Param() params, @Body() body) {
|
||||
console.log(`/catalog`, 'ON FIND ONE DATA APP ROUTE');
|
||||
this.logger.info(`/catalog - ON FIND ONE DATA APP ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
const { id } = params;
|
||||
|
||||
const res = await this.catalogService.dataAppsOne(id, body);
|
||||
@@ -43,7 +85,10 @@ export class CatalogController {
|
||||
|
||||
@Get('dashboard-metabase')
|
||||
async getAllDashboardMetabase(@Body() body) {
|
||||
console.log(`/catalog`, 'ON GET ALL DASHBOARDS METABASE ROUTE');
|
||||
this.logger.info(`/catalog - ON GET ALL DASHBOARDS METABASE ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.getAllDashboardMetabase(body);
|
||||
|
||||
@@ -53,7 +98,10 @@ export class CatalogController {
|
||||
@Get('dashboard-metabase/:id')
|
||||
async getOneDashboardMetabase(@Body() body, @Param() params) {
|
||||
const { id } = params;
|
||||
console.log(`/catalog`, 'ON GET ONE DASHBOARD METABASE ROUTE');
|
||||
this.logger.info(`/catalog - ON GET ONE DASHBOARD METABASE ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.getOneDashboardMetabase(id, body);
|
||||
|
||||
@@ -62,7 +110,10 @@ export class CatalogController {
|
||||
|
||||
@Get('table-metadata')
|
||||
async getAllTableMetadata(@Body() body, @Query() query) {
|
||||
console.log(`/catalog`, 'ON GET ALL TABLES METADATA ROUTE');
|
||||
this.logger.info(`/catalog - ON GET ALL TABLES METADATA ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
if (!query) {
|
||||
const res = await this.catalogService.getAllTableMetadata(body);
|
||||
@@ -76,7 +127,10 @@ export class CatalogController {
|
||||
@Delete('table-metadata/:id')
|
||||
async deleteOneTableMetadata(@Param() params, @Body() body) {
|
||||
const { id } = params;
|
||||
console.log(`/catalog`, 'ON DELETE ONE TABLE METADATA ROUTE');
|
||||
this.logger.info(`/catalog - ON DELETE ONE TABLE METADATA ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.deleteOneTableMetadata(id, body);
|
||||
|
||||
@@ -85,7 +139,10 @@ export class CatalogController {
|
||||
|
||||
@Get('column-metadata')
|
||||
async getOneColumnMetadata(@Body() body, @Query() params) {
|
||||
console.log(`/catalog`, 'ON GET ONE COLUMN METADATA ROUTE');
|
||||
this.logger.info(`/catalog - ON GET ONE COLUMN METADATA ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.getOneColumnMetadata(body, params);
|
||||
|
||||
@@ -94,7 +151,10 @@ export class CatalogController {
|
||||
|
||||
@Delete('column-metadata/:id')
|
||||
async deleteOneColumnMetadata(@Param() params, @Body() body) {
|
||||
console.log(`/catalog`, 'ON DELETE ONE COLUMN METADATA ROUTE');
|
||||
this.logger.info(`/catalog - ON DELETE ONE COLUMN METADATA ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
const { id } = params;
|
||||
|
||||
const res = await this.catalogService.deleteOneColumnMetadata(id, body);
|
||||
@@ -104,7 +164,10 @@ export class CatalogController {
|
||||
|
||||
@Get('data-preview')
|
||||
async getOneDataPreview(@Body() body, @Query() params) {
|
||||
console.log(`/catalog`, 'ON GET ONE DATAPREVIEW ROUTE');
|
||||
this.logger.info(`/catalog - ON GET ONE DATAPREVIEW ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.getOneDataPreview(body, params);
|
||||
|
||||
@@ -113,7 +176,10 @@ export class CatalogController {
|
||||
|
||||
@Get('data-status')
|
||||
async getDataStatus(@Body() body) {
|
||||
console.log(`/catalog`, 'ON GET DATA STATUS ROUTE');
|
||||
this.logger.info(`/catalog - ON GET DATA STATUS ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.getDataStatus(body);
|
||||
|
||||
@@ -122,7 +188,10 @@ export class CatalogController {
|
||||
|
||||
@Get('data-status')
|
||||
async createDataStatus(@Body() body) {
|
||||
console.log(`/catalog`, 'ON CREATE DATA STATUS ROUTE');
|
||||
this.logger.info(`/catalog - ON CREATE DATA STATUS ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.createDataStatus(body);
|
||||
|
||||
@@ -131,7 +200,10 @@ export class CatalogController {
|
||||
|
||||
@Get('data-description')
|
||||
async getDataDescription(@Body() body) {
|
||||
console.log(`/catalog`, 'ON GET DATA DESCRIPTION ROUTE');
|
||||
this.logger.info(`/catalog - ON GET DATA DESCRIPTION ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.getDataDescription(body);
|
||||
|
||||
@@ -140,7 +212,10 @@ export class CatalogController {
|
||||
|
||||
@Post('data-description')
|
||||
async createDataDescription(@Body() body) {
|
||||
console.log(`/catalog`, 'ON CREATE DATA DESCRIPTION ROUTE');
|
||||
this.logger.info(`/catalog - ON CREATE DATA DESCRIPTION ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.createDataDescription(body);
|
||||
|
||||
@@ -149,7 +224,10 @@ export class CatalogController {
|
||||
|
||||
@Get('data-docs')
|
||||
async getDataDocs(@Body() body) {
|
||||
console.log(`/catalog`, 'ON GET DATA DOCS ROUTE');
|
||||
this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.getDataDocs(body);
|
||||
|
||||
@@ -158,7 +236,10 @@ export class CatalogController {
|
||||
|
||||
@Post('data-docs')
|
||||
async createDataDocs(@Body() body) {
|
||||
console.log(`/catalog`, 'ON CREATE DATA DOCS ROUTE');
|
||||
this.logger.info(`/catalog - ON CREATE DATA DOCS ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.createDataDocs(body);
|
||||
|
||||
@@ -167,7 +248,10 @@ export class CatalogController {
|
||||
|
||||
@Get('data-rating')
|
||||
async getDataRating(@Body() body) {
|
||||
console.log(`/catalog`, 'ON GET DATA RATING ROUTE');
|
||||
this.logger.info(`/catalog - ON GET DATA RATING ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.getDataRating(body);
|
||||
|
||||
@@ -176,7 +260,10 @@ export class CatalogController {
|
||||
|
||||
@Post('data-rating')
|
||||
async createDataRating(@Body() body) {
|
||||
console.log(`/catalog`, 'ON GET DATA RATING ROUTE');
|
||||
this.logger.info(`/catalog - ON GET DATA RATING ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.createDataRating(body);
|
||||
|
||||
@@ -185,7 +272,7 @@ export class CatalogController {
|
||||
|
||||
@Get('summary-rating/:id')
|
||||
async getSummaryRating() {
|
||||
console.log(`/catalog`, 'ON GET SUMMARY RATING ROUTE');
|
||||
this.logger.info(`/catalog - ON GET SUMMARY RATING ROUTE`);
|
||||
//const { id } = params;
|
||||
|
||||
//
|
||||
@@ -200,7 +287,10 @@ export class CatalogController {
|
||||
|
||||
@Get('data-comment')
|
||||
async getDataComment(@Body() body) {
|
||||
console.log(`/catalog`, 'ON GET DATA COMMENT ROUTE');
|
||||
this.logger.info(`/catalog - ON GET DATA COMMENT ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.getDataComment(body);
|
||||
|
||||
@@ -209,7 +299,10 @@ export class CatalogController {
|
||||
|
||||
@Post('data-comment')
|
||||
async createDataComment(@Body() body) {
|
||||
console.log(`/catalog`, 'ON GET DATA COMMENT ROUTE');
|
||||
this.logger.info(`/catalog - ON GET DATA COMMENT ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.createDataComment(body);
|
||||
|
||||
@@ -218,7 +311,10 @@ export class CatalogController {
|
||||
|
||||
@Get('data-review')
|
||||
async getDataReview(@Body() body, @Query() params) {
|
||||
console.log(`/catalog`, 'ON GET DATA REVIEW ROUTE');
|
||||
this.logger.info(`/catalog - ON GET DATA REVIEW ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.getDataReview(body, params);
|
||||
|
||||
@@ -227,7 +323,10 @@ export class CatalogController {
|
||||
|
||||
@Post('tags')
|
||||
async createTags(@Body() body) {
|
||||
console.log(`/catalog`, 'ON CREATE TAG ROUTE');
|
||||
this.logger.info(`/catalog - ON CREATE TAG ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.createTags(body);
|
||||
|
||||
@@ -236,7 +335,10 @@ export class CatalogController {
|
||||
|
||||
@Get('tags')
|
||||
async findAllTags(@Body() body) {
|
||||
console.log(`/catalog`, 'ON FIND ALL TAGS ROUTE');
|
||||
this.logger.info(`/catalog - ON FIND ALL TAGS ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.findAllTags(body);
|
||||
|
||||
@@ -245,7 +347,10 @@ export class CatalogController {
|
||||
|
||||
@Delete('tags/:id')
|
||||
async deleteTags(@Param() params, @Body() body) {
|
||||
console.log(`/catalog`, 'ON DELETE TAG ROUTE');
|
||||
this.logger.info(`/catalog - ON DELETE TAG ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
const { id } = params;
|
||||
|
||||
const res = await this.catalogService.deleteTags(id, body);
|
||||
@@ -255,7 +360,10 @@ export class CatalogController {
|
||||
|
||||
@Post('table-tags')
|
||||
async createTableTags(@Body() body) {
|
||||
console.log(`/catalog`, 'ON CREATE TABLE TAG ROUTE');
|
||||
this.logger.info(`/catalog - ON CREATE TABLE TAG ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.createTableTags(body);
|
||||
|
||||
@@ -264,7 +372,10 @@ export class CatalogController {
|
||||
|
||||
@Get('table-tags')
|
||||
async getAllTableTags(@Body() body) {
|
||||
console.log(`/catalog`, 'ON GET ALL TABLE TAGS ROUTE');
|
||||
this.logger.info(`/catalog - ON GET ALL TABLE TAGS ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.findAllTableTags(body);
|
||||
|
||||
@@ -273,7 +384,10 @@ export class CatalogController {
|
||||
|
||||
@Delete('table-tags/:id')
|
||||
async deleteTableTags(@Param() params, @Body() body) {
|
||||
console.log(`/catalog`, 'ON CREATE TABLE TAGS ROUTE');
|
||||
this.logger.info(`/catalog - ON CREATE TABLE TAGS ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
const { id } = params;
|
||||
|
||||
const res = await this.catalogService.deleteTableTags(id, body);
|
||||
@@ -283,7 +397,10 @@ export class CatalogController {
|
||||
|
||||
@Get('table-rules')
|
||||
async getTableRules(@Body() body) {
|
||||
console.log(`/catalog`, 'ON GET TABLE RULES ROUTE');
|
||||
this.logger.info(`/catalog - ON GET TABLE RULES ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const res = await this.catalogService.getTableRules(body);
|
||||
|
||||
@@ -292,7 +409,10 @@ export class CatalogController {
|
||||
|
||||
@Delete('table-rules/:id')
|
||||
async deleteTableRules(@Param() params, @Body() body) {
|
||||
console.log(`/catalog`, 'ON DELETE TABLE RULES ROUTE');
|
||||
this.logger.info(`/catalog - ON DELETE TABLE RULES ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
const { id } = params;
|
||||
|
||||
const res = await this.catalogService.deleteTableRules(id, body);
|
||||
|
||||
@@ -46,7 +46,6 @@ class CatalogService {
|
||||
const { data } = await axios.get(
|
||||
`${nimbusUrl}/api/catalog/dashboard-metabase/${id}`,
|
||||
);
|
||||
console.log(data);
|
||||
|
||||
// const [, path] = data.iframe_url.split('.dadosfera');
|
||||
// const host = `httpss://metabase-${body.info.customer.toLowerCase()}.dadosfera.ai`;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
import { ClientOptions, Transport } from '@nestjs/microservices';
|
||||
import { ConnectorManager } from '@dadosfera/protospack-v2';
|
||||
|
||||
export class ConnectorClientConfiguration {
|
||||
config(): ClientOptions {
|
||||
return {
|
||||
transport: Transport.GRPC,
|
||||
options: {
|
||||
url: process.env.INFACTORY_URL,
|
||||
package: [
|
||||
ConnectorManager.ProtoPackages.WritePackage,
|
||||
ConnectorManager.ProtoPackages.ReadPackage,
|
||||
],
|
||||
// credentials: undefined,
|
||||
credentials: process.env.LOCAL_ENV
|
||||
? undefined
|
||||
: credentials.createSsl(),
|
||||
protoPath: [
|
||||
ConnectorManager.ProtoPaths.WriteFilePath,
|
||||
ConnectorManager.ProtoPaths.ReadFilePath,
|
||||
],
|
||||
loader: {
|
||||
enums: String,
|
||||
objects: true,
|
||||
arrays: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpException,
|
||||
Param,
|
||||
Put,
|
||||
Post,
|
||||
Query,
|
||||
UploadedFile,
|
||||
UseInterceptors,
|
||||
Inject,
|
||||
Headers,
|
||||
UploadedFiles,
|
||||
} from '@nestjs/common';
|
||||
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
|
||||
import { ApiBearerAuth, ApiConsumes, ApiTags } from '@nestjs/swagger';
|
||||
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 { 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: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
private connectorClientService: ConnectorClientService,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
@Post()
|
||||
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.CREATE)
|
||||
@ApiConsumes('multipart/form-data')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async uploadConnector(
|
||||
@UploadedFile() file,
|
||||
@Body() body: CreateConnectorDto,
|
||||
) {
|
||||
this.logger.info('/upload - Upload Connector Route');
|
||||
|
||||
const response = await this.connectorClientService.uploadConnector({
|
||||
file,
|
||||
connector: JSON.parse(body.connector),
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@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 uploadConnectorsFile(@UploadedFile() file: UploadConnectorsFileDto) {
|
||||
console.log(`/upload`, 'Upload Connector Route');
|
||||
|
||||
const response = await this.connectorClientService.uploadConnectorsFile({
|
||||
file,
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Get()
|
||||
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.GET)
|
||||
async getAllConnectors(
|
||||
@Headers('dadosfera-lang') language,
|
||||
@Query() queries: GetAllDto,
|
||||
) {
|
||||
this.logger.info('/upload - Upload Connector Route');
|
||||
|
||||
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;
|
||||
|
||||
return {
|
||||
message: response.message,
|
||||
connectors,
|
||||
};
|
||||
}
|
||||
|
||||
@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,
|
||||
) {
|
||||
this.logger.info('/upload - Upload Connector Route');
|
||||
|
||||
const pluginId = `${plugin}-${version}`;
|
||||
|
||||
const response: any = await this.connectorClientService.getConnector(
|
||||
pluginId,
|
||||
language,
|
||||
);
|
||||
|
||||
return {
|
||||
message: response.message || 'ok',
|
||||
connector: { ...JSON.parse(response.connector) },
|
||||
};
|
||||
}
|
||||
|
||||
@Get('/:plugin/details')
|
||||
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.GET)
|
||||
async getConnectorDetails(
|
||||
@Headers('dadosfera-lang') language,
|
||||
@Param('plugin') plugin: string,
|
||||
@Query('version') version: string,
|
||||
) {
|
||||
this.logger.info('/upload - Upload Connector Route');
|
||||
|
||||
const pluginId = `${plugin}-${version}`;
|
||||
|
||||
const response: any = await this.connectorClientService.getConnectorDetails(
|
||||
pluginId,
|
||||
language || 'pt-br',
|
||||
);
|
||||
|
||||
return {
|
||||
message: response.message || 'ok',
|
||||
connector: { ...JSON.parse(response.connector) },
|
||||
};
|
||||
}
|
||||
|
||||
@Put('/:plugin')
|
||||
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.UPDATE)
|
||||
@ApiConsumes('multipart/form-data')
|
||||
async updateConnector(
|
||||
@Param('plugin') plugin: string,
|
||||
@Body() body: UpdateDto,
|
||||
) {
|
||||
this.logger.info('/upload - Upload Connector Route');
|
||||
|
||||
const changes = body;
|
||||
|
||||
const response: any = await this.connectorClientService.updateConnector({
|
||||
plugin,
|
||||
changes: JSON.stringify(changes),
|
||||
});
|
||||
|
||||
return {
|
||||
message: response.message || 'ok',
|
||||
connector: { ...JSON.parse(response.connector) },
|
||||
};
|
||||
}
|
||||
|
||||
@Put('/:plugin/add-tag')
|
||||
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.UPDATE)
|
||||
async addTagOnConnector(
|
||||
@Param('plugin') plugin: string,
|
||||
@Body() body: AddTagDto,
|
||||
) {
|
||||
this.logger.info('/upload - Upload Connector Route');
|
||||
|
||||
const { tags } = body;
|
||||
|
||||
if (!tags || !(typeof tags === 'object') || !tags.length) {
|
||||
throw new HttpException('Not found tags attributes', 400);
|
||||
}
|
||||
|
||||
const changes = { plugin, tags };
|
||||
|
||||
const response: any = await this.connectorClientService.updateConnector({
|
||||
plugin,
|
||||
changes: JSON.stringify(changes),
|
||||
});
|
||||
|
||||
return {
|
||||
message: response.message || 'ok',
|
||||
connector: { ...JSON.parse(response.connector) },
|
||||
};
|
||||
}
|
||||
|
||||
@Put('/:plugin/remove-tag')
|
||||
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.UPDATE)
|
||||
async removeTagOnConnector(
|
||||
@Param('plugin') plugin: string,
|
||||
@Body() body: RemoveTagDto,
|
||||
) {
|
||||
this.logger.info('/upload - Upload Connector Route');
|
||||
|
||||
const { tags } = body;
|
||||
const remove = tags;
|
||||
|
||||
if (!remove || !(typeof remove === 'object') || !remove.length) {
|
||||
throw new HttpException('Not found remove attribute', 400);
|
||||
}
|
||||
|
||||
const changes = { plugin, remove };
|
||||
|
||||
const response: any = await this.connectorClientService.updateConnector({
|
||||
plugin,
|
||||
changes: JSON.stringify(changes),
|
||||
});
|
||||
|
||||
return {
|
||||
message: response.message || 'ok',
|
||||
connector: { ...JSON.parse(response.connector) },
|
||||
};
|
||||
}
|
||||
|
||||
@Delete('/:plugin')
|
||||
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.DELETE)
|
||||
async deleteConnector(
|
||||
@Param('plugin') plugin: string,
|
||||
@Body() { version }: DeleteConnectorDto,
|
||||
) {
|
||||
this.logger.info('/upload - Upload Connector Route');
|
||||
|
||||
const response: any = await this.connectorClientService.deleteConnector(
|
||||
plugin,
|
||||
version,
|
||||
);
|
||||
|
||||
return {
|
||||
message: response.message || 'ok',
|
||||
connector: { ...JSON.parse(response.connector) },
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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 AddTagDto {
|
||||
@ApiProperty()
|
||||
tags: string[];
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateConnectorDto {
|
||||
@ApiProperty({ format: 'binary' })
|
||||
file: string;
|
||||
|
||||
@ApiProperty()
|
||||
connector: string;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class UploadConnectorsFileDto {
|
||||
@ApiProperty({ format: 'binary' })
|
||||
file: string;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class DeleteConnectorDto {
|
||||
@ApiProperty()
|
||||
version: string;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class GetAllDto {
|
||||
@ApiProperty()
|
||||
search: string;
|
||||
|
||||
@ApiProperty()
|
||||
language: string;
|
||||
|
||||
@ApiProperty()
|
||||
size: string;
|
||||
|
||||
@ApiProperty()
|
||||
page: string;
|
||||
|
||||
@ApiProperty()
|
||||
filters: any;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class RemoveTagDto {
|
||||
@ApiProperty()
|
||||
tags: string[];
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class UpdateDto {
|
||||
@ApiProperty({ format: 'binary', required: false })
|
||||
file?: string;
|
||||
|
||||
@ApiProperty()
|
||||
version: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
category?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
plugin?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
image?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
docs?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
name?: string;
|
||||
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
description: 'skip_select_entities: boolean skip_select_columns: boolean;',
|
||||
})
|
||||
options?: {
|
||||
skip_select_entities: boolean;
|
||||
skip_select_columns: boolean;
|
||||
};
|
||||
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
type: 'array',
|
||||
items: {
|
||||
properties: {},
|
||||
},
|
||||
})
|
||||
connection_controls?: object[];
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class UploadFileDto {
|
||||
@ApiProperty({ format: 'binary' })
|
||||
file: string;
|
||||
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
}
|
||||
@@ -1,13 +1,22 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { Controller, Get, Inject } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { HealthService } from './health.service';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
|
||||
@ApiTags('Health')
|
||||
@Controller('health')
|
||||
export class HealthController {
|
||||
constructor(private readonly healthService: HealthService) {}
|
||||
logger: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
private readonly healthService: HealthService,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
@Get()
|
||||
check() {
|
||||
console.log(`/health`, 'check');
|
||||
return this.healthService.check();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
describe('PipelinesGrpcServerService', () => {
|
||||
it('should be defined', () => {
|
||||
expect(2 + 2).toBe(4);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,16 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
|
||||
@Injectable()
|
||||
export class HealthService {
|
||||
logger: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
check() {
|
||||
console.log('HEALTH');
|
||||
return { message: 'Ok' };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Info } from 'src/clients/inputs/interfaces';
|
||||
import { Method } from 'axios';
|
||||
|
||||
export class InputModel {
|
||||
category: string;
|
||||
plugin: string;
|
||||
name: string;
|
||||
cron?: string;
|
||||
credentials?: {
|
||||
connection_type: string;
|
||||
oauth: string;
|
||||
oauth_code: string;
|
||||
start_date: string;
|
||||
};
|
||||
options?: {
|
||||
oauth?: {
|
||||
get_tokens_url: string;
|
||||
get_tokens_method?: Method;
|
||||
get_tokens_url_params: string;
|
||||
get_tokens_set_response: Record<string, string>;
|
||||
content_type: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
class GoogleAnalyticsClientSecrets {
|
||||
@ApiProperty()
|
||||
type: string;
|
||||
@ApiProperty()
|
||||
project_id: string;
|
||||
@ApiProperty()
|
||||
private_key_id: string;
|
||||
@ApiProperty()
|
||||
private_key: string;
|
||||
@ApiProperty()
|
||||
client_email: string;
|
||||
@ApiProperty()
|
||||
client_id: string;
|
||||
@ApiProperty()
|
||||
auth_uri: string;
|
||||
@ApiProperty()
|
||||
token_uri: string;
|
||||
@ApiProperty()
|
||||
auth_provider_x509_cert_url: string;
|
||||
@ApiProperty()
|
||||
client_x509_cert_url: string;
|
||||
}
|
||||
class OauthObject {
|
||||
@ApiPropertyOptional()
|
||||
get_tokens_url: string;
|
||||
@ApiPropertyOptional()
|
||||
get_tokens_url_params: string;
|
||||
@ApiPropertyOptional()
|
||||
get_tokens_set_response: Record<string, any>;
|
||||
@ApiPropertyOptional()
|
||||
content_type: string;
|
||||
}
|
||||
class InputOptions {
|
||||
@ApiPropertyOptional()
|
||||
oauth: OauthObject;
|
||||
@ApiPropertyOptional()
|
||||
skip_select_columns: true;
|
||||
@ApiPropertyOptional()
|
||||
skip_select_entities: false;
|
||||
@ApiPropertyOptional()
|
||||
skip_transformation: true;
|
||||
}
|
||||
|
||||
class CredentialsJdbc {
|
||||
@ApiPropertyOptional()
|
||||
jdbc_user: string;
|
||||
@ApiPropertyOptional()
|
||||
jdbc_password: string;
|
||||
@ApiPropertyOptional()
|
||||
database: string;
|
||||
@ApiPropertyOptional()
|
||||
endpoint: string;
|
||||
@ApiPropertyOptional()
|
||||
port: string;
|
||||
@ApiPropertyOptional()
|
||||
engine: string;
|
||||
@ApiPropertyOptional()
|
||||
schema: string;
|
||||
}
|
||||
class FileFormatParams {
|
||||
@ApiPropertyOptional()
|
||||
file_format: string;
|
||||
@ApiPropertyOptional()
|
||||
encoding: string;
|
||||
}
|
||||
|
||||
class Credentials extends CredentialsJdbc {
|
||||
@ApiProperty()
|
||||
connection_type: string;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
client_aws_access_key_id: string;
|
||||
@ApiPropertyOptional()
|
||||
client_aws_secret_access_key: string;
|
||||
@ApiPropertyOptional()
|
||||
client_bucket: string;
|
||||
@ApiPropertyOptional()
|
||||
file_to_extract: string;
|
||||
@ApiPropertyOptional()
|
||||
file_format_params: FileFormatParams;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
view_id: string;
|
||||
@ApiPropertyOptional()
|
||||
client_secrets: GoogleAnalyticsClientSecrets;
|
||||
@ApiPropertyOptional()
|
||||
start_date: string;
|
||||
@ApiPropertyOptional()
|
||||
end_date: string;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
oauth_code: string;
|
||||
}
|
||||
class Column {
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
@ApiProperty()
|
||||
type: string;
|
||||
}
|
||||
class TableColumns {
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
@ApiProperty()
|
||||
columns: Column[];
|
||||
@ApiProperty()
|
||||
references: Column[];
|
||||
}
|
||||
export class GetAvailableEntitiesReq {
|
||||
@ApiProperty()
|
||||
plugin: string;
|
||||
}
|
||||
|
||||
export class GetAvailableEntitiesRes {
|
||||
@ApiProperty()
|
||||
entities: string[];
|
||||
}
|
||||
|
||||
export class TestConnectionGetColumnsReq {
|
||||
@ApiProperty()
|
||||
plugin: string;
|
||||
@ApiProperty()
|
||||
tables: string[];
|
||||
@ApiPropertyOptional()
|
||||
credentials: CredentialsJdbc;
|
||||
@ApiPropertyOptional()
|
||||
id: string;
|
||||
@ApiPropertyOptional()
|
||||
info: Info;
|
||||
}
|
||||
export class TestConnectionGetColumnsRes {
|
||||
@ApiProperty()
|
||||
tables: TableColumns[];
|
||||
}
|
||||
|
||||
export class TestConnectionReq {
|
||||
@ApiProperty()
|
||||
plugin: string;
|
||||
@ApiProperty()
|
||||
credentials: CredentialsJdbc;
|
||||
@ApiPropertyOptional()
|
||||
info: Info;
|
||||
}
|
||||
export class TestConnectionRes {
|
||||
@ApiProperty()
|
||||
connection_state: boolean;
|
||||
@ApiProperty()
|
||||
total_entities: number;
|
||||
@ApiProperty()
|
||||
database_tables: string[];
|
||||
}
|
||||
export class CreateInputReq {
|
||||
@ApiPropertyOptional()
|
||||
id: string;
|
||||
@ApiProperty()
|
||||
plugin: string;
|
||||
@ApiProperty()
|
||||
category: string;
|
||||
@ApiProperty()
|
||||
credentials: Credentials;
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
@ApiPropertyOptional()
|
||||
options: InputOptions;
|
||||
@ApiPropertyOptional()
|
||||
info: Info;
|
||||
}
|
||||
|
||||
export class Input {
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
@ApiProperty()
|
||||
category: string;
|
||||
@ApiProperty()
|
||||
plugin: string;
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
@ApiProperty()
|
||||
cron: string;
|
||||
@ApiProperty()
|
||||
credentials: Credentials;
|
||||
@ApiProperty()
|
||||
client_id: string;
|
||||
@ApiProperty()
|
||||
created_at: string;
|
||||
@ApiProperty()
|
||||
updated_at: string;
|
||||
@ApiPropertyOptional()
|
||||
info: Info;
|
||||
}
|
||||
@@ -3,59 +3,144 @@ import {
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
} from '@nestjs/common';
|
||||
import { InputsService } from './inputs.service';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
import { InputsClientService } from 'src/clients/inputs/client.service';
|
||||
import { UpdateInputRequest } from 'src/clients/inputs/interfaces';
|
||||
import { InputNewCreateRequest } from '@victorradael/protospack';
|
||||
import { PERMISSIONS } from '../../authentication/permissions.enum';
|
||||
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
|
||||
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
CreateInputReq,
|
||||
GetAvailableEntitiesReq,
|
||||
GetAvailableEntitiesRes,
|
||||
Input,
|
||||
TestConnectionGetColumnsReq,
|
||||
TestConnectionGetColumnsRes,
|
||||
TestConnectionReq,
|
||||
TestConnectionRes,
|
||||
} from './dtos/input.model';
|
||||
|
||||
@ApiTags('inputs')
|
||||
@Controller('inputs')
|
||||
@AuthenticateCondition((req, user) => {
|
||||
let action: any;
|
||||
|
||||
switch (req.method) {
|
||||
case 'POST':
|
||||
action = 'CREATE';
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
action = 'UPDATE';
|
||||
break;
|
||||
|
||||
case 'PATCH':
|
||||
action = 'UPDATE';
|
||||
break;
|
||||
|
||||
default:
|
||||
action = req.method;
|
||||
}
|
||||
|
||||
if (
|
||||
`${req.method} ${req.route.path}` ===
|
||||
'GET /inputs/available-entities/:plugin'
|
||||
) {
|
||||
action = 'GET_ENTITIES';
|
||||
}
|
||||
|
||||
return user.permissions.includes(PERMISSIONS.INPUT.permissions[action].seqid);
|
||||
})
|
||||
export class InputsController {
|
||||
constructor(private inputsClientService: InputsClientService) {}
|
||||
inputService: InputsService;
|
||||
logger: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
private inputsClientService: InputsClientService,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
this.inputService = new InputsService(this.inputsClientService);
|
||||
}
|
||||
|
||||
@Get('available-entities/:plugin')
|
||||
@ApiOkResponse({ type: GetAvailableEntitiesRes })
|
||||
async getAvailableEntities(
|
||||
@Body() body,
|
||||
@Param() params: GetAvailableEntitiesReq,
|
||||
) {
|
||||
const { info } = body;
|
||||
const { plugin } = params;
|
||||
return await this.inputService.getAvailableEntities({ info, plugin });
|
||||
}
|
||||
|
||||
@Post('/test-connection')
|
||||
async testConnection(@Body() data) {
|
||||
console.log(`/test-connection`, 'ON TEST CONNECTION ROUTE');
|
||||
@ApiOkResponse({ type: TestConnectionRes })
|
||||
async testConnection(@Body() data: TestConnectionReq) {
|
||||
this.logger.info(`/test-connection - ON TEST CONNECTION ROUTE`, {
|
||||
user: data.info.user_id,
|
||||
customer: data.info.customer,
|
||||
});
|
||||
|
||||
const inputService = new InputsService(this.inputsClientService);
|
||||
const response = await inputService.testConnection(data);
|
||||
const response = await this.inputService.testConnection(data);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('/test-connection/get-columns')
|
||||
async getColumns(@Body() data) {
|
||||
console.log(
|
||||
`/test-connection/get-columns`,
|
||||
'ON TEST CONNECTION GET COLUMNS ROUTE',
|
||||
@ApiOkResponse({ type: TestConnectionGetColumnsRes })
|
||||
async getColumns(@Body() data: TestConnectionGetColumnsReq) {
|
||||
this.logger.info(
|
||||
`/test-connection/get-columns - ON TEST CONNECTION GET COLUMNS ROUTE`,
|
||||
{
|
||||
user: data.info.user_id,
|
||||
customer: data.info.customer,
|
||||
},
|
||||
);
|
||||
|
||||
const inputService = new InputsService(this.inputsClientService);
|
||||
const response = await inputService.getColumns(data);
|
||||
const response = await this.inputService.getColumns(data);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post()
|
||||
async create(@Body() createInputDto: InputNewCreateRequest) {
|
||||
console.log(`/input`, 'ON CREATE ROUTE');
|
||||
@ApiOkResponse({ type: Input })
|
||||
async create(@Body() createInputDto: CreateInputReq) {
|
||||
this.logger.info(`/input - ON CREATE ROUTE`, {
|
||||
user: createInputDto.info.user_id,
|
||||
customer: createInputDto.info.customer,
|
||||
});
|
||||
|
||||
const inputService = new InputsService(this.inputsClientService);
|
||||
const response = await inputService.create(createInputDto);
|
||||
const response = await this.inputService.create(createInputDto);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post(':id')
|
||||
@ApiOkResponse({ type: Input })
|
||||
async reCreate(@Param('id') id: string, @Body() input: CreateInputReq) {
|
||||
if (!input.id) input.id = id;
|
||||
this.logger.info(`POST /${id}`, 'ON RECREATE ROUTE');
|
||||
|
||||
const response = await this.inputService.reCreate(input);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Get()
|
||||
async findAll(@Body() body) {
|
||||
console.log(`/input`, 'ON FIND ALL ROUTE');
|
||||
this.logger.info(`/input - ON FIND ALL ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const inputService = new InputsService(this.inputsClientService);
|
||||
|
||||
const response = await inputService.findAll(body);
|
||||
const response = await this.inputService.findAll(body);
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -63,10 +148,12 @@ export class InputsController {
|
||||
@Get('/:id')
|
||||
async findOne(@Body() body, @Param() params) {
|
||||
const { id } = params;
|
||||
console.log(`/input/${id}`, 'ON FIND ONE ROUTE');
|
||||
const inputService = new InputsService(this.inputsClientService);
|
||||
this.logger.info(`/input/${id} - ON FIND ONE ROUTE`, {
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
});
|
||||
|
||||
const response = await inputService.findOne({ id, ...body });
|
||||
const response = await this.inputService.findOne({ id, ...body });
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -75,12 +162,13 @@ export class InputsController {
|
||||
async update(@Body() updateInputDto: UpdateInputRequest, @Param() params) {
|
||||
const { id } = params;
|
||||
const { info } = updateInputDto;
|
||||
this.logger.info(`/input/${id} - ON UPDATE ROUTE`, {
|
||||
user: info.user_id,
|
||||
customer: info.customer_id,
|
||||
});
|
||||
delete updateInputDto.info;
|
||||
|
||||
console.log(`/input/${id}`, 'ON UPDATE ROUTE');
|
||||
const inputService = new InputsService(this.inputsClientService);
|
||||
|
||||
const response = await inputService.update(id, updateInputDto, info);
|
||||
const response = await this.inputService.update(id, updateInputDto, info);
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -89,11 +177,12 @@ export class InputsController {
|
||||
async delete(@Body() data, @Param() params) {
|
||||
const { id } = params;
|
||||
|
||||
console.log(`/input/${id}`, 'ON DELETE ROUTE');
|
||||
this.logger.info(`/input/${id} - ON DELETE ROUTE`, {
|
||||
user: data.info.user_id,
|
||||
customer: data.info.customer,
|
||||
});
|
||||
|
||||
const inputService = new InputsService(this.inputsClientService);
|
||||
|
||||
const response = await inputService.remove({ id, ...data });
|
||||
const response = await this.inputService.remove({ id, ...data });
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
describe('PipelinesGrpcServerService', () => {
|
||||
it('should be defined', () => {
|
||||
expect(2 + 2).toBe(4);
|
||||
});
|
||||
});
|
||||
@@ -1,41 +1,91 @@
|
||||
import { Body, HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { Timeout } from '@nestjs/schedule';
|
||||
import { DecodeGrpcStruct } from 'protospack';
|
||||
import CronParser, { CronExpression } from 'cron-parser';
|
||||
import { InputsClientService } from 'src/clients/inputs/client.service';
|
||||
import { IIdRequest, Info } from 'src/clients/inputs/interfaces';
|
||||
|
||||
@Injectable()
|
||||
export class InputsService {
|
||||
constructor(private inputClient: InputsClientService) {}
|
||||
secondsInADay = 60 * 60 * 24;
|
||||
secondsInAnHour = 60 * 60;
|
||||
|
||||
adjustInputPayload(payload) {
|
||||
if (payload?.input_generic) return DecodeGrpcStruct(payload.input_generic);
|
||||
if (!payload.input_s3 && !payload.input_jdbc) return payload;
|
||||
return payload?.input_s3 || payload?.input_jdbc;
|
||||
}
|
||||
async create(@Body() data) {
|
||||
getDifferenceInSeconds(date1: Date, date2: Date) {
|
||||
const diffInMs = Math.abs(date2.getTime() - date1.getTime());
|
||||
return diffInMs / 1000;
|
||||
}
|
||||
validateCron(data) {
|
||||
const { info, cron } = data;
|
||||
const { customer_tier } = info;
|
||||
if (!cron) return;
|
||||
let interval: CronExpression;
|
||||
try {
|
||||
if (
|
||||
data.plugin == 'csv' ||
|
||||
data.plugin == 'json' ||
|
||||
data.plugin == 'parquet'
|
||||
) {
|
||||
interval = CronParser.parseExpression(cron);
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
'Intervalo de tempo inválido',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
const nextDate = interval.next().toDate();
|
||||
const afterNextDate = interval.next().toDate();
|
||||
const secondsApart = this.getDifferenceInSeconds(nextDate, afterNextDate);
|
||||
if (customer_tier === 'BASIC' && secondsApart < this.secondsInADay) {
|
||||
throw new HttpException(
|
||||
'Intervalo de tempo não pode ser inferior a um dia.',
|
||||
HttpStatus.FORBIDDEN,
|
||||
);
|
||||
} else if (secondsApart < this.secondsInAnHour) {
|
||||
throw new HttpException(
|
||||
'Intervalo de tempo não pode ser inferior a uma hora.',
|
||||
HttpStatus.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
}
|
||||
async create(@Body() data) {
|
||||
this.validateCron(data);
|
||||
let response;
|
||||
switch (data.plugin.toLowerCase()) {
|
||||
case 'csv':
|
||||
case 'json':
|
||||
case 'parquet': {
|
||||
const { info, ...input } = data;
|
||||
const inputPayload = this.generateInputS3Payload(input);
|
||||
const createInputResponse = await this.inputClient.createS3Inputs({
|
||||
response = await this.inputClient.createS3Inputs({
|
||||
input: inputPayload,
|
||||
info,
|
||||
});
|
||||
return createInputResponse;
|
||||
} else {
|
||||
const createInputResponse: any = await this.inputClient.create(data);
|
||||
if (createInputResponse?.input?.input_jdbc) {
|
||||
return { input: createInputResponse?.input?.input_jdbc };
|
||||
}
|
||||
return createInputResponse;
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
|
||||
case 'oracle':
|
||||
case 'mysql':
|
||||
case 'postgresql':
|
||||
case 'sqlserver':
|
||||
response = await this.inputClient.newCreate(data);
|
||||
break;
|
||||
default:
|
||||
response = await this.inputClient.createGeneric(data);
|
||||
break;
|
||||
}
|
||||
const adjustedInput = this.adjustInputPayload(response.input);
|
||||
return { ...response, input: adjustedInput };
|
||||
}
|
||||
async reCreate(data) {
|
||||
const { info, cron } = data;
|
||||
if (cron) this.validateCron({ info, cron });
|
||||
const response = await this.inputClient.createGeneric(data);
|
||||
const adjustedInput = this.adjustInputPayload(response.input);
|
||||
return { ...response, input: adjustedInput };
|
||||
}
|
||||
async getAvailableEntities(data): Promise<{ entities: string[] }> {
|
||||
return await this.inputClient.getAvailableEntities(data);
|
||||
}
|
||||
|
||||
async findAll(body) {
|
||||
try {
|
||||
const findAllInputResponse: any = await this.inputClient.findAll(body);
|
||||
@@ -65,6 +115,7 @@ export class InputsService {
|
||||
}
|
||||
|
||||
async update(id: string, data, info: Info) {
|
||||
this.validateCron({ ...data, info });
|
||||
try {
|
||||
const updateInputResponse: any = await this.inputClient.update({
|
||||
id,
|
||||
@@ -116,7 +167,7 @@ export class InputsService {
|
||||
}
|
||||
|
||||
generateInputS3Payload(payload) {
|
||||
const { credentials, plugin } = payload;
|
||||
const { credentials, plugin, cron } = payload;
|
||||
if (!credentials) return payload;
|
||||
|
||||
const {
|
||||
@@ -127,8 +178,10 @@ export class InputsService {
|
||||
client_bucket,
|
||||
file_to_extract,
|
||||
} = credentials;
|
||||
|
||||
const formatedPayload = {
|
||||
plugin,
|
||||
cron,
|
||||
source_bucket: client_bucket,
|
||||
source_prefix: file_to_extract,
|
||||
auth_parameters: {
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import { Controller, Get, Redirect, Req, UseGuards } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { InputsClientService } from 'src/clients/inputs/client.service';
|
||||
import { InputsService } from '../inputs/inputs.service';
|
||||
import { Request } from 'express';
|
||||
|
||||
@ApiTags('oauth')
|
||||
@Controller('oauth')
|
||||
export class OauthController {
|
||||
inputService: InputsService;
|
||||
frontendRedirectUri = '';
|
||||
constructor(private inputsClientService: InputsClientService) {
|
||||
this.inputService = new InputsService(this.inputsClientService);
|
||||
switch (process.env.ENV) {
|
||||
case 'dev':
|
||||
case 'stg':
|
||||
this.frontendRedirectUri = `https://app.${process.env.ENV}.dadosfera.ai/coletix/auth-callback`;
|
||||
break;
|
||||
case 'prd':
|
||||
this.frontendRedirectUri = `https://app.dadosfera.ai/coletix/auth-callback`;
|
||||
break;
|
||||
default:
|
||||
this.frontendRedirectUri = `http://localhost:4200/coletix/auth-callback`;
|
||||
}
|
||||
}
|
||||
@Get('hubspot')
|
||||
@UseGuards(AuthGuard('hubspot'))
|
||||
async oauthHubspot() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Get('hubspot/callback')
|
||||
@UseGuards(AuthGuard('hubspot'))
|
||||
@Redirect()
|
||||
async oauthHubspotCallback(@Req() req) {
|
||||
return await this.callback(req, 'hubspot');
|
||||
}
|
||||
|
||||
@Get('googleads')
|
||||
@UseGuards(AuthGuard('google'))
|
||||
async oauthGoogleAds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Get('googleads/callback')
|
||||
@UseGuards(AuthGuard('google'))
|
||||
@Redirect()
|
||||
async oauthGoogleAdsCallback(@Req() req) {
|
||||
return await this.callback(req, 'googleads');
|
||||
}
|
||||
|
||||
@Get('google-sheets')
|
||||
@UseGuards(AuthGuard('google'))
|
||||
async oauthGoogleSheets() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Get('google-sheets/callback')
|
||||
@UseGuards(AuthGuard('google'))
|
||||
@Redirect()
|
||||
async oauthGoogleSheetsCallback(@Req() req) {
|
||||
return await this.callback(req, 'google-sheets');
|
||||
}
|
||||
|
||||
@Get('facebook')
|
||||
@UseGuards(AuthGuard('facebook'))
|
||||
async oauthFacebook() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Get('facebook/callback')
|
||||
@UseGuards(AuthGuard('facebook'))
|
||||
@Redirect()
|
||||
async oauthFacebookCallback(@Req() req) {
|
||||
return await this.callback(req, 'facebook');
|
||||
}
|
||||
|
||||
@Get('mailchimp')
|
||||
@UseGuards(AuthGuard('mailchimp'))
|
||||
async oauthMailchimp() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Get('mailchimp/callback')
|
||||
@UseGuards(AuthGuard('mailchimp'))
|
||||
@Redirect()
|
||||
async oauthMailchimpCallback(@Req() req) {
|
||||
return await this.callback(req, 'mailchimp');
|
||||
}
|
||||
|
||||
@Get('salesforce')
|
||||
@UseGuards(AuthGuard('salesforce'))
|
||||
async oauthSalesforce() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Get('salesforce/callback')
|
||||
@UseGuards(AuthGuard('salesforce'))
|
||||
@Redirect()
|
||||
async oauthSalesforceCallback(@Req() req) {
|
||||
return await this.callback(req, 'salesforce');
|
||||
}
|
||||
|
||||
async callback(@Req() req: Request, plugin) {
|
||||
const { error, state: customer_id } = req.query;
|
||||
|
||||
if (error)
|
||||
return {
|
||||
url: `${this.frontendRedirectUri}?error=${error}`,
|
||||
};
|
||||
const request: any = req;
|
||||
const { authInfo } = request;
|
||||
|
||||
if (!authInfo)
|
||||
return {
|
||||
url: `${this.frontendRedirectUri}?error=invalid_credentials`,
|
||||
};
|
||||
|
||||
const { accessToken: access_token, refreshToken: refresh_token } = authInfo;
|
||||
|
||||
const response = await this.inputService.create({
|
||||
info: { customer_id, customer: 'customer', user_id: 'empty' },
|
||||
plugin,
|
||||
credentials: { access_token, refresh_token },
|
||||
});
|
||||
|
||||
return { url: `${this.frontendRedirectUri}?input_id=${response.input.id}` };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
Profile,
|
||||
Strategy,
|
||||
StrategyOption,
|
||||
VerifyFunction,
|
||||
} from 'passport-facebook';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { OauthSecrets } from 'src/utils/OauthSecrets';
|
||||
import { Request } from 'express';
|
||||
import { ParamsDictionary } from 'express-serve-static-core';
|
||||
import { ParsedQs } from 'qs';
|
||||
|
||||
@Injectable()
|
||||
export class FacebookStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(
|
||||
@Inject('OAUTH_SECRETS')
|
||||
private readonly oauthSecrets: OauthSecrets,
|
||||
) {
|
||||
const options: StrategyOption = {
|
||||
clientID: oauthSecrets.facebook.client_id,
|
||||
clientSecret: oauthSecrets.facebook.client_secret,
|
||||
callbackURL: oauthSecrets.facebook.redirect_uri,
|
||||
};
|
||||
const verify: VerifyFunction = (
|
||||
accessToken: string,
|
||||
refreshToken: string,
|
||||
profile: Profile,
|
||||
done: (error: any, user?: any, info?: any) => void,
|
||||
) => {
|
||||
return done(null, profile, { accessToken, refreshToken });
|
||||
};
|
||||
|
||||
super(options, verify);
|
||||
}
|
||||
authenticate(
|
||||
req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>,
|
||||
options?: any,
|
||||
): void {
|
||||
const { customer_id } = req.query;
|
||||
options.state = customer_id;
|
||||
options.scope = 'ads_read ads_management';
|
||||
super.authenticate(req, options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Profile, Strategy, StrategyOptions } from 'passport-google-oauth20';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { OauthSecrets } from 'src/utils/OauthSecrets';
|
||||
|
||||
@Injectable()
|
||||
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
|
||||
redirect_uri: string;
|
||||
constructor(
|
||||
@Inject('OAUTH_SECRETS')
|
||||
private readonly oauthSecrets: OauthSecrets,
|
||||
) {
|
||||
const options: StrategyOptions = {
|
||||
clientID: oauthSecrets.google.client_id,
|
||||
clientSecret: oauthSecrets.google.client_secret,
|
||||
callbackURL: '/oauth/googleads/callback',
|
||||
};
|
||||
const verify = (
|
||||
accessToken: string,
|
||||
refreshToken: string,
|
||||
profile: Profile,
|
||||
done,
|
||||
) => {
|
||||
console.log('verify');
|
||||
return done(null, profile, { accessToken, refreshToken });
|
||||
};
|
||||
|
||||
super(options, verify);
|
||||
switch (process.env.ENV) {
|
||||
case 'dev':
|
||||
case 'stg':
|
||||
this.redirect_uri = `https://maestro.${process.env.ENV}.dadosfera.ai/oauth`;
|
||||
break;
|
||||
case 'prd':
|
||||
this.redirect_uri = `https://maestro.dadosfera.ai/oauth`;
|
||||
break;
|
||||
default:
|
||||
this.redirect_uri = `/oauth`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
authenticate(req, options) {
|
||||
const plugin: string = req.route.path
|
||||
.replace('/oauth/', '')
|
||||
.replace('/callback', '');
|
||||
|
||||
const { customer_id } = req.query;
|
||||
options.scope = ['https://www.googleapis.com/auth/userinfo.email'];
|
||||
options.callbackURL = `${this.redirect_uri}/${plugin}/callback`;
|
||||
|
||||
switch (plugin) {
|
||||
case 'googleads':
|
||||
options.scope.push('https://www.googleapis.com/auth/adwords');
|
||||
break;
|
||||
case 'google-sheets':
|
||||
options.scope.push(
|
||||
'https://www.googleapis.com/auth/drive.metadata.readonly',
|
||||
'https://www.googleapis.com/auth/spreadsheets.readonly',
|
||||
);
|
||||
break;
|
||||
}
|
||||
options.state = customer_id;
|
||||
options.accessType = 'offline';
|
||||
options.prompt = 'consent';
|
||||
super.authenticate(req, options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Strategy } from 'passport-hubspot-oauth2';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { OauthSecrets } from 'src/utils/OauthSecrets';
|
||||
|
||||
@Injectable()
|
||||
export class HubspotStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(
|
||||
@Inject('OAUTH_SECRETS')
|
||||
private readonly oauthSecrets: OauthSecrets,
|
||||
) {
|
||||
super(
|
||||
{
|
||||
clientID: oauthSecrets.hubspot.client_id,
|
||||
clientSecret: oauthSecrets.hubspot.client_secret,
|
||||
callbackURL: oauthSecrets.hubspot.redirect_uri,
|
||||
scope:
|
||||
'automation business-intelligence oauth forms integration-sync sales-email-read crm.lists.read crm.objects.contacts.read crm.schemas.contacts.read crm.objects.companies.read crm.objects.deals.read crm.schemas.companies.read crm.schemas.deals.read crm.objects.owners.read crm.objects.quotes.read crm.schemas.quotes.read crm.objects.line_items.read crm.schemas.line_items.read',
|
||||
passReqToCallback: true,
|
||||
},
|
||||
(accessToken, refreshToken, tokenInfo, profile, done) => {
|
||||
return done(null, profile, { accessToken, refreshToken, tokenInfo });
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
authenticate(req, options) {
|
||||
const { customer_id } = req.query;
|
||||
options.state = customer_id;
|
||||
super.authenticate(req, options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Strategy } from 'passport-mailchimp';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Inject, Injectable, Req } from '@nestjs/common';
|
||||
import { OauthSecrets } from 'src/utils/OauthSecrets';
|
||||
|
||||
@Injectable()
|
||||
export class MailChimpStrategy extends PassportStrategy(Strategy, 'mailchimp') {
|
||||
constructor(
|
||||
@Inject('OAUTH_SECRETS')
|
||||
private readonly oauthSecrets: OauthSecrets,
|
||||
) {
|
||||
const options = {
|
||||
clientID: oauthSecrets.mailchimp.client_id,
|
||||
clientSecret: oauthSecrets.mailchimp.client_secret,
|
||||
callbackURL: oauthSecrets.mailchimp.redirect_uri,
|
||||
scope: '',
|
||||
};
|
||||
const verify = (
|
||||
accessToken: string,
|
||||
refreshToken: string,
|
||||
profile: any,
|
||||
done,
|
||||
) => {
|
||||
return done(null, profile, { accessToken, refreshToken });
|
||||
};
|
||||
|
||||
super(options, verify);
|
||||
}
|
||||
|
||||
authenticate(@Req() req, options) {
|
||||
const { customer_id } = req.query;
|
||||
options.state = customer_id;
|
||||
super.authenticate(req, options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Strategy } from 'passport-forcedotcom';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { OauthSecrets } from 'src/utils/OauthSecrets';
|
||||
|
||||
@Injectable()
|
||||
export class SalesforceStrategy extends PassportStrategy(
|
||||
Strategy,
|
||||
'salesforce',
|
||||
) {
|
||||
constructor(
|
||||
@Inject('OAUTH_SECRETS')
|
||||
private readonly oauthSecrets: OauthSecrets,
|
||||
) {
|
||||
const options = {
|
||||
clientID: oauthSecrets.salesforce.client_id,
|
||||
clientSecret: oauthSecrets.salesforce.client_secret,
|
||||
callbackURL: oauthSecrets.salesforce.redirect_uri,
|
||||
scope: [
|
||||
'wave_api',
|
||||
'chatter_api',
|
||||
'chatbot_api',
|
||||
'cdp_ingest_api',
|
||||
'cdp_profile_api',
|
||||
'api',
|
||||
'pardot_api',
|
||||
'cdp_segment_api',
|
||||
'cdp_query_api',
|
||||
'refresh_token',
|
||||
'offline_access',
|
||||
],
|
||||
};
|
||||
const verify = ({ params }, refreshToken: string, profile: any, done) => {
|
||||
return done(null, profile, {
|
||||
accessToken: params.access_token,
|
||||
refreshToken,
|
||||
});
|
||||
};
|
||||
super(options, verify);
|
||||
}
|
||||
|
||||
authenticate(req, options) {
|
||||
const { customer_id } = req.query;
|
||||
options.state = customer_id;
|
||||
super.authenticate(req, options);
|
||||
}
|
||||
}
|
||||
@@ -3,21 +3,57 @@ import {
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
} from '@nestjs/common';
|
||||
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 { OutputsService } from './outputs.service';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
|
||||
@ApiTags('Outputs')
|
||||
@Controller('outputs')
|
||||
@AuthenticateCondition((req, user) => {
|
||||
let action;
|
||||
|
||||
switch (req.method) {
|
||||
case 'POST':
|
||||
action = 'CREATE';
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
action = 'UPDATE';
|
||||
break;
|
||||
|
||||
default:
|
||||
action = req.method;
|
||||
}
|
||||
|
||||
return user.permissions.includes(
|
||||
PERMISSIONS.OUTPUT.permissions[action].seqid,
|
||||
);
|
||||
})
|
||||
export class OutputsController {
|
||||
constructor(private outputsClientService: OutputsClientService) {}
|
||||
logger: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
private outputsClientService: OutputsClientService,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
@Post()
|
||||
async create(@Body() createOutputDto) {
|
||||
console.log(process.env.DEV_URL + `/output`, 'ON CREATE ROUTE');
|
||||
this.logger.info(process.env.DEV_URL + `/output - ON CREATE ROUTE`, {
|
||||
user: createOutputDto.info.user_id,
|
||||
customer: createOutputDto.info.customer,
|
||||
});
|
||||
|
||||
const outputService = new OutputsService(this.outputsClientService);
|
||||
const response = await outputService.create(createOutputDto);
|
||||
@@ -27,7 +63,10 @@ export class OutputsController {
|
||||
|
||||
@Get()
|
||||
async findAll(@Body() data) {
|
||||
console.log(process.env.DEV_URL + `/output`, 'ON Find All ROUTE');
|
||||
this.logger.info(process.env.DEV_URL + `/output - ON Find All ROUTE`, {
|
||||
user: data.info.user_id,
|
||||
customer: data.info.customer,
|
||||
});
|
||||
|
||||
const outputService = new OutputsService(this.outputsClientService);
|
||||
const response = await outputService.findAll(data);
|
||||
@@ -38,7 +77,13 @@ export class OutputsController {
|
||||
@Get('/:id')
|
||||
async findOne(@Body() data, @Param() params) {
|
||||
const { id } = params;
|
||||
console.log(process.env.DEV_URL + `/output/${id}`, 'ON Find One ROUTE');
|
||||
this.logger.info(
|
||||
process.env.DEV_URL + `/output/${id} - ON Find One ROUTE`,
|
||||
{
|
||||
user: data.info.user_id,
|
||||
customer: data.info.customer,
|
||||
},
|
||||
);
|
||||
|
||||
const outputService = new OutputsService(this.outputsClientService);
|
||||
const response = await outputService.findOne({ id, ...data });
|
||||
@@ -50,10 +95,12 @@ export class OutputsController {
|
||||
async update(@Payload() updateOutputDto, @Param() params) {
|
||||
const { id } = params;
|
||||
const { info } = updateOutputDto;
|
||||
this.logger.info(process.env.DEV_URL + `/output/${id} - ON UPDATE ROUTE`, {
|
||||
user: updateOutputDto.info.user_id,
|
||||
customer: updateOutputDto.info.customer,
|
||||
});
|
||||
delete updateOutputDto.info;
|
||||
|
||||
console.log(process.env.DEV_URL + `/output/${id}`, 'ON UPDATE ROUTE');
|
||||
|
||||
const outputService = new OutputsService(this.outputsClientService);
|
||||
const response = await outputService.update(id, updateOutputDto, info);
|
||||
|
||||
@@ -63,7 +110,10 @@ export class OutputsController {
|
||||
@Delete(':id')
|
||||
async delete(@Body() data, @Param() params) {
|
||||
const { id } = params;
|
||||
console.log(process.env.DEV_URL + `/output/${id}`, 'ON DELETE ROUTE');
|
||||
this.logger.info(process.env.DEV_URL + `/output/${id} - ON DELETE ROUTE`, {
|
||||
user: data.info.user_id,
|
||||
customer: data.info.customer,
|
||||
});
|
||||
|
||||
const outputService = new OutputsService(this.outputsClientService);
|
||||
const response = await outputService.remove({ id, ...data });
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
describe('PipelinesGrpcServerService', () => {
|
||||
it('should be defined', () => {
|
||||
expect(2 + 2).toBe(4);
|
||||
});
|
||||
});
|
||||
@@ -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 };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ClientsModule } from '@nestjs/microservices';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
|
||||
import { PermissionsService } from './permissions.service';
|
||||
|
||||
import { DucClient } from '../../clients/duc/client.config';
|
||||
import { PermissionsController } from './permissions.controller';
|
||||
|
||||
const ducClient = new DucClient();
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ClientsModule.register([
|
||||
{
|
||||
name: 'DUC_PACKAGE',
|
||||
...ducClient.config(),
|
||||
},
|
||||
]),
|
||||
],
|
||||
providers: [PermissionsService, DadosferaLogger],
|
||||
controllers: [PermissionsController],
|
||||
exports: [PermissionsService],
|
||||
})
|
||||
export class PermissionsModule {}
|
||||
@@ -0,0 +1,118 @@
|
||||
import {
|
||||
OnApplicationBootstrap,
|
||||
OnModuleInit,
|
||||
Inject,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { ClientGrpc } from '@nestjs/microservices';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
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 PermissionsService
|
||||
implements OnModuleInit, OnApplicationBootstrap
|
||||
{
|
||||
logger: DadosferaLogger;
|
||||
|
||||
private permissionsClientService: PermissionsServiceInterface;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
@Inject('DUC_PACKAGE') private readonly grpcClient: ClientGrpc,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
onModuleInit() {
|
||||
this.permissionsClientService =
|
||||
this.grpcClient.getService<PermissionsServiceInterface>(
|
||||
ProtoServices.PermissionsProtoService,
|
||||
);
|
||||
}
|
||||
|
||||
// internally used to send permissions to duc on microservice startup
|
||||
async onApplicationBootstrap() {
|
||||
this.logger.info('sending permissions to DUC...');
|
||||
|
||||
const permissions = Object.values(PERMISSIONS).flatMap((namespace) =>
|
||||
Object.values(namespace.permissions),
|
||||
);
|
||||
|
||||
return this.injectPermissions({
|
||||
permissions,
|
||||
}).catch((err: ErrorBuilder) => {
|
||||
if (
|
||||
err.code === 'No connection established' &&
|
||||
process.env.LOCAL_ENV === 'true'
|
||||
) {
|
||||
return this.logger.info(
|
||||
"couldn't connect to DUC. suppresing in local env",
|
||||
);
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
async injectPermissions({ permissions }: InjectPermissionsRequest) {
|
||||
this.logger.info('InjectPermissions');
|
||||
|
||||
return lastValueFrom(
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -3,25 +3,61 @@ import {
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
} from '@nestjs/common';
|
||||
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 { PipelinesService } from './pipelines.service';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
|
||||
@ApiTags('Pipelines')
|
||||
@Controller('pipelines')
|
||||
@AuthenticateCondition((req, user) => {
|
||||
let action;
|
||||
|
||||
switch (req.method) {
|
||||
case 'POST':
|
||||
action = 'CREATE';
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
action = 'UPDATE';
|
||||
break;
|
||||
|
||||
default:
|
||||
action = req.method;
|
||||
}
|
||||
|
||||
return user.permissions.includes(
|
||||
PERMISSIONS.PIPELINE.permissions[action].seqid,
|
||||
);
|
||||
})
|
||||
export class PipelinesController {
|
||||
constructor(private pipelinesClientService: PipelinesClientService) {}
|
||||
logger: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
private pipelinesClientService: PipelinesClientService,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
@Post('start/:id')
|
||||
async activate(@Param() params, @Body() body) {
|
||||
const { id } = params;
|
||||
const { info } = body;
|
||||
|
||||
console.log(
|
||||
process.env.DEV_URL + `/pipeline/start/${id}`,
|
||||
'ON START PIPELINE ROUTE',
|
||||
this.logger.info(
|
||||
process.env.DEV_URL + `/pipeline/start/${id} - ON START PIPELINE ROUTE`,
|
||||
{
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
},
|
||||
);
|
||||
|
||||
const pipelineService = new PipelinesService(this.pipelinesClientService);
|
||||
@@ -35,9 +71,12 @@ export class PipelinesController {
|
||||
const { id } = params;
|
||||
body.id = id;
|
||||
|
||||
console.log(
|
||||
process.env.DEV_URL + `/pipeline/${id}`,
|
||||
'ON GET PIPELINE STATUS ROUTE',
|
||||
this.logger.info(
|
||||
process.env.DEV_URL + `/pipeline/${id} - ON GET PIPELINE STATUS ROUTE`,
|
||||
{
|
||||
user: body.info.user_id,
|
||||
customer: body.info.customer,
|
||||
},
|
||||
);
|
||||
|
||||
const pipelineService = new PipelinesService(this.pipelinesClientService);
|
||||
@@ -49,9 +88,9 @@ export class PipelinesController {
|
||||
@Get(':id/:details')
|
||||
async getPipelineLogs(@Param() params) {
|
||||
const { id, details } = params;
|
||||
console.log(
|
||||
process.env.DEV_URL + `/pipeline/${id}`,
|
||||
'ON GET PIPELINE LOGS ROUTE',
|
||||
this.logger.info(
|
||||
process.env.DEV_URL + `/pipeline/${id} - ON GET PIPELINE LOGS ROUTE`,
|
||||
{},
|
||||
);
|
||||
|
||||
const pipelineService = new PipelinesService(this.pipelinesClientService);
|
||||
@@ -62,7 +101,7 @@ export class PipelinesController {
|
||||
|
||||
@Post()
|
||||
async create(@Body() createPipelineDto) {
|
||||
console.log(process.env.DEV_URL + `/pipelines`, 'ON CREATE ROUTE');
|
||||
this.logger.info(process.env.DEV_URL + `/pipelines ON CREATE ROUTE`);
|
||||
|
||||
const pipelineService = new PipelinesService(this.pipelinesClientService);
|
||||
const response = await pipelineService.create(createPipelineDto);
|
||||
@@ -72,7 +111,10 @@ export class PipelinesController {
|
||||
|
||||
@Get()
|
||||
async findAll(@Body() data) {
|
||||
console.log(process.env.DEV_URL + `/pipeline`, 'ON Find All ROUTE');
|
||||
this.logger.info(process.env.DEV_URL + `/pipeline ON Find All ROUTE`, {
|
||||
user: data.info.user_id,
|
||||
customer: data.info.customer,
|
||||
});
|
||||
|
||||
const pipelineService = new PipelinesService(this.pipelinesClientService);
|
||||
const response = await pipelineService.findAll(data);
|
||||
@@ -83,7 +125,7 @@ export class PipelinesController {
|
||||
@Get('/:id')
|
||||
async findOne(@Body() data, @Param() params) {
|
||||
const { id } = params;
|
||||
console.log(process.env.DEV_URL + `/pipeline/${id}`, 'ON Find One ROUTE');
|
||||
this.logger.info(process.env.DEV_URL + `/pipeline/${id} ON Find One ROUTE`);
|
||||
|
||||
const pipelineService = new PipelinesService(this.pipelinesClientService);
|
||||
const response = await pipelineService.findOne({ id, ...data });
|
||||
@@ -97,7 +139,7 @@ export class PipelinesController {
|
||||
const { info } = updatePipelineDto;
|
||||
delete updatePipelineDto.info;
|
||||
|
||||
console.log(process.env.DEV_URL + `/pipeline/${id}`, 'ON UPDATE ROUTE');
|
||||
this.logger.info(process.env.DEV_URL + `/pipeline/${id} ON UPDATE ROUTE`);
|
||||
|
||||
const pipelineService = new PipelinesService(this.pipelinesClientService);
|
||||
const response = await pipelineService.update(id, updatePipelineDto, info);
|
||||
@@ -108,7 +150,7 @@ export class PipelinesController {
|
||||
@Delete(':id')
|
||||
async delete(@Body() data, @Param() params) {
|
||||
const { id } = params;
|
||||
console.log(process.env.DEV_URL + `/pipeline/${id}`, 'ON DELETE ROUTE');
|
||||
this.logger.info(process.env.DEV_URL + `/pipeline/${id} ON DELETE ROUTE`);
|
||||
|
||||
const pipelineService = new PipelinesService(this.pipelinesClientService);
|
||||
const response = await pipelineService.remove({ id, ...data });
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
describe('PipelinesGrpcServerService', () => {
|
||||
it('should be defined', () => {
|
||||
expect(2 + 2).toBe(4);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { DecodeGrpcStruct } from 'protospack';
|
||||
import { PipelinesClientService } from 'src/clients/pipelines/client.service';
|
||||
import { IIdRequest, Info } from 'src/clients/pipelines/interfaces';
|
||||
import { objectCamelToSnake } from 'src/utils/CaseConverter';
|
||||
@@ -8,23 +9,22 @@ export class PipelinesService {
|
||||
constructor(private pipelineClient: PipelinesClientService) {}
|
||||
|
||||
adjustPayload(payload) {
|
||||
payload.input = payload.input?.input_s3 || payload.input?.input_jdbc;
|
||||
if (payload.input?.input_generic) {
|
||||
payload.input = DecodeGrpcStruct(payload.input?.input_generic);
|
||||
} else {
|
||||
payload.input = payload.input?.input_s3 || payload.input?.input_jdbc;
|
||||
}
|
||||
}
|
||||
|
||||
async create(createPipelineDto) {
|
||||
try {
|
||||
const createPipelineResponse = await this.pipelineClient.create(
|
||||
createPipelineDto,
|
||||
);
|
||||
const createPipelineResponse = await this.pipelineClient.create(
|
||||
createPipelineDto,
|
||||
);
|
||||
|
||||
const pipeline = objectCamelToSnake(createPipelineResponse);
|
||||
this.adjustPayload(pipeline.pipeline);
|
||||
const pipeline = objectCamelToSnake(createPipelineResponse);
|
||||
this.adjustPayload(pipeline.pipeline);
|
||||
|
||||
return pipeline;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
async findOne(data: IIdRequest) {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -3,24 +3,61 @@ import {
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
} from '@nestjs/common';
|
||||
import { Payload } from '@nestjs/microservices';
|
||||
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 { TransformationsService } from './transformations.service';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
|
||||
@ApiTags('Transformations')
|
||||
@Controller('transformations')
|
||||
@AuthenticateCondition((req, user) => {
|
||||
let action;
|
||||
|
||||
switch (req.method) {
|
||||
case 'POST':
|
||||
action = 'CREATE';
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
action = 'UPDATE';
|
||||
break;
|
||||
|
||||
default:
|
||||
action = req.method;
|
||||
}
|
||||
|
||||
return user.permissions.includes(
|
||||
PERMISSIONS.TRANSFORMATIONS.permissions[action].seqid,
|
||||
);
|
||||
})
|
||||
export class TransformationsController {
|
||||
logger: DadosferaLogger;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
private transformationsClientService: TransformationsClientService,
|
||||
) {}
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
@Post()
|
||||
async create(@Body() createTransformationDto) {
|
||||
console.log(process.env.DEV_URL + `/transformation`, 'ON CREATE ROUTE');
|
||||
this.logger.info(
|
||||
process.env.DEV_URL + `/transformation - ON CREATE ROUTE`,
|
||||
{
|
||||
user: createTransformationDto.info.user_id,
|
||||
customer: createTransformationDto.info.customer,
|
||||
},
|
||||
);
|
||||
|
||||
const transformationService = new TransformationsService(
|
||||
this.transformationsClientService,
|
||||
@@ -34,7 +71,13 @@ export class TransformationsController {
|
||||
|
||||
@Get()
|
||||
async findAll(@Body() data: IIdRequest) {
|
||||
console.log(process.env.DEV_URL + `/transformation`, 'ON Find All ROUTE');
|
||||
this.logger.info(
|
||||
process.env.DEV_URL + `/transformation - ON Find All ROUTE`,
|
||||
{
|
||||
user: data.info.user_id,
|
||||
customer: data.info.customer_id,
|
||||
},
|
||||
);
|
||||
|
||||
const transformationService = new TransformationsService(
|
||||
this.transformationsClientService,
|
||||
@@ -47,9 +90,12 @@ export class TransformationsController {
|
||||
@Get('/:id')
|
||||
async findOne(@Body() data, @Param() params) {
|
||||
const { id } = params;
|
||||
console.log(
|
||||
process.env.DEV_URL + `/transformation/${id}`,
|
||||
'ON Find One ROUTE',
|
||||
this.logger.info(
|
||||
process.env.DEV_URL + `/transformation/${id} - ON Find One ROUTE`,
|
||||
{
|
||||
user: data.info.user_id,
|
||||
customer: data.info.customer,
|
||||
},
|
||||
);
|
||||
|
||||
const transformationService = new TransformationsService(
|
||||
@@ -63,9 +109,12 @@ export class TransformationsController {
|
||||
@Put(':id')
|
||||
async update(@Payload() updateTransformationDto, @Param() params) {
|
||||
const { id } = params;
|
||||
console.log(
|
||||
process.env.DEV_URL + `/transformation/${id}`,
|
||||
'ON UPDATE ROUTE',
|
||||
this.logger.info(
|
||||
process.env.DEV_URL + `/transformation/${id} - ON UPDATE ROUTE`,
|
||||
{
|
||||
user: updateTransformationDto.info.user_id,
|
||||
customer: updateTransformationDto.info.customer,
|
||||
},
|
||||
);
|
||||
|
||||
const transformationService = new TransformationsService(
|
||||
@@ -82,9 +131,12 @@ export class TransformationsController {
|
||||
@Delete(':id')
|
||||
async delete(@Body() data, @Param() params) {
|
||||
const { id } = params;
|
||||
console.log(
|
||||
process.env.DEV_URL + `/transformation/${id}`,
|
||||
'ON DELETE ROUTE',
|
||||
this.logger.info(
|
||||
process.env.DEV_URL + `/transformation/${id} - ON DELETE ROUTE`,
|
||||
{
|
||||
user: data.info.user_id,
|
||||
customer: data.info.customer,
|
||||
},
|
||||
);
|
||||
|
||||
const transformationService = new TransformationsService(
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
describe('PipelinesGrpcServerService', () => {
|
||||
it('should be defined', () => {
|
||||
expect(2 + 2).toBe(4);
|
||||
});
|
||||
});
|
||||
@@ -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[];
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
+116
-36
@@ -1,131 +1,211 @@
|
||||
import { HttpStatus, HttpException } from '@nestjs/common';
|
||||
import { HttpStatus, HttpException, Logger } from '@nestjs/common';
|
||||
import { RpcException } from '@nestjs/microservices';
|
||||
|
||||
import ErrorCodes from './errorCodes';
|
||||
|
||||
function Builder({ statusCode, message, error, code }) {
|
||||
return new HttpException({ statusCode, message, error, code }, statusCode);
|
||||
}
|
||||
|
||||
export default function ErrorBuilder(code: string) {
|
||||
console.log(code);
|
||||
|
||||
export function EnrichErrorCode(code: string) {
|
||||
switch (code) {
|
||||
case ErrorCodes.AUTH.WRONG_CREDENTIALS:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não autenticado',
|
||||
message: 'Usuário ou senha incorretos',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.WRONG_PASSWORD_CONFIRMATION:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Senha incorreta',
|
||||
message: 'Confirmação de senha incorreta',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.TOTP_NOT_ENABLED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.PRECONDITION_FAILED,
|
||||
error: 'Não permitido',
|
||||
message: 'A autenticação multifator não está habilitada',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.TOTP_ALREADY_ENABLED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.PRECONDITION_FAILED,
|
||||
error: 'Não permitido',
|
||||
message: 'A autenticação multifator já está habilitada',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.TOTP_ALREADY_DISABLED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.PRECONDITION_FAILED,
|
||||
error: 'Não permitido',
|
||||
message: 'A autenticação multifator já está desabilitada',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.TOTP_REQUIRED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não autenticado',
|
||||
message: 'Informe o token de autenticação multifator',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.CODE_MISMATCH:
|
||||
case ErrorCodes.AUTH.CODE_ALREADY_USED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não autenticado',
|
||||
message: 'Token de autenticação multifator inválido',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.RESET_PASSWORD_CODE_EXPIRED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não permitido',
|
||||
message: 'Token para recuperar senha expirado',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.RESET_PASSWORD_CODE_INVALID:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não permitido',
|
||||
message: 'Token para recuperar senha inválido',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.WEAK_NEW_PASSWORD:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.BAD_REQUEST,
|
||||
error: 'Não permitido',
|
||||
message: 'Senha muito fraca. Escolha uma senha mais forte',
|
||||
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 Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.TOO_MANY_REQUESTS,
|
||||
error: 'Limite excedido',
|
||||
message:
|
||||
'Você tentou realizar essa operação muitas vezes. Tente novamente mais tarde',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.UNAUTHORIZED:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Não autenticado',
|
||||
message: 'É necessário estar logado para realizar essa operação',
|
||||
code,
|
||||
});
|
||||
};
|
||||
|
||||
case ErrorCodes.AUTH.FORBIDDEN:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.FORBIDDEN,
|
||||
error: 'Não autorizado',
|
||||
message:
|
||||
'Você não tem permissões suficientes para realizar essa operação',
|
||||
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:
|
||||
return Builder({
|
||||
return {
|
||||
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
error: 'Desconhecido',
|
||||
message:
|
||||
'Erro desconhecido. Tente novamente ou entre em contato com o suporte',
|
||||
code: ErrorCodes.UNKNOWN,
|
||||
});
|
||||
details: code,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const logger = new Logger();
|
||||
|
||||
export default class ErrorBuilder extends HttpException {
|
||||
code: string;
|
||||
|
||||
constructor(code: string | RpcException = '') {
|
||||
if (typeof code !== 'string') {
|
||||
logger.log(code?.stack);
|
||||
code = (code as any).details as string;
|
||||
} else {
|
||||
logger.log(code);
|
||||
}
|
||||
|
||||
const {
|
||||
statusCode,
|
||||
message,
|
||||
error,
|
||||
code: rCode,
|
||||
details,
|
||||
} = EnrichErrorCode(code);
|
||||
super({ statusCode, message, error, code: rCode, details }, statusCode);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import {
|
||||
SecretsManagerClient,
|
||||
GetSecretValueCommand,
|
||||
} from '@aws-sdk/client-secrets-manager';
|
||||
|
||||
class OauthSecretsObject {
|
||||
client_id = '';
|
||||
client_secret = '';
|
||||
redirect_uri = '';
|
||||
}
|
||||
// @Injectable()
|
||||
export class OauthSecrets {
|
||||
hubspot = new OauthSecretsObject();
|
||||
google = new OauthSecretsObject();
|
||||
mailchimp = new OauthSecretsObject();
|
||||
facebook = new OauthSecretsObject();
|
||||
salesforce = new OauthSecretsObject();
|
||||
}
|
||||
export async function getOauthSecrets() {
|
||||
const secrets = new OauthSecrets();
|
||||
const path = process.env.SM_OAUTH_PATH;
|
||||
const secretsManagerClient = new SecretsManagerClient({});
|
||||
for (const key in secrets) {
|
||||
const getSecretComand = new GetSecretValueCommand({
|
||||
SecretId: `${path}/${key}`,
|
||||
});
|
||||
const res = await secretsManagerClient
|
||||
.send(getSecretComand)
|
||||
.catch(() => null);
|
||||
if (res) {
|
||||
secrets[key] = JSON.parse(res.SecretString);
|
||||
} else {
|
||||
secrets[key] = {
|
||||
client_id: 'id',
|
||||
client_secret: 'id',
|
||||
redirect_uri: 'id',
|
||||
};
|
||||
}
|
||||
}
|
||||
return secrets;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum LanguageEnum {
|
||||
ptbr = 'pt-br',
|
||||
enus = 'en-us',
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user