mirror of
https://github.com/dadosfera/maestro.git
synced 2026-09-01 04:08:16 +00:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b280871bce | ||
|
|
fd73663940 | ||
|
|
e420bf6547 | ||
|
|
cc4cc99ccc | ||
|
|
fd0d68b149 | ||
|
|
98759b1cf5 | ||
|
|
695fac97e9 | ||
|
|
86b076246e | ||
|
|
faa98cae8b | ||
|
|
313ecc81ec | ||
|
|
820d71d16d | ||
|
|
734978b76f |
+1
-1
@@ -8,7 +8,7 @@ module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
|
||||
@@ -3,7 +3,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
environment:
|
||||
@@ -12,6 +11,7 @@ on:
|
||||
type: choice
|
||||
options:
|
||||
- stg
|
||||
- stg2
|
||||
- prd
|
||||
push_to_dockerhub:
|
||||
description: "Push image to Dockerhub?"
|
||||
@@ -34,8 +34,6 @@ jobs:
|
||||
echo "environment=${DEPLOY_ENV}" >> $GITHUB_OUTPUT
|
||||
elif [ ${GITHUB_REF} == "refs/heads/main" ]; then
|
||||
echo "environment=prd" >> $GITHUB_OUTPUT
|
||||
elif [ ${GITHUB_REF} == "refs/heads/beta" ]; then
|
||||
echo "environment=stg" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
id: extract_environment
|
||||
|
||||
@@ -50,40 +48,38 @@ jobs:
|
||||
|
||||
- if: github.event_name != 'workflow_dispatch'
|
||||
name: Semantic Release
|
||||
uses: cycjimmy/semantic-release-action@v3
|
||||
uses: cycjimmy/semantic-release-action@v4
|
||||
id: semantic
|
||||
with:
|
||||
extra_plugins: |
|
||||
conventional-changelog-eslint@4.0.0
|
||||
branches: |
|
||||
[
|
||||
'main',
|
||||
{
|
||||
name: 'alpha',
|
||||
prerelease: true
|
||||
},
|
||||
{
|
||||
name: 'beta',
|
||||
prerelease: true
|
||||
}
|
||||
'main'
|
||||
]
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build_ecr_image:
|
||||
if: ${{ github.event_name == 'workflow_dispatch' || needs.semantic_release.outputs.new_release_published == 'true' }}
|
||||
deploy-info:
|
||||
if: ${{ github.event_name == 'workflow_dispatch'}}
|
||||
needs: [extract_environment, semantic_release]
|
||||
runs-on:
|
||||
[self-hosted, "prd"]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Printing stats
|
||||
- name: Create summary
|
||||
env:
|
||||
EVENT: ${{ github.event_name }}
|
||||
IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
|
||||
ENV: ${{ needs.extract_environment.outputs.environment }}
|
||||
run: echo ${GITHUB_REF#refs/heads/}
|
||||
run: echo "### Deploy da branch \`$GITHUB_REF_NAME\` no ambiente **$ENV** :rocket:" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
deploy:
|
||||
if: ${{ github.event_name == 'workflow_dispatch' || needs.semantic_release.outputs.new_release_published == 'true' }}
|
||||
needs: [extract_environment, semantic_release]
|
||||
runs-on:
|
||||
[self-hosted, "${{ needs.extract_environment.outputs.environment }}"]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -91,6 +87,10 @@ jobs:
|
||||
run: |
|
||||
python3 -m pip install --upgrade pip
|
||||
|
||||
- name: Install Docker Compose
|
||||
run: |
|
||||
python3 -m pip install docker-compose --upgrade
|
||||
|
||||
- name: Install AWS CLI
|
||||
run: |
|
||||
python3 -m pip install awscli --upgrade
|
||||
@@ -115,8 +115,8 @@ jobs:
|
||||
IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
|
||||
ACCOUNT_ID: ${{ steps.aws.outputs.aws-account-id }}
|
||||
run: |
|
||||
docker compose -f build.docker-compose.yml build
|
||||
docker compose -f build.docker-compose.yml push
|
||||
docker-compose -f build.docker-compose.yml build
|
||||
docker-compose -f build.docker-compose.yml push
|
||||
|
||||
- name: Login to Docker Hub
|
||||
if: ${{inputs.push_to_dockerhub}}
|
||||
@@ -132,86 +132,97 @@ jobs:
|
||||
IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
|
||||
ACCOUNT_ID: ${{ steps.aws.outputs.aws-account-id }}
|
||||
run: |
|
||||
docker compose -f build.docker-compose.dockerhub.yml build
|
||||
docker compose -f build.docker-compose.dockerhub.yml push
|
||||
docker-compose -f build.docker-compose.dockerhub.yml build
|
||||
docker-compose -f build.docker-compose.dockerhub.yml push
|
||||
|
||||
# - name: Create ZIP file to Deploy AWS Beanstalk
|
||||
# env:
|
||||
# ENV: ${{ needs.extract_environment.outputs.environment }}
|
||||
# IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
|
||||
# ACCOUNT_ID: ${{ steps.aws.outputs.aws-account-id }}
|
||||
# NODE_EXPORTER_URL: ${{needs.extract_environment.outputs.environment == 'prd' && '611330257153.dkr.ecr.us-east-1.amazonaws.com\/monitoring\/node_exporter:latest' || '468720548566.dkr.ecr.us-east-1.amazonaws.com\/monitoring\/node_exporter:latest'}}
|
||||
# run: |
|
||||
# sed -i -e "s/\${ENV}/$ENV/g" docker-compose.yml
|
||||
# sed -i -e "s/\${IMAGE_TAG}/$IMAGE_TAG/g" docker-compose.yml
|
||||
# sed -i -e "s/\${ACCOUNT_ID}/$ACCOUNT_ID/g" docker-compose.yml
|
||||
# sed -i -e "s/\${NODE_EXPORTER_URL}/$NODE_EXPORTER_URL/g" docker-compose.yml
|
||||
# zip deploy.zip docker-compose.yml -r .ebextensions
|
||||
- name: Create ZIP file to Deploy AWS Beanstalk
|
||||
env:
|
||||
ENV: ${{ needs.extract_environment.outputs.environment }}
|
||||
IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
|
||||
ACCOUNT_ID: ${{ steps.aws.outputs.aws-account-id }}
|
||||
NODE_EXPORTER_URL: ${{needs.extract_environment.outputs.environment == 'prd' && '611330257153.dkr.ecr.us-east-1.amazonaws.com\/monitoring\/node_exporter:latest' || '468720548566.dkr.ecr.us-east-1.amazonaws.com\/monitoring\/node_exporter:latest'}}
|
||||
run: |
|
||||
sed -i -e "s/\${ENV}/$ENV/g" docker-compose.yml
|
||||
sed -i -e "s/\${IMAGE_TAG}/$IMAGE_TAG/g" docker-compose.yml
|
||||
sed -i -e "s/\${ACCOUNT_ID}/$ACCOUNT_ID/g" docker-compose.yml
|
||||
sed -i -e "s/\${NODE_EXPORTER_URL}/$NODE_EXPORTER_URL/g" docker-compose.yml
|
||||
zip deploy.zip docker-compose.yml -r .ebextensions
|
||||
|
||||
# - name: Deploy AWS Beanstalk
|
||||
# env:
|
||||
# ENV: ${{ needs.extract_environment.outputs.environment }}
|
||||
# AWS_REGION: us-east-1
|
||||
# APP_NAME: ${{ github.event.repository.name }}
|
||||
# run: |
|
||||
# eb use $APP_NAME-$ENV
|
||||
# echo -e "deploy:\n artifact: deploy.zip" >> .elasticbeanstalk/config.yml
|
||||
# eb deploy
|
||||
- name: Deploy AWS Beanstalk
|
||||
env:
|
||||
ENV: ${{ needs.extract_environment.outputs.environment }}
|
||||
AWS_REGION: us-east-1
|
||||
APP_NAME: ${{ github.event.repository.name }}
|
||||
run: |
|
||||
eb use $APP_NAME-$ENV
|
||||
echo -e "deploy:\n artifact: deploy.zip" >> .elasticbeanstalk/config.yml
|
||||
eb deploy
|
||||
|
||||
- name: Remove Docker's Trash
|
||||
if: always()
|
||||
run: |
|
||||
docker system prune --volumes -a -f
|
||||
docker system df
|
||||
|
||||
helmfile-deploy:
|
||||
needs: [extract_environment, semantic_release, build_ecr_image]
|
||||
runs-on: [self-hosted, "prd-azure"]
|
||||
|
||||
api_docs:
|
||||
needs: [extract_environment, semantic_release, deploy]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v1
|
||||
with:
|
||||
version: 'v3.9.0'
|
||||
|
||||
- name: Install Azure ClI
|
||||
run: |
|
||||
curl -sL https://aka.ms/InstallAzureCLIDeb | bash
|
||||
|
||||
- uses: azure/login@v2
|
||||
with:
|
||||
creds: '{"clientId":"${{ secrets.ARM_CLIENT_ID }}","clientSecret":"${{ secrets.ARM_CLIENT_SECRET }}","subscriptionId":"${{ secrets.ARM_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.ARM_TENANT_ID }}"}'
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.8'
|
||||
|
||||
- name: Install Helmfile
|
||||
run: |
|
||||
wget https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz
|
||||
tar -xzf helmfile_0.148.0_linux_amd64.tar.gz
|
||||
mv helmfile /usr/local/bin/
|
||||
helmfile --version
|
||||
|
||||
- name: Install Helm Diff Plugin
|
||||
run: helm plugin install https://github.com/databus23/helm-diff || true
|
||||
|
||||
- name: Setup kubectl
|
||||
uses: azure/setup-kubectl@v1
|
||||
with:
|
||||
version: 'v1.30.1'
|
||||
|
||||
- name: Authenticate with cluster
|
||||
env:
|
||||
CLUSTER_NAME: platform-${{ needs.extract_environment.outputs.environment }}
|
||||
run: az aks get-credentials --resource-group dadosfera-prd --name ${CLUSTER_NAME} --overwrite-existing
|
||||
|
||||
- name: Run Helmfile Apply
|
||||
- name: Extract Docs BlockId and PageId
|
||||
env:
|
||||
ENV: ${{ needs.extract_environment.outputs.environment }}
|
||||
IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
|
||||
run: helmfile -f helmfiles/${ENV}.yaml sync --set image.tag=$IMAGE_TAG
|
||||
STG2_DOCS_BLOCK_ID: ${{ secrets.DEV_DOCS_BLOCK_ID }}
|
||||
STG2_DOCS_PAGE_ID: ${{ secrets.DEV_DOCS_PAGE_ID }}
|
||||
STG_DOCS_BLOCK_ID: ${{ secrets.STG_DOCS_BLOCK_ID }}
|
||||
STG_DOCS_PAGE_ID: ${{ secrets.STG_DOCS_PAGE_ID }}
|
||||
PRD_DOCS_BLOCK_ID: ${{ secrets.PRD_DOCS_BLOCK_ID }}
|
||||
PRD_DOCS_PAGE_ID: ${{ secrets.PRD_DOCS_PAGE_ID }}
|
||||
shell: bash
|
||||
run: |
|
||||
if [ $ENV == "stg2" ]; then
|
||||
echo "block_id=$STG2_DOCS_BLOCK_ID" >> $GITHUB_OUTPUT
|
||||
echo "page_id=$STG2_DOCS_PAGE_ID" >> $GITHUB_OUTPUT
|
||||
elif [ $ENV == "stg" ]; then
|
||||
echo "block_id=$STG_DOCS_BLOCK_ID" >> $GITHUB_OUTPUT
|
||||
echo "page_id=$STG_DOCS_PAGE_ID" >> $GITHUB_OUTPUT
|
||||
elif [ $ENV == "prd" ]; then
|
||||
echo "block_id=$PRD_DOCS_BLOCK_ID" >> $GITHUB_OUTPUT
|
||||
echo "page_id=$PRD_DOCS_PAGE_ID" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
id: extract_docs_info
|
||||
|
||||
- name: Generate API docs
|
||||
env:
|
||||
DOCS_URL: ${{ secrets.DOCS_URL }}
|
||||
DOCS_API_TOKEN: ${{ secrets.DOCS_API_TOKEN }}
|
||||
DOCS_PAGE_ID: ${{ steps.extract_docs_info.outputs.page_id }}
|
||||
DOCS_BLOCK_ID: ${{ steps.extract_docs_info.outputs.block_id }}
|
||||
EVENT: ${{ github.event_name }}
|
||||
RELEASE_VERSION: ${{ needs.semantic_release.outputs.new_release_version }}
|
||||
run: |
|
||||
if [ ${EVENT} != "workflow_dispatch" ]; then
|
||||
sed -i -E "s/(\"title\": )\"Maestro.+\"/\1\"Maestro - $RELEASE_VERSION\"/g" docsfera.json
|
||||
fi
|
||||
sed -i -e "s/\${DOCS_API_TOKEN}/$DOCS_API_TOKEN/g" docsfera.json
|
||||
sed -i -e "s/\${DOCS_PAGE_ID}/$DOCS_PAGE_ID/g" docsfera.json
|
||||
sed -i -e "s/\${DOCS_BLOCK_ID}/$DOCS_BLOCK_ID/g" docsfera.json
|
||||
curl -X POST -H 'Content-Type: application/json' -d @docsfera.json $DOCS_URL
|
||||
|
||||
- name: Generate External API docs
|
||||
env:
|
||||
DOCS_URL: ${{ secrets.DOCS_URL }}
|
||||
DOCS_API_TOKEN: ${{ secrets.DOCS_API_TOKEN }}
|
||||
DOCS_PAGE_ID: ${{secrets.EXTERNAL_DOCS_PAGE_ID}}
|
||||
DOCS_BLOCK_ID: ${{secrets.EXTERNAL_DOCS_BLOCK_ID}}
|
||||
EVENT: ${{ github.event_name }}
|
||||
RELEASE_VERSION: ${{ needs.semantic_release.outputs.new_release_version }}
|
||||
run: |
|
||||
if [ ${EVENT} != "workflow_dispatch" ]; then
|
||||
sed -i -E "s/(\"title\": )\"Maestro.+\"/\1\"Maestro - $RELEASE_VERSION\"/g" docsfera.external.json
|
||||
fi
|
||||
sed -i -e "s/\${DOCS_API_TOKEN}/$DOCS_API_TOKEN/g" docsfera.external.json
|
||||
sed -i -e "s/\${DOCS_PAGE_ID}/$DOCS_PAGE_ID/g" docsfera.external.json
|
||||
sed -i -e "s/\${DOCS_BLOCK_ID}/$DOCS_BLOCK_ID/g" docsfera.external.json
|
||||
curl -X POST -H 'Content-Type: application/json' -d @docsfera.external.json $DOCS_URL
|
||||
|
||||
@@ -6,18 +6,23 @@ on:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: [self-hosted, prd]
|
||||
env:
|
||||
APP_NAME: ${{ github.event.repository.name }}
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build
|
||||
run: docker build -t ${APP_NAME}_teste --target test .
|
||||
env:
|
||||
APP_NAME: ${{ github.event.repository.name }}
|
||||
run: |
|
||||
docker build -t $APP_NAME --target test .
|
||||
|
||||
- name: Run Test
|
||||
run: docker run ${APP_NAME}_teste
|
||||
env:
|
||||
ENV: test
|
||||
APP_NAME: ${{ github.event.repository.name }}
|
||||
run: |
|
||||
docker run --rm --entrypoint="npm" $APP_NAME run test
|
||||
|
||||
- name: Remove Docker's Trash
|
||||
if: always()
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
name: Validate K8S Modifications
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- stg
|
||||
|
||||
jobs:
|
||||
extract_environment:
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
environment: ${{ steps.extract_environment.outputs.environment }}
|
||||
steps:
|
||||
- name: Extract Environment
|
||||
run: |
|
||||
if [ "${{ github.event.pull_request.base.ref }}" == "main" ]; then
|
||||
echo "environment=prd" >> $GITHUB_OUTPUT
|
||||
elif [ "${{ github.event.pull_request.base.ref }}" == "beta" ]; then
|
||||
echo "environment=stg" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
id: extract_environment
|
||||
|
||||
helmfile-deploy:
|
||||
needs: [extract_environment]
|
||||
runs-on: [self-hosted, "prd-azure"]
|
||||
|
||||
steps:
|
||||
- name: Summary
|
||||
env:
|
||||
ENV: ${{ needs.extract_environment.outputs.environment }}
|
||||
run: |
|
||||
echo "### :rocket: Deploy da branch \`$GITHUB_REF_NAME\` para o environment ($ENV)" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v1
|
||||
with:
|
||||
version: 'v3.9.0'
|
||||
|
||||
- name: Install Azure ClI
|
||||
run: |
|
||||
curl -sL https://aka.ms/InstallAzureCLIDeb | bash
|
||||
|
||||
- uses: azure/login@v2
|
||||
with:
|
||||
creds: '{"clientId":"${{ secrets.ARM_CLIENT_ID }}","clientSecret":"${{ secrets.ARM_CLIENT_SECRET }}","subscriptionId":"${{ secrets.ARM_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.ARM_TENANT_ID }}"}'
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.8'
|
||||
|
||||
- name: Install Helmfile
|
||||
run: |
|
||||
wget https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz
|
||||
tar -xzf helmfile_0.148.0_linux_amd64.tar.gz
|
||||
mv helmfile /usr/local/bin/
|
||||
helmfile --version
|
||||
|
||||
- name: Install Helm Diff Plugin
|
||||
run: helm plugin install https://github.com/databus23/helm-diff || true
|
||||
|
||||
- name: Setup kubectl
|
||||
uses: azure/setup-kubectl@v1
|
||||
with:
|
||||
version: 'v1.30.1'
|
||||
|
||||
- name: Authenticate with cluster
|
||||
env:
|
||||
CLUSTER_NAME: platform-${{ needs.extract_environment.outputs.environment }}
|
||||
run: az aks get-credentials --resource-group dadosfera-prd --name ${CLUSTER_NAME} --overwrite-existing
|
||||
|
||||
- name: Run Helmfile Diff
|
||||
env:
|
||||
ENV: ${{ needs.extract_environment.outputs.environment }}
|
||||
run: helmfile -f helmfiles/${ENV}.yaml diff
|
||||
Vendored
+2
-2
@@ -12,7 +12,7 @@
|
||||
"start:debug"
|
||||
],
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeVersion": "18.17",
|
||||
"runtimeVersion": "18.10.0",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
@@ -20,4 +20,4 @@
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+17
-39
@@ -1,43 +1,21 @@
|
||||
FROM node:18.17-alpine AS base_image
|
||||
|
||||
FROM base_image AS build_base
|
||||
# install all dependencies
|
||||
FROM node:18.10-alpine as packages
|
||||
WORKDIR /app
|
||||
RUN apk update
|
||||
# needed packages to build dependencies from source
|
||||
RUN apk add --no-cache aws-cli
|
||||
COPY package*.json ./
|
||||
|
||||
|
||||
# run aws cli without mounting secret, because CI already has AWS credentials
|
||||
FROM build_base AS ci_image
|
||||
RUN aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
RUN apk add --no-cache aws-cli \
|
||||
&& aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1 \
|
||||
&& npm install
|
||||
|
||||
# unit test specific build
|
||||
FROM ci_image AS test
|
||||
ENV DUC_URL=0.0.0.0:50051
|
||||
ENTRYPOINT ["npm", "run", "test"]
|
||||
|
||||
|
||||
# dev build
|
||||
FROM build_base AS dev
|
||||
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials \
|
||||
aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1
|
||||
# flag --build-from-source is required to force-build sqlite3
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
ENTRYPOINT npm run start:dev
|
||||
|
||||
|
||||
FROM ci_image AS prod_build
|
||||
RUN npm run build
|
||||
|
||||
|
||||
FROM base_image
|
||||
FROM node:18.10-alpine as test
|
||||
WORKDIR /app
|
||||
COPY --from=prod_build /app/dist ./dist
|
||||
COPY --from=prod_build /app/node_modules ./node_modules
|
||||
COPY --from=prod_build /app/package*.json ./
|
||||
ENTRYPOINT npm run start:prod
|
||||
COPY --from=packages /app/node_modules ./node_modules
|
||||
COPY . ./
|
||||
|
||||
FROM node:18.10-alpine
|
||||
WORKDIR /app
|
||||
COPY . /app/
|
||||
COPY --from=packages /app/node_modules ./node_modules
|
||||
RUN npm run build
|
||||
EXPOSE 3333
|
||||
ENTRYPOINT npm run start
|
||||
|
||||
@@ -2,57 +2,52 @@
|
||||
<image src="./assets/maestro.svg" style="width:10rem">
|
||||
</p>
|
||||
|
||||
|
||||
# Maestro
|
||||
|
||||
Maestro é a API principal da Dadosfera. É responsável pela comunicação do Frontend com nossos microsserviços.
|
||||
Maestro is the Dadosfera's gateway, it's responsible for the communication between the frontend application and Dadosfera's microservices.
|
||||
|
||||
```mermaid
|
||||
graph TD;
|
||||
Frontend<-->Maestro;
|
||||
Maestro<-->duc;
|
||||
Maestro<-->pi-factory;
|
||||
Maestro<-->in-factory;
|
||||
```
|
||||
## 🚀 Starting
|
||||
|
||||
É uma API REST, desenvolvida em NodeJs utilizando o Framework [NestJs](https://docs.nestjs.com/).
|
||||
These instructions will allow you to get a working copy of the project on your local machine for development and testing purposes.
|
||||
|
||||
## 🚀 Iniciando
|
||||
### 📋 Requirements
|
||||
|
||||
Estas instruções permitirão que você obtenha uma cópia funcional do projeto em sua máquina local para desenvolvimento e testes.
|
||||
- [NodeJS v18.10.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
|
||||
|
||||
### 📋 Requisitos
|
||||
### 🔧 Installation<a id="installation"></a>
|
||||
|
||||
- [NodeJS v18.17 / NPM v9.6.7](https://nodejs.org/pt-br/download/)
|
||||
- Clone the repository
|
||||
|
||||
> Dica: Utilize [NVM](https://github.com/nvm-sh/nvm) para gerenciar facilmente as versões do node
|
||||
- SSH
|
||||
|
||||
- Solicite acesso à conta de desenvolvimento do AWS Console para **todos os serviços necessários**
|
||||
- Crie sua Access Key no menu "Security credentials"
|
||||
- Defina a Access Key em sua máquina de desenvolvimento local
|
||||
- Solicite acesso às VPNs de stg e prd
|
||||
```
|
||||
git clone git@github.com:dadosfera/maestro.git
|
||||
```
|
||||
|
||||
### 🔧 Instalação<a id="installation"></a>
|
||||
or
|
||||
|
||||
- Clone o repositório
|
||||
- HTTPS
|
||||
```
|
||||
git clone https://github.com/dadosfera/maestro.git
|
||||
```
|
||||
|
||||
```sh
|
||||
git clone git@github.com:dadosfera/maestro.git
|
||||
```
|
||||
|
||||
- Selecione a versão correta do node (opcional, apenas se estiver usando o [NVM](https://github.com/nvm-sh/nvm)):
|
||||
- Select the correct node version (optional, only if using [NVM](https://github.com/nvm-sh/nvm)):
|
||||
|
||||
```sh
|
||||
nvm use
|
||||
```
|
||||
|
||||
- Instale as dependências do projeto:
|
||||
- Install the project dependencies:
|
||||
|
||||
```sh
|
||||
npm i
|
||||
```
|
||||
|
||||
- Configure as seguintes variáveis de ambiente:
|
||||
- Setup the following enviroment variables:
|
||||
|
||||
```
|
||||
ENV=
|
||||
@@ -63,41 +58,42 @@ Estas instruções permitirão que você obtenha uma cópia funcional do projeto
|
||||
SM_OAUTH_PATH=
|
||||
```
|
||||
|
||||
- Inicie o servidor:
|
||||
- Start the server:
|
||||
|
||||
```sh
|
||||
# dev mode
|
||||
npm run start:dev
|
||||
|
||||
# or in debug mode
|
||||
npm run start:debug
|
||||
```
|
||||
|
||||
> Se preferir, utilize o debbuger do VSCode apertando F5
|
||||
The service should start successfully.
|
||||
|
||||
O serviço deve iniciar com sucesso.
|
||||
## 📄 Documentation
|
||||
|
||||
## 📄 Documentação
|
||||
NestJs makes it easy to document each route using decorators on all requests and responses properties. It automatically generates a swagger for given information and provides a route to access it http://localhost:3333/api.
|
||||
For more info check the [official documentation](https://docs.nestjs.com/openapi/introduction).
|
||||
|
||||
O NestJs facilita a documentação de cada rota usando decoradores em todas as propriedades de solicitações e respostas. Ele gera automaticamente um swagger para as informações fornecidas e fornece uma rota para acessá-lo em http://localhost:3333/api. Para mais informações, consulte a [documentação oficial](https://docs.nestjs.com/openapi/introduction).
|
||||
### - Multiple documentations
|
||||
|
||||
### - Documentações múltiplas
|
||||
We are currently generating **2 different** documentations: Internal and External.
|
||||
|
||||
Atualmente, estamos gerando **2 documentações diferentes**: Interna e Externa.
|
||||
All routes that have the decorator `@ApiInternalOnly()` will not be visible on the **External** API swagger.
|
||||
|
||||
Todas as rotas que têm o decorador `@ApiInternalOnly()` não serão visíveis no swagger da API **Externa**.
|
||||
- When you start the application with `npm run start` it will serve and generate the swagger JSON of the **External** API
|
||||
- When you start the application with `npm run start:internal` it will serve and generate the swagger JSON of the **Internal** API
|
||||
- When you run `npm run docs` it will generate the swagger JSON of both **Internal** and **External** API, and save them to `docsfera.json` and `docsfera.external.json` respectively;
|
||||
|
||||
- Quando você inicia a aplicação com `npm run start:dev`, ela servirá e gerará o JSON do swagger da API **Interna**
|
||||
It is important to run `npm run docs` before every deploy so we can always have the most updated docs published.
|
||||
|
||||
- Quando você executa `npm run docs`, ele gerará o JSON do swagger de ambas as APIs **Interna** e **Externa** e os salvará em `docsfera.json` e `docsfera.external.json`, respectivamente;
|
||||
## Authentication decorators
|
||||
|
||||
> Link para documentação interna: https://dadosfera.github.io/docsfera
|
||||
|
||||
É importante executar `npm run docs` antes de cada implantação para que sempre tenhamos as documentações mais atualizadas publicadas.
|
||||
|
||||
## Decoradores de autenticação
|
||||
|
||||
O Maestro possui utilitários para facilitar a autenticação do usuário em todos os controladores e rotas. Os seguintes decoradores estão disponíveis:
|
||||
Maestro have utilities to ease the user authentication on every controller and route. The following decorators are available:
|
||||
|
||||
### `@Authenticated`
|
||||
|
||||
Se o usuário precisar estar autenticado para fazer uma solicitação, podemos usar o decorador `@Authenticated` no controlador ou rota, conforme necessário.
|
||||
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';
|
||||
@@ -109,7 +105,7 @@ class FooController {
|
||||
|
||||
@Get('bar')
|
||||
async getBar() {
|
||||
this.logger.info('usuário está autenticado!');
|
||||
this.logger.info('user is authenticated!');
|
||||
|
||||
return { authenticated: true };
|
||||
}
|
||||
@@ -126,14 +122,14 @@ class FooController {
|
||||
@Get('bar')
|
||||
@Authenticated()
|
||||
async getBar() {
|
||||
this.logger.info('usuário está autenticado!');
|
||||
this.logger.info('user is authenticated!');
|
||||
|
||||
return { authenticated: true };
|
||||
}
|
||||
|
||||
@Post('bar')
|
||||
async postBar() {
|
||||
this.logger.info('usuário NÃO está autenticado!');
|
||||
this.logger.info('user is NOT authenticated!');
|
||||
|
||||
return { authenticated: false };
|
||||
}
|
||||
@@ -142,7 +138,7 @@ class FooController {
|
||||
|
||||
### `@RequireAllPermissions`
|
||||
|
||||
Este decorador exige que **todas as permissões** listadas sejam concedidas ao usuário solicitante.
|
||||
This decorator requires that **all permissions** listed are granted to the requesting user.
|
||||
|
||||
```ts
|
||||
import { RequireAllPermissions } from '../../authentication/authentication.decorator';
|
||||
@@ -162,7 +158,7 @@ class FooController {
|
||||
|
||||
### `@RequireSomePermission`
|
||||
|
||||
No caso seguinte, é necessário que o usuário tenha **pelo menos uma** das permissões listadas.
|
||||
In the following case, the user is required to have **at least one** listed permission.
|
||||
|
||||
```ts
|
||||
import { RequireSomePermission } from '../../authentication/authentication.decorator';
|
||||
@@ -182,18 +178,18 @@ class FooController {
|
||||
|
||||
### `@AuthenticateCondition`
|
||||
|
||||
Se for necessária uma verificação de autenticação mais complicada, podemos usar o decorador `@AuthenticateCondition` para defini-la. A função personalizada deve retornar `true` para autenticar a solicitação.
|
||||
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.
|
||||
|
||||
No exemplo a seguir:
|
||||
In the following example:
|
||||
|
||||
- todas as rotas no controlador `FooController` só podem ser solicitadas a partir do localhost
|
||||
- `POST /foo/bar` só pode ser solicitado a partir do localhost **e** por usuários do cliente com id `111...eef`
|
||||
- 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')
|
||||
// permitir solicitações apenas do localhost
|
||||
// allow requests only from localhost
|
||||
@AuthenticateCondition((request: Request) => request.ip === '::ffff:127.0.0.1')
|
||||
class FooController {
|
||||
/* ... */
|
||||
@@ -204,7 +200,7 @@ class FooController {
|
||||
}
|
||||
|
||||
@Post('bar')
|
||||
// permitir solicitações apenas de um cliente específico
|
||||
// allow requests only from a specific customer
|
||||
@AuthenticateCondition(
|
||||
(request: Request, user: RequestUser) =>
|
||||
user.customer_id === '1113e943-2187-4fdd-9c2c-54338fedaeef',
|
||||
@@ -215,11 +211,11 @@ class FooController {
|
||||
}
|
||||
```
|
||||
|
||||
### Nota sobre decoradores de autenticação
|
||||
### Note on authentication decorators
|
||||
|
||||
- O método antigo de autenticação colocava os dados do usuário no campo `request.body.info`, o que impõe certos problemas em relação ao corpo da solicitação, pois esses dados devem vir do frontend sem qualquer modificação pelo Maestro. Agora, esse uso está ⚠️ **DESCONTINUADO** ⚠️. Estamos trabalhando para migrar para o decorador de parâmetro `@User`. O método antigo está funcionando enquanto a migração está em andamento.
|
||||
- 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.
|
||||
|
||||
- Os decoradores de autenticação podem ser usados juntos e todos eles **devem** passar para que a solicitação seja autenticada, mas, no caso geral, você não precisa (_e não gostaria de..._) usar todos eles juntos, pois você pode codificar toda a lógica de autenticação no decorador `@AuthenticateCondition`.
|
||||
- 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 {
|
||||
@@ -255,17 +251,17 @@ class FooController {
|
||||
}
|
||||
```
|
||||
|
||||
- Se `@RequireAllPermissions` e `@RequireSomePermission` forem usados com **apenas uma única permissão**, eles apresentam o **exatamente mesmo comportamento**.
|
||||
- If `@RequireAllPermissions` and `@RequireSomePermission` are used with **only a single permission**, they present the **exactly same behavior**.
|
||||
|
||||
```ts
|
||||
// mesmo comportamento
|
||||
// same behavior
|
||||
@RequireAllPermissions(Permissions.BAR.MANAGE)
|
||||
@RequireSomePermission(Permissions.BAR.MANAGE)
|
||||
```
|
||||
|
||||
## Decorador de parâmetro `@User`
|
||||
## `@User` parameter decorator
|
||||
|
||||
Os dados do usuário solicitante podem ser obtidos usando o decorador de parâmetro @User, como no exemplo a seguir:
|
||||
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';
|
||||
@@ -283,7 +279,7 @@ class FooController {
|
||||
}
|
||||
```
|
||||
|
||||
Se o usuário precisar estar logado, defina `required` como `true`, como por exemplo:
|
||||
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';
|
||||
@@ -309,46 +305,46 @@ class UserController {
|
||||
}
|
||||
```
|
||||
|
||||
## 📦 Desenvolvimento
|
||||
## 📦 Development
|
||||
|
||||
### ⌨️ Estilo de Codificação
|
||||
### ⌨️ Coding Style
|
||||
|
||||
Por padrão, usamos [ESLint](https://eslint.org/) + [Prettier](https://prettier.io/) com configurações padrão.
|
||||
By default, we use [ESLint](https://eslint.org/) + [Prettier](https://prettier.io/) with default settings.
|
||||
|
||||
**Recomendamos usar o Visual Studio Code e instalar as extensões recomendadas para facilitar o processo de desenvolvimento.**
|
||||
**We recommend using Visual Studio Code and installing the recommended extensions to ease the development process.**
|
||||
|
||||
### Padrão de commits
|
||||
### Commits pattern
|
||||
|
||||
Nosso pipeline de fluxo de trabalho segue as especificações de commits convencionais ([cheat sheets](https://cheatography.com/albelop/cheat-sheets/conventional-commits/)) para liberar versões conforme necessário.
|
||||
Our workflow pipeline follows the conventional commits specs ([cheat sheets](https://cheatography.com/albelop/cheat-sheets/conventional-commits/)) to release versions accordingly.
|
||||
|
||||
Formato: `<type>[optional scope]: <description>`
|
||||
Format: `<type>[optional scope]: <description>`
|
||||
|
||||
Exemplo: `FIX: ensure Range headers adhere more closely to RFC 2616`
|
||||
Example: `FIX: ensure Range headers adhere more closely to RFC 2616`
|
||||
|
||||
### Convenção de nomenclatura de branchs
|
||||
### Branching naming convention
|
||||
|
||||
- **Feature**: Quaisquer alterações de código para um novo módulo ou caso de uso devem ser feitas em uma branch de feature. Esta branch é criada com base na branch `main`. Quando todas as alterações estiverem concluídas, será necessário um Pull Request/Merge Request para colocar todas essas alterações de volta na branch `main`. Exemplos: `feature/integrate-swagger`, `feature/JIRA-1234`, `feature/JIRA-1234_support-dark-theme`.
|
||||
- **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`.
|
||||
|
||||
**Recomenda-se usar todas as letras em minúsculas e hífen (-) para separar palavras, a menos que seja um nome ou ID de item específico. O sublinhado (\_) pode ser usado para separar o ID e a descrição.**
|
||||
**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**: Se as alterações de código feitas na branch de feature foram rejeitadas após um lançamento, sprint ou demo, quaisquer correções necessárias após isso devem ser feitas na branch de correção de bug. Exemplos: `bugfix/more-gray-shades`, `bugfix/JIRA-1444_gray-on-blur-fix`.
|
||||
- **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**: Se houver necessidade de corrigir um bloqueador, fazer um patch temporário, aplicar uma mudança crítica de framework ou configuração que deva ser tratada imediatamente, ela deve ser criada como um Hotfix. Exemplos: `hotfix/disable-endpoint-zero-day-exploit`, `hotfix/increase-scaling-threshold`.
|
||||
- **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**: Uma branch para experimentar. Qualquer nova feature ou ideia que não faça parte de um lançamento ou sprint. Exemplo: `experimental/dark-theme-support`.
|
||||
- **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`.
|
||||
|
||||
### Fazendo um Pull Request
|
||||
### Making a Pull Request
|
||||
|
||||
1. Comite suas alterações
|
||||
2. Abra o Pull Request no GitHub
|
||||
3. Envie o link do Pull Request no grupo de chat do Google da Microsfera para revisão e possível aprovação
|
||||
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
|
||||
|
||||
## 🛠️ Construído com
|
||||
## 🛠️ Built with
|
||||
|
||||
Algumas tecnologias usadas neste projeto:
|
||||
Some technologies used in this project:
|
||||
|
||||
- [NestJS](https://docs.nestjs.com) - Framework para construir aplicações NodeJS eficientes e escaláveis no lado do servidor
|
||||
- [NestJS](https://docs.nestjs.com) - Framework for building efficient and scalable NodeJS server-side applications
|
||||
|
||||
## ⚙️ Arquitetura de Back-end
|
||||
## ⚙️ Back-end Architecture
|
||||
|
||||
A arquitetura pode ser encontrada neste [link](https://sites.google.com/dadosfera.ai/wikidoproduto/time/back-end).
|
||||
The architecture can be found at [this link](https://sites.google.com/dadosfera.ai/wikidoproduto/time/back-end).
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
maestro:
|
||||
build: .
|
||||
image: dadosfera/maestro_${ENV}:${IMAGE_TAG}
|
||||
image: dadosfera/maestro_${ENV}:${IMAGE_TAG}
|
||||
@@ -1,4 +1,5 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
maestro:
|
||||
build: .
|
||||
image: ${ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/microservices/maestro_prd:${IMAGE_TAG}
|
||||
image: ${ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/microservices/maestro_${ENV}:${IMAGE_TAG}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
maestro:
|
||||
image: dadosfera/maestro_${ENV}:${IMAGE_TAG}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
maestro:
|
||||
image: ${ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/microservices/maestro_${ENV}:${IMAGE_TAG}
|
||||
|
||||
+2
-231
@@ -133,32 +133,7 @@
|
||||
},
|
||||
"get": {
|
||||
"operationId": "ConnectionController_getAllConnections",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "search",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
@@ -515,45 +490,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/pipelinesV2/monitoring-dashboard": {
|
||||
"get": {
|
||||
"operationId": "PipelinesController_getMonitoringDashboard",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"PipelinesV2"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/pipelinesV2": {
|
||||
"post": {
|
||||
"operationId": "PipelinesController_create",
|
||||
@@ -1800,118 +1736,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/customers/{id}/links": {
|
||||
"get": {
|
||||
"operationId": "CustomersController_getCustomerLinks",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CustomerLinksResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Customers"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"operationId": "CustomersController_setCustomerLinks",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CustomerLinkRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Customers"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
},
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/customers/token": {
|
||||
"get": {
|
||||
"operationId": "CustomersController_getCustomerToken",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "exp",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Customers"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
},
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/health": {
|
||||
"get": {
|
||||
"operationId": "HealthController_check",
|
||||
@@ -1928,7 +1752,7 @@
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"title": "Maestro - feat/new-customer-monitoring",
|
||||
"title": "Maestro - feat/generate-token",
|
||||
"description": "This is the Maestro API",
|
||||
"version": "1.0.0",
|
||||
"contact": {}
|
||||
@@ -2209,9 +2033,6 @@
|
||||
"ConnectionToCatalogDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -3303,56 +3124,6 @@
|
||||
"updated_at",
|
||||
"updated_by"
|
||||
]
|
||||
},
|
||||
"CustomerLink": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"href": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"iconSrc": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"href",
|
||||
"name",
|
||||
"description"
|
||||
]
|
||||
},
|
||||
"CustomerLinksResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"links": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/CustomerLink"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"links"
|
||||
]
|
||||
},
|
||||
"CustomerLinkRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"links": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/CustomerLink"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"links"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+92
-631
@@ -542,6 +542,67 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/auth/oauth/magalu-id": {
|
||||
"get": {
|
||||
"operationId": "AuthController_magaluIdOauth",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Auth"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/auth/oauth/magalu-id/callback": {
|
||||
"get": {
|
||||
"operationId": "AuthController_magaluIdOauthCallback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Auth"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/connections": {
|
||||
"post": {
|
||||
"operationId": "ConnectionController_createConnection",
|
||||
@@ -582,32 +643,7 @@
|
||||
},
|
||||
"get": {
|
||||
"operationId": "ConnectionController_getAllConnections",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "search",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
@@ -2688,45 +2724,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/pipelinesV2/monitoring-dashboard": {
|
||||
"get": {
|
||||
"operationId": "PipelinesController_getMonitoringDashboard",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"PipelinesV2"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/pipelinesV2": {
|
||||
"post": {
|
||||
"operationId": "PipelinesController_create",
|
||||
@@ -4513,315 +4510,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/catalog/rls-rule": {
|
||||
"post": {
|
||||
"operationId": "CatalogController_addRlsRule",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Catalog"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
},
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"get": {
|
||||
"operationId": "CatalogController_getRlsRules",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Catalog"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
},
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"operationId": "CatalogController_batchRemoveRlsRules",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "nimbus_dashboard_id",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id_rls",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Catalog"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
},
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/catalog/rls-rule/{id}": {
|
||||
"get": {
|
||||
"operationId": "CatalogController_getOneRlsRule",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Catalog"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
},
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"operationId": "CatalogController_removeRlsRule",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Catalog"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
},
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/catalog/nimbus-dashboards": {
|
||||
"get": {
|
||||
"operationId": "CatalogController_getNimbusDashboards",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Catalog"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
},
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/catalog/register-dataset": {
|
||||
"post": {
|
||||
"operationId": "CatalogController_registerDatasetWithMetadataRequest",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Catalog"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
},
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/terms-of-use/token": {
|
||||
"get": {
|
||||
"operationId": "TermsOfUseController_getToken",
|
||||
@@ -5322,7 +5010,14 @@
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": ""
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
@@ -5335,16 +5030,7 @@
|
||||
"/customers/{id}/links": {
|
||||
"get": {
|
||||
"operationId": "CustomersController_getCustomerLinks",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
@@ -5368,29 +5054,27 @@
|
||||
},
|
||||
"put": {
|
||||
"operationId": "CustomersController_setCustomerLinks",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CustomerLinkRequest"
|
||||
"$ref": "#/components/schemas/CustomerDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
@@ -5423,7 +5107,7 @@
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -5444,147 +5128,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/customers/monitoring-dashboard": {
|
||||
"get": {
|
||||
"operationId": "CustomersController_getCustomerMonitoringDashboard",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Customers"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
},
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/open-data/sharing-ocean-data": {
|
||||
"post": {
|
||||
"operationId": "OpenDataController_createUser",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dadosfera-lang",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"pt-br",
|
||||
"en-us"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "language",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "origin",
|
||||
"required": true,
|
||||
"in": "header",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"OpenData"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/customers/{id}/theme": {
|
||||
"post": {
|
||||
"operationId": "ThemeController_saveCustomertheme",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CustomerThemeRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CustomerThemeResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Theme"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"access-token": []
|
||||
},
|
||||
{
|
||||
"access-token": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"get": {
|
||||
"operationId": "ThemeController_getCustomerTheme",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CustomerThemeResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Theme"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/health": {
|
||||
"get": {
|
||||
"operationId": "HealthController_check",
|
||||
@@ -5601,7 +5144,7 @@
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"title": "Maestro",
|
||||
"title": "Maestro - ci/dockerhub",
|
||||
"description": "This is the Maestro API",
|
||||
"version": "1.0.0",
|
||||
"contact": {}
|
||||
@@ -5651,9 +5194,6 @@
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"displayName": {
|
||||
"type": "string"
|
||||
},
|
||||
"tier": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -5665,20 +5205,15 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"themeEnabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"modules",
|
||||
"id",
|
||||
"name",
|
||||
"displayName",
|
||||
"tier",
|
||||
"scheduleLimit",
|
||||
"links",
|
||||
"themeEnabled"
|
||||
"links"
|
||||
]
|
||||
},
|
||||
"AuthUser": {
|
||||
@@ -5833,11 +5368,15 @@
|
||||
},
|
||||
"accessToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"refreshToken": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"permissions",
|
||||
"accessToken"
|
||||
"accessToken",
|
||||
"refreshToken"
|
||||
]
|
||||
},
|
||||
"CreateConnectionDto": {
|
||||
@@ -5885,9 +5424,6 @@
|
||||
"ConnectionToCatalogDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -8347,35 +7883,13 @@
|
||||
"tags"
|
||||
]
|
||||
},
|
||||
"CustomerLink": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"href": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"iconSrc": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"href",
|
||||
"name",
|
||||
"description"
|
||||
]
|
||||
},
|
||||
"CustomerLinksResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"links": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/CustomerLink"
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8383,72 +7897,19 @@
|
||||
"links"
|
||||
]
|
||||
},
|
||||
"CustomerLinkRequest": {
|
||||
"CustomerDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"links": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/CustomerLink"
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"links"
|
||||
]
|
||||
},
|
||||
"CustomerThemeRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"displayName": {
|
||||
"type": "string"
|
||||
},
|
||||
"backgroundColor": {
|
||||
"type": "string"
|
||||
},
|
||||
"textColor": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"displayName",
|
||||
"backgroundColor",
|
||||
"textColor"
|
||||
]
|
||||
},
|
||||
"CustomerTheme": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"backgroundColor": {
|
||||
"type": "string"
|
||||
},
|
||||
"textColor": {
|
||||
"type": "string"
|
||||
},
|
||||
"logo": {
|
||||
"type": "string"
|
||||
},
|
||||
"logoLogin": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"backgroundColor",
|
||||
"textColor",
|
||||
"logo",
|
||||
"logoLogin"
|
||||
]
|
||||
},
|
||||
"CustomerThemeResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"theme": {
|
||||
"$ref": "#/components/schemas/CustomerTheme"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"theme"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-2
@@ -4,6 +4,7 @@ declare global {
|
||||
NODE_ENV: 'test';
|
||||
ENV: 'local' | 'stg' | 'prd' | 'test';
|
||||
LOCAL_ENV: 'stg' | 'prd';
|
||||
CLOUD_ENVIRONMENT: 'aws' | 'gcp' | 'mgc';
|
||||
|
||||
DUC_URL: string;
|
||||
INFACTORY_URL: string;
|
||||
@@ -12,8 +13,6 @@ declare global {
|
||||
INTERNAL_SWAGGER: 'true' | 'false';
|
||||
|
||||
AWS_REGION: string;
|
||||
OPEN_GROUP_ID: string;
|
||||
OPEN_CUSTOMER_ID: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
charts:
|
||||
- name: maestro
|
||||
chart: ../maestro
|
||||
values:
|
||||
- ../maestro/values.yaml
|
||||
set:
|
||||
- name: maestro.duc_url
|
||||
value: duc.dadosfera.ai
|
||||
- name: hostname
|
||||
value: maestro.dadosfera.ai
|
||||
- name: maestro.pi_factory_url
|
||||
value: pi-factory.dadosfera.ai
|
||||
- name: maestro.in_factory_url
|
||||
value: in-factory.dadosfera.ai
|
||||
- name: maestro.tr_factory_url
|
||||
value: in-factory.dadosfera.ai
|
||||
- name: maestro.open_customer_id
|
||||
value: f239718a-a271-4ef9-ae7e-02a2f0f3aa6e
|
||||
- name: maestro.open_group_id
|
||||
value: 401573bb-334f-44b2-b30e-88d4cea31ae9
|
||||
- name: replicaCount
|
||||
value: 2
|
||||
@@ -1,22 +0,0 @@
|
||||
charts:
|
||||
- name: maestro
|
||||
chart: ../maestro
|
||||
values:
|
||||
- ../maestro/values.yaml
|
||||
set:
|
||||
- name: maestro.duc_url
|
||||
value: duc.stg.dadosfera.ai
|
||||
- name: hostname
|
||||
value: maestro.stg.dadosfera.ai
|
||||
- name: maestro.pi_factory_url
|
||||
value: pi-factory.stg.dadosfera.ai
|
||||
- name: maestro.in_factory_url
|
||||
value: in-factory.stg.dadosfera.ai
|
||||
- name: maestro.tr_factory_url
|
||||
value: in-factory.stg.dadosfera.ai
|
||||
- name: maestro.open_customer_id
|
||||
value: b3e3dfe5-b992-4586-a73c-c0b0c00f615d
|
||||
- name: maestro.open_group_id
|
||||
value: e3f98a2f-7748-4981-8505-7695c8ca8218
|
||||
- name: replicaCount
|
||||
value: 1
|
||||
@@ -1,23 +0,0 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
@@ -1,24 +0,0 @@
|
||||
apiVersion: v2
|
||||
name: maestro
|
||||
description: A Helm chart for Kubernetes
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "1.16.0"
|
||||
@@ -1,122 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: maestro
|
||||
namespace: applications
|
||||
labels:
|
||||
app: maestro
|
||||
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: maestro
|
||||
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: maestro
|
||||
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: {{ .Values.imagePullSecrets }}
|
||||
nodeSelector:
|
||||
"beta.kubernetes.io/os": linux
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: application
|
||||
operator: In
|
||||
values:
|
||||
- general
|
||||
|
||||
tolerations:
|
||||
- key: "kubernetes.azure.com/scalesetpriority"
|
||||
operator: "Equal"
|
||||
value: "spot"
|
||||
effect: "NoSchedule"
|
||||
|
||||
containers:
|
||||
- name: maestro
|
||||
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.containerPort }}
|
||||
resources:
|
||||
requests:
|
||||
cpu: {{ .Values.resources.requests.cpu }}
|
||||
memory: {{ .Values.resources.requests.memory }}
|
||||
limits:
|
||||
cpu: {{ .Values.resources.limits.cpu }}
|
||||
memory: {{ .Values.resources.limits.memory }}
|
||||
env:
|
||||
- name: AWS_IDENTITY_POOL_ID
|
||||
value: {{ .Values.maestro.aws_identity_pool_id }}
|
||||
- name: AWS_REGION
|
||||
value: "us-east-1"
|
||||
- name: BASE_HOST
|
||||
value: "maestro_prd"
|
||||
- name: BUCKET_CUSTOMER_CSV_ASSETS
|
||||
value: {{ .Values.maestro.bucket_customer_csv_assets }}
|
||||
- name: CONNECTORS_INDEX
|
||||
value: "connectors"
|
||||
- name: DUC_URL
|
||||
value: {{ .Values.maestro.duc_url }}
|
||||
- name: ELASTIC_APM_ENVIRONMENT
|
||||
value: {{ .Values.maestro.env }}
|
||||
- name: ELASTIC_APM_SERVER_URL
|
||||
value: "https://apm-server.dadosfera.ai"
|
||||
- name: ELASTIC_APM_SERVICE_NAME
|
||||
value: {{ .Values.maestro.apm_service }}
|
||||
- name: ENV
|
||||
value: {{ .Values.maestro.env }}
|
||||
- name: INFACTORY_URL
|
||||
value: {{ .Values.maestro.in_factory_url }}
|
||||
- name: LOGGER_GELF_HOST
|
||||
value: "logstash-pipelines.dadosfera.ai"
|
||||
- name: LOGGER_GELF_PORT
|
||||
value: "{{ .Values.maestro.logger_gelf_port }}"
|
||||
- name: NIMBUS_BASE_URL
|
||||
value: "http://nimbus-api"
|
||||
- name: NPM_TOKEN
|
||||
value: {{ .Values.maestro.npm_token }}
|
||||
- name: PB_TOKEN_PATH
|
||||
value: {{ .Values.maestro.pb_token_path }}
|
||||
- name: PIFACTORY_URL
|
||||
value: {{ .Values.maestro.pi_factory_url }}
|
||||
- name: SM_OAUTH_PATH
|
||||
value: {{ .Values.maestro.sm_oauth_path }}
|
||||
- name: TRFACTORY_URL
|
||||
value: {{ .Values.maestro.tr_factory_url }}
|
||||
- name: UPLOAD_FILE_AGENT_CONNECTION
|
||||
value: {{ .Values.maestro.upload_file_agent_connection }}
|
||||
- name: OPEN_CUSTOMER_ID
|
||||
value: {{ .Values.maestro.open_customer_id }}
|
||||
- name: OPEN_GROUP_ID
|
||||
value: {{ .Values.maestro.open_group_id }}
|
||||
- name: JWT_PRIVATE_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: prd-duc
|
||||
key: jwt_token
|
||||
- name: AWS_ACCESS_KEY_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: prd-maestro
|
||||
key: AWS_ACCESS_KEY_ID
|
||||
- name: AWS_SECRET_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: prd-maestro
|
||||
key: AWS_SECRET_ACCESS_KEY
|
||||
- name: AWS_DEFAULT_REGION
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: prd-maestro
|
||||
key: AWS_DEFAULT_REGION
|
||||
@@ -1,28 +0,0 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/whitelist-source-range: "69.49.241.121/32" # hostgator ip
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||
nginx.ingress.kubernetes.io/server-snippet: |
|
||||
underscores_in_headers on;
|
||||
ignore_invalid_headers on;
|
||||
|
||||
generation: 1
|
||||
labels:
|
||||
app: maestro
|
||||
name: open-data
|
||||
namespace: applications
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: {{ .Values.hostname }}
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: maestro
|
||||
port:
|
||||
number: {{ .Values.ingress.port }}
|
||||
path: /open-data/sharing-ocean-data
|
||||
pathType: Prefix
|
||||
@@ -1,27 +0,0 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||
nginx.ingress.kubernetes.io/server-snippet: |
|
||||
underscores_in_headers on;
|
||||
ignore_invalid_headers on;
|
||||
|
||||
generation: 1
|
||||
labels:
|
||||
app: maestro
|
||||
name: maestro
|
||||
namespace: applications
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: {{ .Values.hostname }}
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: maestro
|
||||
port:
|
||||
number: {{ .Values.ingress.port }}
|
||||
path: /
|
||||
pathType: Prefix
|
||||
@@ -1,40 +0,0 @@
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: prd-maestro
|
||||
namespace: applications
|
||||
labels:
|
||||
app: maestro
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: secretsmanager-prd
|
||||
kind: SecretStore
|
||||
target:
|
||||
name: prd-maestro
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: AWS_ACCESS_KEY_ID
|
||||
remoteRef:
|
||||
key: prd/microservices/aws_credentials/maestro
|
||||
version: "AWSCURRENT"
|
||||
property: AWS_ACCESS_KEY_ID
|
||||
|
||||
- secretKey: AWS_SECRET_ACCESS_KEY
|
||||
remoteRef:
|
||||
key: prd/microservices/aws_credentials/maestro
|
||||
version: "AWSCURRENT"
|
||||
property: AWS_SECRET_ACCESS_KEY
|
||||
|
||||
- secretKey: AWS_DEFAULT_REGION
|
||||
remoteRef:
|
||||
key: prd/microservices/aws_credentials/maestro
|
||||
version: "AWSCURRENT"
|
||||
property: AWS_DEFAULT_REGION
|
||||
|
||||
- secretKey: jwt_token
|
||||
remoteRef:
|
||||
key: {{ .Values.maestro.jwt_token_secret_id }}
|
||||
version: "AWSCURRENT"
|
||||
property: token
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: maestro
|
||||
namespace: applications
|
||||
labels:
|
||||
app: maestro
|
||||
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: maestro
|
||||
protocol: TCP
|
||||
port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.targetPort }}
|
||||
selector:
|
||||
app: maestro
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
# Default values for metabase.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
replicaCount: 3
|
||||
hostname: maestro.dadosfera.ai
|
||||
image:
|
||||
repository: 611330257153.dkr.ecr.us-east-1.amazonaws.com/microservices/maestro_prd
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: 1.56.0
|
||||
containerPort: 3333
|
||||
imagePullSecrets: "applications-secrets-ecr-auth-token-external-secret"
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 3333
|
||||
targetPort: 3333
|
||||
ingress:
|
||||
enabled: false
|
||||
port: 3333
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 1500Mi
|
||||
limits:
|
||||
cpu: 2000m
|
||||
memory: 2Gi
|
||||
maestro:
|
||||
aws_identity_pool_id: "us-east-1_Mrezsw9Sn"
|
||||
duc_url: duc.dadosfera.ai
|
||||
in_factory_url: in-factory.dadosfera.ai
|
||||
bucket_customer_csv_assets: "customers-csv-assets-prd-611330257153"
|
||||
env: prd
|
||||
apm_service: maestro
|
||||
jwt_token_secret_id: prd/root/jwt_token
|
||||
logger_gelf_port: "1026"
|
||||
npm_token: npm_Xp17h3daMkORcZ55NY95Ez4gRfdzsQ3UvINP
|
||||
pb_token_path: prd/root/productboard_token
|
||||
pi_factory_url: pi-factory.dadosfera.ai
|
||||
sm_oauth_path: prd/root/oauth_applications
|
||||
tr_factory_url: in-factory.dadosfera.ai
|
||||
upload_file_agent_connection: cbc2f881-58c4-4d60-8003-0979b0b5b911
|
||||
open_customer_id: f239718a-a271-4ef9-ae7e-02a2f0f3aa6e
|
||||
open_group_id: 401573bb-334f-44b2-b30e-88d4cea31ae9
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 100
|
||||
targetCPUUtilizationPercentage: 80
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
Generated
+11557
-3902
File diff suppressed because it is too large
Load Diff
+39
-39
@@ -6,10 +6,10 @@
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"engines": {
|
||||
"node": "18.17"
|
||||
"node": "18.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"co:login": "aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1",
|
||||
"preinstall": "aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1",
|
||||
"proto-update": "npm i @dadosfera/protospack-v2@latest --save-exact",
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "nest build",
|
||||
@@ -20,38 +20,37 @@
|
||||
"start:prod": "node dist/main",
|
||||
"docs": "export KILL_AFTER_START=1 && nest start && export INTERNAL_SWAGGER=true && nest start",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest --detectOpenHandles --forceExit",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.414.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.112.0",
|
||||
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
|
||||
"@dadosfera/protospack": "2.5.3",
|
||||
"@dadosfera/protospack-v2": "^3.37.0-beta.10 ",
|
||||
"@grpc/grpc-js": "^1.9.3",
|
||||
"@grpc/proto-loader": "^0.7.9",
|
||||
"@nestjs/cli": "^9.5.0",
|
||||
"@nestjs/common": "^9.4.3",
|
||||
"@nestjs/config": "^2.3.4",
|
||||
"@nestjs/core": "^9.4.3",
|
||||
"@dadosfera/protospack-v2": "3.32.0-beta.2",
|
||||
"@grpc/grpc-js": "^1.6.7",
|
||||
"@grpc/proto-loader": "^0.6.13",
|
||||
"@nestjs/cli": "^9.4.2",
|
||||
"@nestjs/common": "^9.4.0",
|
||||
"@nestjs/config": "^2.3.1",
|
||||
"@nestjs/core": "^9.4.0",
|
||||
"@nestjs/mapped-types": "^1.2.2",
|
||||
"@nestjs/microservices": "^9.4.3",
|
||||
"@nestjs/microservices": "^9.4.0",
|
||||
"@nestjs/passport": "^9.0.3",
|
||||
"@nestjs/platform-express": "^9.4.3",
|
||||
"@nestjs/schematics": "^9.2.0",
|
||||
"@nestjs/platform-express": "^9.4.0",
|
||||
"@nestjs/schematics": "^9.1.0",
|
||||
"@nestjs/swagger": "^6.3.0",
|
||||
"@nestjs/testing": "^9.4.3",
|
||||
"axios": "^0.27.2",
|
||||
"@nestjs/testing": "^9.4.0",
|
||||
"axios": "^1.6.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"cron-parser": "^4.9.0",
|
||||
"cron-parser": "^4.4.0",
|
||||
"dotenv": "^14.3.2",
|
||||
"elastic-apm-node": "^3.50.0",
|
||||
"helmet": "^5.1.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"elastic-apm-node": "^3.36.0",
|
||||
"helmet": "^5.1.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"jwk-to-pem": "^2.0.5",
|
||||
"mixpanel": "^0.17.0",
|
||||
"ms": "^3.0.0-canary.1",
|
||||
@@ -61,39 +60,40 @@
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-hubspot-oauth2": "^1.0.3",
|
||||
"passport-mailchimp": "^1.1.0",
|
||||
"protospack": "2.5.2",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.5.5",
|
||||
"swagger-ui-express": "^4.6.3"
|
||||
"swagger-ui-express": "^4.4.0"
|
||||
},
|
||||
"overrides": {
|
||||
"multer": "1.4.5-lts.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/jsonwebtoken": "^8.5.9",
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/jwk-to-pem": "^2.0.1",
|
||||
"@types/multer": "^1.4.12",
|
||||
"@types/node": "^16.18.52",
|
||||
"@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.12",
|
||||
"@types/passport-oauth2": "^1.4.11",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.35.0",
|
||||
"@typescript-eslint/parser": "^5.35.0",
|
||||
"eslint": "^8.18.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.5.1",
|
||||
"nock": "^13.3.3",
|
||||
"prettier": "^2.8.8",
|
||||
"nock": "^13.2.7",
|
||||
"prettier": "^2.7.1",
|
||||
"source-map-support": "^0.5.20",
|
||||
"supertest": "^6.3.3",
|
||||
"supertest": "^6.2.3",
|
||||
"ts-jest": "^27.1.5",
|
||||
"ts-loader": "^9.4.4",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^3.14.2",
|
||||
"typescript": "^4.9.5"
|
||||
"ts-loader": "^9.3.1",
|
||||
"ts-node": "^10.8.1",
|
||||
"tsconfig-paths": "^3.14.1",
|
||||
"typescript": "^4.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-5
@@ -26,10 +26,9 @@ import { PipelinesV2Module } from './modules/pipelinesV2/pipelines.module';
|
||||
import { ProductboardModule } from './modules/productboard/productboard.module';
|
||||
import { MixpanelModule } from './modules/mixpanel/mixpanel.module';
|
||||
import { CustomersModule } from './modules/customers/customers.module';
|
||||
import { OpenDataModule } from './modules/open-data/open-data.module';
|
||||
import { ThemeModule } from './modules/theme/theme.module';
|
||||
|
||||
@Module({
|
||||
controllers: [],
|
||||
providers: [
|
||||
DadosferaLogger,
|
||||
{
|
||||
@@ -59,10 +58,8 @@ import { ThemeModule } from './modules/theme/theme.module';
|
||||
ProductboardModule,
|
||||
MixpanelModule,
|
||||
CustomersModule,
|
||||
OpenDataModule,
|
||||
ThemeModule,
|
||||
//Always leave HealthModule last, so it is on the bottom of swagger
|
||||
HealthModule
|
||||
HealthModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -79,9 +79,13 @@ export class AuthenticationGuard
|
||||
request,
|
||||
mustBeAuthenticated: boolean,
|
||||
): RequestUser | false {
|
||||
const accessToken = request.get('Authorization');
|
||||
const accessToken: string = request.get('Authorization');
|
||||
let accessTokenPayload: RequestUser;
|
||||
|
||||
this.logger.info(`mustBeAuthenticated: ${mustBeAuthenticated}`);
|
||||
this.logger.info(
|
||||
`accessToken (first 5 chars): ${accessToken?.slice(0, 5)}`,
|
||||
);
|
||||
// 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 {
|
||||
|
||||
@@ -67,6 +67,7 @@ export const PERMISSIONS_GROUPS = {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
PIPELINE: {
|
||||
title: {
|
||||
'pt-br': 'Coletar | Pipelines',
|
||||
@@ -116,6 +117,7 @@ export const PERMISSIONS_GROUPS = {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
CONNECTION: {
|
||||
title: {
|
||||
'pt-br': 'Coletar | Fontes de dados',
|
||||
@@ -155,6 +157,7 @@ export const PERMISSIONS_GROUPS = {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
NETWORK_CONFIG: {
|
||||
title: {
|
||||
'pt-br': 'Coletar | Redes',
|
||||
@@ -184,6 +187,7 @@ export const PERMISSIONS_GROUPS = {
|
||||
// },
|
||||
},
|
||||
},
|
||||
|
||||
OUTPUT: {
|
||||
title: {
|
||||
'pt-br': 'Output',
|
||||
@@ -233,6 +237,7 @@ export const PERMISSIONS_GROUPS = {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
TRANSFORMATIONS: {
|
||||
title: {
|
||||
'pt-br': 'Micro-transformações',
|
||||
@@ -282,6 +287,7 @@ export const PERMISSIONS_GROUPS = {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
CATALOG: {
|
||||
title: {
|
||||
'pt-br': 'Explorar | Catálogo',
|
||||
@@ -362,6 +368,7 @@ export const PERMISSIONS_GROUPS = {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
CONNECTORS: {
|
||||
title: {
|
||||
'pt-br': 'Conectores',
|
||||
@@ -411,6 +418,7 @@ export const PERMISSIONS_GROUPS = {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
SNOWFLAKE: {
|
||||
title: {
|
||||
'pt-br': 'Explorar | Consolidar',
|
||||
@@ -430,6 +438,7 @@ export const PERMISSIONS_GROUPS = {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
ZENDESK: {
|
||||
title: {
|
||||
'pt-br': 'Zendesk',
|
||||
@@ -449,6 +458,7 @@ export const PERMISSIONS_GROUPS = {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
DATAVIZ: {
|
||||
title: {
|
||||
'pt-br': 'Analisar | Visualização',
|
||||
@@ -487,6 +497,7 @@ export const PERMISSIONS_GROUPS = {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
MODULES: {
|
||||
title: {
|
||||
'pt-br': 'Módulos da Dadosfera',
|
||||
@@ -583,25 +594,6 @@ export const PERMISSIONS_GROUPS = {
|
||||
},
|
||||
},
|
||||
},
|
||||
CUSTOMER: {
|
||||
title: {
|
||||
'pt-br': 'Organização',
|
||||
'en-us': 'Organization',
|
||||
'es-es': 'Organización',
|
||||
},
|
||||
permissions: {
|
||||
MONITORING_DASHBOARD: {
|
||||
seqid: 47,
|
||||
claim: 'customer:monitoring-dashboard',
|
||||
usage: PermissionUsages.PUBLIC,
|
||||
name: {
|
||||
'pt-br': 'Ver o dashboard de monitoramento',
|
||||
'en-us': 'View the monitoring dashboard',
|
||||
'es-es': 'Ver el dashboard de monitoreo',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
export interface DadosferaModule {
|
||||
name: string;
|
||||
|
||||
@@ -52,6 +52,7 @@ import jwt from 'jsonwebtoken';
|
||||
import { LanguageEnum } from 'src/utils/languages.enum';
|
||||
import { Language } from 'src/decorators/language.decorator';
|
||||
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
|
||||
import { getFrontendUrl } from 'src/utils/getFrontendBaseUrl';
|
||||
|
||||
@ApiTags('Auth')
|
||||
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
|
||||
@@ -59,7 +60,7 @@ import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
logger: DadosferaLogger;
|
||||
redirectUrl: string;
|
||||
frontendRedirectUrl: string;
|
||||
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
@@ -67,17 +68,7 @@ export class AuthController {
|
||||
private authClient: AuthClientService,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
|
||||
switch (process.env.ENV) {
|
||||
case 'stg':
|
||||
this.redirectUrl = `https://app.${process.env.ENV}.dadosfera.ai/auth/login`;
|
||||
break;
|
||||
case 'prd':
|
||||
this.redirectUrl = `https://app.dadosfera.ai/auth/login`;
|
||||
break;
|
||||
default:
|
||||
this.redirectUrl = `http://localhost:4200/auth/login`;
|
||||
}
|
||||
this.frontendRedirectUrl = getFrontendUrl('/auth/login').href;
|
||||
}
|
||||
|
||||
@Post('sign-in')
|
||||
@@ -105,7 +96,7 @@ export class AuthController {
|
||||
customer_name,
|
||||
language,
|
||||
});
|
||||
|
||||
this.logger.info(`metadata: ${metadata}`);
|
||||
return this.authClient.refreshAccessToken({ refreshToken }, metadata);
|
||||
}
|
||||
|
||||
@@ -269,7 +260,13 @@ export class AuthController {
|
||||
@UseGuards(AuthGuard('google-login'))
|
||||
@Redirect()
|
||||
async googleOauthCallback(@Req() req) {
|
||||
const { url, email, token, language = 'pt-br' } = await this.callback(req);
|
||||
const {
|
||||
url,
|
||||
email,
|
||||
token,
|
||||
refreshToken,
|
||||
language = 'pt-br',
|
||||
} = await this.callback(req);
|
||||
if (url.searchParams.get('error')) {
|
||||
this.logger.error('/oauth/google - ERROR');
|
||||
return { url: url.href };
|
||||
@@ -279,6 +276,7 @@ export class AuthController {
|
||||
.oauthSignIn({
|
||||
username: email,
|
||||
token,
|
||||
refreshToken,
|
||||
})
|
||||
.then(({ session }) => {
|
||||
this.logger.info('/oauth/google - SUCESS');
|
||||
@@ -308,18 +306,79 @@ export class AuthController {
|
||||
return { url: url.href };
|
||||
}
|
||||
|
||||
async callback(req: Request) {
|
||||
@ApiInternalOnlyEndpoint()
|
||||
@Get('oauth/magalu-id')
|
||||
@UseGuards(AuthGuard('magalu-id-login'))
|
||||
magaluIdOauth() {
|
||||
this.logger.info('/oauth/magalu-id');
|
||||
return true;
|
||||
}
|
||||
|
||||
@ApiInternalOnlyEndpoint()
|
||||
@Get('oauth/magalu-id/callback')
|
||||
@UseGuards(AuthGuard('magalu-id-login'))
|
||||
@Redirect()
|
||||
async magaluIdOauthCallback(@Req() req) {
|
||||
const {
|
||||
url,
|
||||
email,
|
||||
token,
|
||||
refreshToken,
|
||||
language = 'pt-br',
|
||||
} = await this.callback(req, 'magalu-id');
|
||||
if (url.searchParams.get('error')) {
|
||||
this.logger.error('/oauth/magalu-id/callback - ERROR');
|
||||
this.logger.error(url.searchParams.get('error'));
|
||||
return { url: url.href };
|
||||
}
|
||||
|
||||
await this.authClient
|
||||
.oauthSignIn({
|
||||
username: email,
|
||||
token,
|
||||
refreshToken,
|
||||
})
|
||||
.then(({ session }) => {
|
||||
this.logger.info('/oauth/magalu-id/callback - SUCESS');
|
||||
url.searchParams.set('session', session);
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error('/oauth/magalu-id/callback - LOGIN ERROR');
|
||||
let error = OauthErrors.INVALID_CREDENTIALS[language].error;
|
||||
let error_description =
|
||||
OauthErrors.INVALID_CREDENTIALS[language].error_description;
|
||||
switch (err.details) {
|
||||
case ErrorCodes.USER.NOT_FOUND:
|
||||
error = OauthErrors.USER_NOT_FOUND[language].error;
|
||||
error_description =
|
||||
OauthErrors.USER_NOT_FOUND[language].error_description(email);
|
||||
break;
|
||||
case ErrorCodes.AUTH.UNAUTHORIZED:
|
||||
error = OauthErrors.INVALID_SESSION[language].error;
|
||||
error_description =
|
||||
OauthErrors.INVALID_SESSION[language].error_description;
|
||||
break;
|
||||
}
|
||||
url.searchParams.set('error', error);
|
||||
url.searchParams.set('error_description', error_description);
|
||||
return null;
|
||||
});
|
||||
return { url: url.href };
|
||||
}
|
||||
|
||||
async callback(req: Request, oauth_type?: 'google' | 'magalu-id') {
|
||||
const { error, state } = req.query;
|
||||
const { authInfo } = req;
|
||||
const url = new URL(this.redirectUrl);
|
||||
let email, token, error_title, error_description;
|
||||
let language: 'pt-br' | 'en-us' = 'pt-br';
|
||||
|
||||
const stateObject = jwt.verify(
|
||||
state as string,
|
||||
process.env.JWT_PRIVATE_KEY,
|
||||
);
|
||||
if (typeof stateObject != 'string') language = stateObject.language;
|
||||
const url = new URL(this.frontendRedirectUrl);
|
||||
let email, token, refreshToken, error_title, error_description;
|
||||
const language: 'pt-br' | 'en-us' = 'pt-br';
|
||||
// let stateObject;
|
||||
// try {
|
||||
// stateObject = jwt.verify(state as string, process.env.JWT_PRIVATE_KEY);
|
||||
// } catch (error) {
|
||||
// console.log('error', error);
|
||||
// }
|
||||
// if (typeof stateObject != 'string') language = stateObject.language;
|
||||
|
||||
if (error || !authInfo) {
|
||||
this.logger.error(error);
|
||||
@@ -330,11 +389,16 @@ export class AuthController {
|
||||
OauthErrors.INVALID_CREDENTIALS[language].error_description;
|
||||
if (error) error_description += ` - [${error}]`;
|
||||
} else {
|
||||
const { accessToken } = authInfo as any;
|
||||
const { _json: userInfo } = req.user as any;
|
||||
|
||||
const { accessToken, refreshToken: rt } = authInfo as any;
|
||||
const { _json: userInfo = {} } = req.user as any;
|
||||
email = userInfo.email;
|
||||
token = accessToken;
|
||||
refreshToken = rt;
|
||||
|
||||
if (oauth_type === 'magalu-id') {
|
||||
const jwtDecoded = jwt.decode(token, { json: true });
|
||||
email = jwtDecoded.email;
|
||||
}
|
||||
}
|
||||
|
||||
if (error_title) {
|
||||
@@ -342,6 +406,6 @@ export class AuthController {
|
||||
url.searchParams.set('error_description', error_description);
|
||||
}
|
||||
|
||||
return { token, email, url, language };
|
||||
return { token, refreshToken, email, url, language };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { AuthClientService } from './auth.service';
|
||||
import { DucClient } from '../duc/client.config';
|
||||
import { GoogleLoginStrategy } from './passport-strategies/google-strategy';
|
||||
import { getOauthSecrets } from 'src/utils/OauthSecrets';
|
||||
import { MagaluIdStrategy } from './passport-strategies/magalu-id-strategy';
|
||||
const client = new DucClient();
|
||||
|
||||
@Module({
|
||||
@@ -17,6 +18,7 @@ const client = new DucClient();
|
||||
AuthClientService,
|
||||
DadosferaLogger,
|
||||
GoogleLoginStrategy,
|
||||
MagaluIdStrategy,
|
||||
{ provide: 'OAUTH_SECRETS', useValue: getOauthSecrets() },
|
||||
],
|
||||
exports: [AuthClientService],
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
AuthResetPasswordRequest,
|
||||
AuthVerifyResetPasswordCodeRequest,
|
||||
AuthConfirmResetPasswordRequest,
|
||||
AuthOauthSignInRequest,
|
||||
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
|
||||
import { DucClient } from '../duc/client.config';
|
||||
import { Metadata } from '@grpc/grpc-js';
|
||||
@@ -162,10 +163,7 @@ export class AuthClientService implements OnModuleInit {
|
||||
return lastValueFrom(this.authService.AuthGetSession({ session }));
|
||||
}
|
||||
|
||||
async oauthSignIn(data: { username: string; token: string }) {
|
||||
const { username, token } = data;
|
||||
return lastValueFrom(
|
||||
this.authService.AuthOauthSignIn({ token, username, refreshToken: '' }),
|
||||
);
|
||||
async oauthSignIn(data: AuthOauthSignInRequest) {
|
||||
return lastValueFrom(this.authService.AuthOauthSignIn(data));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,15 +72,11 @@ export class AuthCustomer {
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
@ApiProperty()
|
||||
displayName: string;
|
||||
@ApiProperty()
|
||||
tier: string;
|
||||
@ApiProperty()
|
||||
scheduleLimit: string;
|
||||
@ApiProperty()
|
||||
links: Link[];
|
||||
@ApiProperty()
|
||||
themeEnabled: boolean;
|
||||
}
|
||||
|
||||
export class AuthSignInReq implements AuthSignInRequest {
|
||||
@@ -121,4 +117,6 @@ export class AuthRefreshAccessTokenRes {
|
||||
permissions: string[];
|
||||
@ApiProperty()
|
||||
accessToken: string;
|
||||
@ApiProperty()
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import {
|
||||
Strategy as Oauth2Strategy,
|
||||
StrategyOptions,
|
||||
VerifyCallback,
|
||||
VerifyFunction,
|
||||
} from 'passport-oauth2';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { OauthSecrets } from 'src/utils/OauthSecrets';
|
||||
import { Request } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
@Injectable()
|
||||
export class MagaluIdStrategy extends PassportStrategy(
|
||||
Oauth2Strategy,
|
||||
'magalu-id-login',
|
||||
) {
|
||||
redirect_uri: string;
|
||||
constructor() {
|
||||
const options: StrategyOptions = {
|
||||
clientID: process.env.MAGALU_ID_CLIENT_ID,
|
||||
clientSecret: process.env.MAGALU_ID_CLIENT_SECRET,
|
||||
callbackURL: process.env.MAGALU_ID_CALLBACK_URL,
|
||||
scope: ['openid'],
|
||||
authorizationURL: 'https://id.magalu.com/login',
|
||||
tokenURL: 'https://id.magalu.com/oauth/token',
|
||||
};
|
||||
const verify: VerifyFunction = (
|
||||
accessToken: string,
|
||||
refreshToken: string,
|
||||
profile: any,
|
||||
verified: VerifyCallback,
|
||||
) => {
|
||||
return verified(null, profile, { accessToken, refreshToken });
|
||||
};
|
||||
|
||||
super(options, verify);
|
||||
}
|
||||
|
||||
// authenticate(req: Request, options: Record<string, any>) {
|
||||
// const language =
|
||||
// req.headers['dadosfera-lang'] || req.query.language || 'pt-br';
|
||||
// // options.state = jwt.sign({ language }, process.env.JWT_PRIVATE_KEY);
|
||||
// super.authenticate(req, options);
|
||||
// console.log('authenticate', JSON.stringify(options));
|
||||
// }
|
||||
}
|
||||
@@ -5,11 +5,8 @@ import {
|
||||
} from '@nestjs/microservices';
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
import { Catalog } from '@dadosfera/protospack-v2';
|
||||
import { PlatformInterfaces } from '@dadosfera/protospack-v2';
|
||||
|
||||
const isLocalConnection =
|
||||
process.env.PIFACTORY_URL.startsWith('pi-factory:') ||
|
||||
process.env.PIFACTORY_URL.includes('0.0.0.0');
|
||||
const isLocalConnection = !!process.env.PIFACTORY_URL?.includes('0.0.0.0');
|
||||
|
||||
export class CatalogClientConfiguration {
|
||||
public name = 'CatalogClientConfiguration';
|
||||
@@ -20,13 +17,11 @@ export class CatalogClientConfiguration {
|
||||
package: [
|
||||
Catalog.ProtoPackages.ReadPackage,
|
||||
Catalog.ProtoPackages.WritePackage,
|
||||
PlatformInterfaces.ProtoPackages.WritePackage
|
||||
],
|
||||
credentials: isLocalConnection ? undefined : credentials.createSsl(),
|
||||
protoPath: [
|
||||
Catalog.ProtoPaths.ReadFilePath,
|
||||
Catalog.ProtoPaths.WriteFilePath,
|
||||
PlatformInterfaces.ProtoPaths.WriteFilePath
|
||||
],
|
||||
loader: {
|
||||
keepCase: true,
|
||||
|
||||
@@ -13,8 +13,6 @@ import {
|
||||
Put,
|
||||
Query,
|
||||
UseFilters,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiCreatedResponse,
|
||||
@@ -32,9 +30,7 @@ import { CatalogService } from './catalog.service';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
|
||||
import { RequestUser, User } from 'src/decorators/user.decorator';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import {
|
||||
BatchRemoveRlsRulesRequest,
|
||||
GetDatasetCatalogTaskRes,
|
||||
ICatalogAllRequest,
|
||||
ICatalogAllResponse,
|
||||
@@ -53,12 +49,6 @@ import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filt
|
||||
import { Language } from 'src/decorators/language.decorator';
|
||||
import { LanguageEnum } from 'src/utils/languages.enum';
|
||||
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
|
||||
import {
|
||||
AddRlsRuleRequest,
|
||||
GetNimbusDashboardsRequest,
|
||||
GetRlsRulesRequest,
|
||||
RegisterDatasetWithMetatadaRequest,
|
||||
} from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages';
|
||||
|
||||
@ApiTags('Catalog')
|
||||
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
|
||||
@@ -643,198 +633,4 @@ export class CatalogController {
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('rls-rule')
|
||||
@RequireAllPermissions(PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER)
|
||||
async addRlsRule(@User() user: RequestUser, @Body() body: AddRlsRuleRequest) {
|
||||
const { customer_id, customer_name, user_id, username } = user;
|
||||
const metadata = PackTheMetadata({
|
||||
customer_id,
|
||||
customer_name,
|
||||
user_id,
|
||||
username,
|
||||
});
|
||||
const response = await this.catalogService.addRlsRule(body, metadata);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Get('rls-rule/:id')
|
||||
@RequireAllPermissions(PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER)
|
||||
async getOneRlsRule(@Param('id') id: string, @User() user: RequestUser) {
|
||||
const { customer_id, customer_name, user_id, username } = user;
|
||||
const metadata = PackTheMetadata({
|
||||
customer_id,
|
||||
customer_name,
|
||||
user_id,
|
||||
username,
|
||||
});
|
||||
const idInt = parseInt(id);
|
||||
const response = await this.catalogService.getOneRlsRule(idInt, metadata);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Get('rls-rule')
|
||||
@RequireAllPermissions(PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER)
|
||||
async getRlsRules(
|
||||
@User() user: RequestUser,
|
||||
@Query() query: GetRlsRulesRequest,
|
||||
) {
|
||||
const { customer_id, customer_name, user_id, username } = user;
|
||||
const metadata = PackTheMetadata({
|
||||
customer_id,
|
||||
customer_name,
|
||||
user_id,
|
||||
username,
|
||||
});
|
||||
const response = await this.catalogService.getRlsRules(query, metadata);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Delete('rls-rule/:id')
|
||||
@RequireAllPermissions(PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER)
|
||||
async removeRlsRule(@Param('id') id: string, @User() user: RequestUser) {
|
||||
const { customer_id, customer_name, user_id, username } = user;
|
||||
const metadata = PackTheMetadata({
|
||||
customer_id,
|
||||
customer_name,
|
||||
user_id,
|
||||
username,
|
||||
});
|
||||
const idInt = parseInt(id);
|
||||
const response = await this.catalogService.removeRlsRule(idInt, metadata);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Delete('rls-rule')
|
||||
@RequireAllPermissions(PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER)
|
||||
async batchRemoveRlsRules(
|
||||
@Query() query: BatchRemoveRlsRulesRequest,
|
||||
@User() user: RequestUser,
|
||||
) {
|
||||
const { customer_id, customer_name, user_id, username } = user;
|
||||
const metadata = PackTheMetadata({
|
||||
customer_id,
|
||||
customer_name,
|
||||
user_id,
|
||||
username,
|
||||
});
|
||||
await this.catalogService.batchRemoveRlsRule(query, metadata);
|
||||
|
||||
return { message: 'OK' };
|
||||
}
|
||||
|
||||
@Get('nimbus-dashboards')
|
||||
@RequireAllPermissions(PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER)
|
||||
async getNimbusDashboards(
|
||||
@User() user: RequestUser,
|
||||
@Body() body: GetNimbusDashboardsRequest,
|
||||
) {
|
||||
const { customer_id, customer_name, user_id, username } = user;
|
||||
const metadata = PackTheMetadata({
|
||||
customer_id,
|
||||
customer_name,
|
||||
user_id,
|
||||
username,
|
||||
});
|
||||
const dashboards = await this.catalogService.getNimbusDashboards(
|
||||
body,
|
||||
metadata,
|
||||
);
|
||||
|
||||
return JSON.parse(dashboards);
|
||||
}
|
||||
|
||||
|
||||
@Post('register-dataset')
|
||||
@RequireSomePermission(
|
||||
PERMISSIONS_GROUPS.CATALOG.permissions.CREATE,
|
||||
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER
|
||||
)
|
||||
async registerDatasetWithMetadataRequest(
|
||||
@Body() body: RegisterDatasetWithMetatadaRequest,
|
||||
@User() user: RequestUser,
|
||||
) {
|
||||
const { customer_id, customer_name, user_id, username } = user;
|
||||
const logMetadata = {
|
||||
customer_name: customer_name,
|
||||
user_id: user_id,
|
||||
method: 'POST',
|
||||
path: '/catalog/register-dataset',
|
||||
};
|
||||
try {
|
||||
|
||||
const metadata = PackTheMetadata({
|
||||
customer_id,
|
||||
customer_name,
|
||||
user_id,
|
||||
username,
|
||||
});
|
||||
|
||||
this.logger.log(`Request from user ${user_id} for customer ${customer_name}`, logMetadata);
|
||||
|
||||
// Create table metadata
|
||||
const tableMetadataBody = {
|
||||
table_metadata: body.table_metadata,
|
||||
info: {
|
||||
customer: customer_name,
|
||||
|
||||
},
|
||||
logMetadata: logMetadata
|
||||
};
|
||||
|
||||
const table_metadata_id = await this.catalogService.createTableMetadata(
|
||||
tableMetadataBody,
|
||||
);
|
||||
this.logger.info(`table_metadata_id: ${table_metadata_id}`, logMetadata);
|
||||
|
||||
// Create column metadata
|
||||
const columnMetadataBody = {
|
||||
column_metadata: body.column_metadata,
|
||||
info: {
|
||||
customer: customer_name,
|
||||
},
|
||||
};
|
||||
this.logger.info(`Creating column metadata for table ${table_metadata_id}`, logMetadata);
|
||||
const column_metadata_ids = await this.catalogService.createColumnMetadata(columnMetadataBody)
|
||||
|
||||
// Create data preview
|
||||
const dataPreviewBody = {
|
||||
data_preview: body.data_preview,
|
||||
info: {
|
||||
customer: customer_name,
|
||||
},
|
||||
};
|
||||
this.logger.debug(`Creating data preview for table ${table_metadata_id}`, logMetadata);
|
||||
const data_preview_id = await this.catalogService.createDataPreview(
|
||||
dataPreviewBody,
|
||||
);
|
||||
|
||||
// Catalog dataset item
|
||||
this.logger.info(`Cataloging dataset item for table ${table_metadata_id}`, logMetadata);
|
||||
await this.catalogService.catalogDatasetItem(table_metadata_id, metadata);
|
||||
this.logger.info(`Dataset registration completed successfully for table ${table_metadata_id}`, logMetadata);
|
||||
return {
|
||||
message: 'Dataset registered successfully',
|
||||
table_metadata_id: table_metadata_id,
|
||||
column_metadata_ids: column_metadata_ids,
|
||||
data_preview_id: data_preview_id,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to register dataset. The following error occurred: ${error.response.data}`, logMetadata);
|
||||
|
||||
throw new HttpException(
|
||||
{
|
||||
message: 'Ocorreu um erro ao registrar o dataset',
|
||||
error: error.message,
|
||||
code: 'REGISTRATION_FAILED',
|
||||
details: error.message,
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,6 @@ import {
|
||||
Messages,
|
||||
} from '@dadosfera/protospack-v2/dist/lib/Catalog';
|
||||
import {
|
||||
Messages as PlatformInterfaceMessages,
|
||||
WriteService as PlatformInterfaceWriteService,
|
||||
ProtoServices as PlatformInterfacesProtoServices,
|
||||
} from '@dadosfera/protospack-v2/dist/lib/PlatformInterfaces';
|
||||
import {
|
||||
BadRequestException,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Inject,
|
||||
@@ -24,21 +18,11 @@ import { CatalogClientConfiguration } from './catalog-client';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { RolesService } from '../roles/roles.service';
|
||||
import { Metadata } from '@grpc/grpc-js';
|
||||
import {
|
||||
BatchRemoveRlsRulesRequest,
|
||||
IUpdateDataRequest,
|
||||
TriggerCatalogReq,
|
||||
} from './dtos';
|
||||
import {
|
||||
AddRlsRuleRequest,
|
||||
GetNimbusDashboardsRequest,
|
||||
GetRlsRulesRequest,
|
||||
} from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages';
|
||||
import { IUpdateDataRequest, TriggerCatalogReq } from './dtos';
|
||||
|
||||
class CatalogService implements OnModuleInit {
|
||||
catalogReadService: ReadService.CatalogReadServices;
|
||||
catalogWriteService: WriteService.CatalogWriteServices;
|
||||
platformWriteService: PlatformInterfaceWriteService.PlatformInterfacesWriteServices;
|
||||
logger: any;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
@@ -60,20 +44,21 @@ class CatalogService implements OnModuleInit {
|
||||
this.grpcClient.getService<WriteService.CatalogWriteServices>(
|
||||
ProtoServices.CatalogWriteServices,
|
||||
);
|
||||
this.platformWriteService =
|
||||
this.grpcClient.getService<PlatformInterfaceWriteService.PlatformInterfacesWriteServices>(
|
||||
PlatformInterfacesProtoServices.PlatformInterfacesWriteServices,
|
||||
);
|
||||
}
|
||||
|
||||
_getNimbusUrl(body) {
|
||||
this.logger.debug(`Body: ${JSON.stringify(body)}`);
|
||||
const customer = body.info.customer.toLowerCase();
|
||||
|
||||
if (process.env.ENV === 'prd') {
|
||||
const cloud_environment = process.env.CLOUD_ENVIRONMENT || 'aws';
|
||||
|
||||
if (process.env.ENV === 'prd' && cloud_environment === 'aws') {
|
||||
return `https://nimbus-${customer}.dadosfera.ai`;
|
||||
}
|
||||
|
||||
if (process.env.ENV === 'prd' && cloud_environment === 'mgc') {
|
||||
return `https://nimbus-${customer}.dadosfera.com`;
|
||||
}
|
||||
|
||||
return `https://nimbus-${customer}.${process.env.ENV.replace(
|
||||
'local',
|
||||
'stg',
|
||||
@@ -354,7 +339,6 @@ class CatalogService implements OnModuleInit {
|
||||
} as typeof data_asset;
|
||||
});
|
||||
}
|
||||
|
||||
async triggerCatalog(data: TriggerCatalogReq, metadata: Metadata) {
|
||||
const { session } = await lastValueFrom(
|
||||
this.catalogWriteService.TriggerDatasetCataloging(data, metadata),
|
||||
@@ -367,176 +351,6 @@ class CatalogService implements OnModuleInit {
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
async addRlsRule(data: AddRlsRuleRequest, metadata: Metadata) {
|
||||
const res = await lastValueFrom(
|
||||
this.catalogWriteService.AddRlsRule(data, metadata),
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
async removeRlsRule(id: number, metadata: Metadata) {
|
||||
const res = await lastValueFrom(
|
||||
this.catalogWriteService.RemoveRlsRule({ id }, metadata),
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchRemoveRlsRule(
|
||||
query: BatchRemoveRlsRulesRequest,
|
||||
metadata: Metadata,
|
||||
) {
|
||||
const { id_rls, nimbus_dashboard_id } = query;
|
||||
|
||||
if (id_rls && nimbus_dashboard_id) {
|
||||
throw new BadRequestException(
|
||||
"You can't delete using both parameters. Choose either 'id_rls' or 'nimbus_dashboard_id'",
|
||||
);
|
||||
}
|
||||
if (id_rls) {
|
||||
await lastValueFrom(
|
||||
this.catalogWriteService.RemoveRlsRulesByRlsId({ id_rls }, metadata),
|
||||
);
|
||||
} else if (nimbus_dashboard_id) {
|
||||
await lastValueFrom(
|
||||
this.catalogWriteService.RemoveRlsRulesByDashboardId(
|
||||
{ nimbus_dashboard_id: parseInt(nimbus_dashboard_id) },
|
||||
metadata,
|
||||
),
|
||||
);
|
||||
}
|
||||
return 'OK';
|
||||
}
|
||||
|
||||
async getRlsRules(data: GetRlsRulesRequest, metadata: Metadata) {
|
||||
const res = await lastValueFrom(
|
||||
this.catalogReadService.GetRlsRules(data, metadata),
|
||||
);
|
||||
return res.rls_rules;
|
||||
}
|
||||
|
||||
async getOneRlsRule(id: number, metadata: Metadata) {
|
||||
const res = await lastValueFrom(
|
||||
this.catalogReadService.GetOneRlsRule({ id }, metadata),
|
||||
);
|
||||
return res.rls_rule;
|
||||
}
|
||||
|
||||
async getNimbusDashboards(
|
||||
data: GetNimbusDashboardsRequest,
|
||||
metadata: Metadata,
|
||||
) {
|
||||
const res = await lastValueFrom(
|
||||
this.catalogReadService.GetNimbusDashboards(data, metadata),
|
||||
);
|
||||
return res.dashboards;
|
||||
}
|
||||
|
||||
async createTableMetadata(body: any): Promise<number> {
|
||||
const nimbusUrl = this._getNimbusUrl(body);
|
||||
this.logger.info(`Nimbus URL: ${nimbusUrl}`, {...body.logMetadata});
|
||||
|
||||
const endpoint = `${nimbusUrl}/api/catalog/table-metadata/`;
|
||||
|
||||
this.logger.info(`Creating table metadata for table ${body.table_metadata.table_name}`, {...body.logMetadata});
|
||||
this.logger.info(`Using endpoint: ${endpoint}`, {...body.logMetadata});
|
||||
this.logger.debug(`Payload: ${JSON.stringify(body.table_metadata)}`, {...body.logMetadata});
|
||||
|
||||
try {
|
||||
const { data, status } = await axios.post(endpoint, {...body.table_metadata});
|
||||
|
||||
this.logger.info(
|
||||
`Table metadata created successfully with status ${status} for table ${body.table_metadata.table_name}`,
|
||||
{...body.logMetadata},
|
||||
);
|
||||
return data.id;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to create table metadata for table ${body.table_metadata.table_name} failed with status ${
|
||||
error.response?.status
|
||||
} because of ${JSON.stringify(error.response?.data) || error.message}`, {...body.logMetadata});
|
||||
throw new Error(error.response?.data?.message || error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async createColumnMetadata(body: any): Promise<number[]> {
|
||||
const nimbusUrl = this._getNimbusUrl(body);
|
||||
this.logger.info(`Nimbus URL: ${nimbusUrl}`, body.logMetadata);
|
||||
const endpoint = `${nimbusUrl}/api/catalog/column-metadata/`;
|
||||
|
||||
|
||||
|
||||
try {
|
||||
this.logger.info(`Creating column metadata for table ${body.column_metadata.table_name}`, {...body.logMetadata});
|
||||
this.logger.info(`Using endpoint: ${endpoint}`, {...body.logMetadata});
|
||||
this.logger.debug(`Payload: ${JSON.stringify(body.column_metadata)}`, {...body.logMetadata});
|
||||
const { data, status } = await axios.post(endpoint, body.column_metadata);
|
||||
|
||||
this.logger.info(
|
||||
`Column metadata created successfully with status ${status} for table ${body.column_metadata.table_name}`,
|
||||
{...body.logMetadata},
|
||||
);
|
||||
return data.map((column) => column.id);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to create column metadata failed with status for table ${body.column_metadata.table_name} ${
|
||||
error.response?.status
|
||||
} because of ${error.response?.data || error.message}`, {...body.logMetadata});
|
||||
throw new Error(error.response?.data?.message || error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async createDataPreview(body: any): Promise<number> {
|
||||
const nimbusUrl = this._getNimbusUrl(body);
|
||||
this.logger.info(`Nimbus URL: ${nimbusUrl}`, {...body.logMetadata});
|
||||
const endpoint = `${nimbusUrl}/api/catalog/data-preview/`;
|
||||
|
||||
this.logger.info(`Creating data preview for table ${body.data_preview.table_name}`, {...body.logMetadata});
|
||||
this.logger.info(`Using endpoint: ${endpoint}`, {...body.logMetadata});
|
||||
this.logger.debug(`Payload: ${JSON.stringify(body.data_preview)}`, {...body.logMetadata});
|
||||
|
||||
try {
|
||||
const { data, status } = await axios.post(endpoint, body.data_preview);
|
||||
|
||||
this.logger.info(
|
||||
`Data preview created successfully with status ${status} for table ${body.data_preview.table_name}`,
|
||||
{...body.logMetadata},
|
||||
);
|
||||
return data.id;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to create data preview for table ${body.data_preview.table_name} failed with status ${
|
||||
error.response?.status
|
||||
} because of ${error.response?.data || error.message}`,
|
||||
{...body.logMetadata},
|
||||
);
|
||||
throw new Error(error.response?.data?.message || error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async catalogDatasetItem(table_metadata_id: number, metadata: Metadata) {
|
||||
const customer_name_raw = metadata.get('customer_name');
|
||||
|
||||
const customer_name = customer_name_raw?.[0]?.toString();
|
||||
if (!customer_name) {
|
||||
throw new BadRequestException('Customer name not found in metadata');
|
||||
}
|
||||
const res = await lastValueFrom(
|
||||
this.platformWriteService.CatalogDataAssets(
|
||||
{
|
||||
data_assets: [
|
||||
{
|
||||
data_asset_id: table_metadata_id.toString(),
|
||||
customer_name: customer_name,
|
||||
data_asset_type: 'dataset',
|
||||
},
|
||||
],
|
||||
},
|
||||
metadata,
|
||||
),
|
||||
);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export { CatalogService };
|
||||
|
||||
@@ -312,11 +312,3 @@ export class GetDatasetCatalogTaskRes {
|
||||
@ApiProperty()
|
||||
updated_by: string;
|
||||
}
|
||||
|
||||
export class BatchRemoveRlsRulesRequest {
|
||||
@ApiPropertyOptional()
|
||||
nimbus_dashboard_id?: string;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
id_rls?: string;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ import {
|
||||
} from '@nestjs/microservices';
|
||||
import { ConnectionTest } from '@dadosfera/protospack-v2';
|
||||
|
||||
const isLocalConnection =
|
||||
process.env.INFACTORY_URL.startsWith('in-factory:') ||
|
||||
process.env.INFACTORY_URL.includes('0.0.0.0');
|
||||
const isLocalConnection = !!process.env.INFACTORY_URL?.includes('0.0.0.0');
|
||||
|
||||
export class ConnectionTestClientConfiguration {
|
||||
private config: GrpcOptions = {
|
||||
|
||||
@@ -6,9 +6,7 @@ import {
|
||||
} from '@nestjs/microservices';
|
||||
import { ConnectionManager } from '@dadosfera/protospack-v2';
|
||||
|
||||
const isLocalConnection =
|
||||
process.env.INFACTORY_URL.startsWith('in-factory:') ||
|
||||
process.env.INFACTORY_URL.includes('0.0.0.0');
|
||||
const isLocalConnection = !!process.env.INFACTORY_URL?.includes('0.0.0.0');
|
||||
|
||||
export class ConnectionClientConfiguration {
|
||||
public name = 'ConnectionClientConfiguration';
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
ConnectionDetailsRes,
|
||||
ConnectionRes,
|
||||
ConnectionsRes,
|
||||
GetAllConnectionsReq,
|
||||
UpdateConnectionDto,
|
||||
} from './dtos/connection';
|
||||
import { CreateConnectionDto } from './dtos/connection';
|
||||
@@ -139,7 +138,7 @@ export class ConnectionController {
|
||||
async getAllConnections(
|
||||
@User() user: RequestUser,
|
||||
@Language() language: LanguageEnum,
|
||||
@Query() queries: GetAllConnectionsReq,
|
||||
@Query() queries,
|
||||
): Promise<ConnectionsRes> {
|
||||
this.logger.info('/connections - Get All Connection');
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
ConnectionToCatalog,
|
||||
} from '@dadosfera/protospack-v2/dist/lib/ConnectionManager/interfaces/entities';
|
||||
import {
|
||||
GetAllConnectionRequest,
|
||||
GetAllConnectionResponse,
|
||||
GetConnectionDetailsResponse,
|
||||
GetConnectionResponse,
|
||||
@@ -22,7 +21,7 @@ export type ConnectionCredentialsType =
|
||||
| 'oauth'
|
||||
| 'api_key'
|
||||
| 'service_account'
|
||||
| 'headers_auth';
|
||||
| 'headers_authF';
|
||||
export interface ConnectionApiConnection {
|
||||
config_id: string;
|
||||
plugin: string;
|
||||
@@ -95,6 +94,7 @@ export class ConnectionDto implements Connection {
|
||||
|
||||
export class ConnectionToCatalogDto implements ConnectionToCatalog {
|
||||
// ---Automatically generated information - will not be sent by the frontend--- //
|
||||
id: string;
|
||||
customer_id: string;
|
||||
updated_at: string;
|
||||
created_at: string;
|
||||
@@ -104,9 +104,6 @@ export class ConnectionToCatalogDto implements ConnectionToCatalog {
|
||||
customer_name: string;
|
||||
|
||||
// ---Information sent by the frontend--- //
|
||||
@ApiPropertyOptional()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
|
||||
@@ -150,15 +147,6 @@ export class UpdateConnectionDto extends PickType(ConnectionDto, [
|
||||
'properties',
|
||||
]) {}
|
||||
|
||||
export class GetAllConnectionsReq implements GetAllConnectionRequest {
|
||||
@ApiPropertyOptional()
|
||||
search?: string;
|
||||
@ApiPropertyOptional()
|
||||
page?: string;
|
||||
@ApiPropertyOptional()
|
||||
size?: string;
|
||||
}
|
||||
|
||||
export class ConnectionsRes implements GetAllConnectionResponse {
|
||||
@ApiProperty({ type: [ConnectionToCatalogDto] })
|
||||
connections: ConnectionToCatalogDto[];
|
||||
|
||||
@@ -6,9 +6,7 @@ import {
|
||||
} from '@nestjs/microservices';
|
||||
import { ConnectorManager } from '@dadosfera/protospack-v2';
|
||||
|
||||
const isLocalConnection =
|
||||
process.env.INFACTORY_URL.startsWith('in-factory:') ||
|
||||
process.env.INFACTORY_URL.includes('0.0.0.0');
|
||||
const isLocalConnection = !!process.env.INFACTORY_URL?.includes('0.0.0.0');
|
||||
|
||||
export class ConnectorClientConfiguration {
|
||||
public name = 'ConnectorClientConfiguration';
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
import { IdResponse } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Inject,
|
||||
Param,
|
||||
Put,
|
||||
Query,
|
||||
UseFilters,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOkResponse, ApiProduces, ApiTags } from '@nestjs/swagger';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
|
||||
import {
|
||||
Authenticated,
|
||||
RequireAllPermissions,
|
||||
} from 'src/decorators/authentication.decorator';
|
||||
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
|
||||
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
|
||||
import { CustomersService } from './customers.service';
|
||||
import { CustomerLinkRequest, CustomerLinksResponse } from './dtos/customers';
|
||||
import { CustomerDto, CustomerLinksResponse } from './dtos/customers';
|
||||
import { RequestUser, User } from 'src/decorators/user.decorator';
|
||||
import type { StringValue } from 'ms';
|
||||
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
|
||||
|
||||
@ApiInternalOnlyController()
|
||||
@ApiTags('Customers')
|
||||
@Controller('customers')
|
||||
@Authenticated()
|
||||
@UseFilters(GrpcToHttpExceptionFilter)
|
||||
export class CustomersController {
|
||||
logger: DadosferaLogger;
|
||||
@@ -39,32 +40,25 @@ export class CustomersController {
|
||||
}
|
||||
|
||||
@Get(':id/links')
|
||||
@Authenticated()
|
||||
@ApiOkResponse({ type: CustomerLinksResponse })
|
||||
async getCustomerLinks(@Param('id') id: string) {
|
||||
async getCustomerLinks(@Param('id') id): Promise<CustomerLinksResponse> {
|
||||
this.logger.info('getCustomerLinks', { id });
|
||||
const links = await this.customersService.getLinks(id);
|
||||
return { links };
|
||||
}
|
||||
|
||||
@Put(':id/links')
|
||||
@Authenticated()
|
||||
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
|
||||
@ApiOkResponse()
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async setCustomerLinks(
|
||||
@Body() body: CustomerLinkRequest,
|
||||
@Param('id') id: string,
|
||||
) {
|
||||
setCustomerLinks(
|
||||
@Body() body: CustomerDto,
|
||||
@Param('id') id,
|
||||
): Promise<IdResponse> {
|
||||
const { links } = body;
|
||||
this.logger.info('setCustomerLinks', { id, links });
|
||||
await this.customersService.setLinks(id, links);
|
||||
return this.customersService.setLinks(id, links);
|
||||
}
|
||||
|
||||
@Get('token')
|
||||
@Authenticated()
|
||||
@RequireAllPermissions(PERMISSIONS_GROUPS.AUTH.permissions.GENERATE_TOKEN)
|
||||
@ApiProduces('text/plain')
|
||||
async getCustomerToken(
|
||||
@Query('exp') exp: StringValue,
|
||||
@User() user: RequestUser,
|
||||
@@ -77,18 +71,4 @@ export class CustomersController {
|
||||
};
|
||||
return this.customersService.generateToken(exp, data);
|
||||
}
|
||||
|
||||
@Get('monitoring-dashboard')
|
||||
@Authenticated()
|
||||
@RequireAllPermissions(
|
||||
PERMISSIONS_GROUPS.CUSTOMER.permissions.MONITORING_DASHBOARD,
|
||||
)
|
||||
async getCustomerMonitoringDashboard(
|
||||
@User() user: RequestUser,
|
||||
): Promise<{ url: string }> {
|
||||
this.logger.info('getCustomerMonitoringDashboard');
|
||||
|
||||
const metadata = PackTheMetadata(user);
|
||||
return this.customersService.getMonitoringDashboardUrl(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,18 +4,11 @@ import { ClientsModule } from '@nestjs/microservices';
|
||||
import { DucClient } from '../duc/client.config';
|
||||
import { CustomersController } from './customers.controller';
|
||||
import { CustomersService } from './customers.service';
|
||||
import { PipelinesClientConfiguration } from '../pipelinesV2/pipelines-client';
|
||||
|
||||
const ducClient = new DucClient();
|
||||
const piFactoryClient = new PipelinesClientConfiguration();
|
||||
const client = new DucClient();
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ClientsModule.register([
|
||||
ducClient.providerOptions,
|
||||
piFactoryClient.providerOptions,
|
||||
]),
|
||||
],
|
||||
imports: [ClientsModule.register([client.providerOptions])],
|
||||
controllers: [CustomersController],
|
||||
providers: [CustomersService, DadosferaLogger],
|
||||
exports: [CustomersService],
|
||||
|
||||
@@ -22,39 +22,20 @@ import {
|
||||
} from '@aws-sdk/client-secrets-manager';
|
||||
import getEnv from 'src/utils/getEnv';
|
||||
import { logger } from 'elastic-apm-node';
|
||||
import {
|
||||
ReadService,
|
||||
ProtoServices as PipelineProtoServices,
|
||||
} from '@dadosfera/protospack-v2/dist/lib/PipelineV2';
|
||||
import { Metadata } from '@grpc/grpc-js';
|
||||
import { PipelinesClientConfiguration } from '../pipelinesV2/pipelines-client';
|
||||
import DadosferaLogger from '@dadosfera/dadosfera-logs';
|
||||
|
||||
// This function will accept any string, which may result in a bug.
|
||||
@Injectable()
|
||||
export class CustomersService implements OnModuleInit {
|
||||
private customerService: CustomersProtoService;
|
||||
private logger: DadosferaLogger;
|
||||
private pipelineReadService: ReadService.PipelineV2ReadService;
|
||||
|
||||
constructor(
|
||||
@Inject(DucClient.name) private readonly grpcClient: ClientGrpc,
|
||||
@Inject(PipelinesClientConfiguration.name)
|
||||
private readonly pipelinesGrpcClient: ClientGrpc,
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
) {}
|
||||
|
||||
onModuleInit() {
|
||||
this.customerService = this.grpcClient.getService<CustomersProtoService>(
|
||||
ProtoServices.CustomersProtoService,
|
||||
);
|
||||
this.pipelineReadService =
|
||||
this.pipelinesGrpcClient.getService<ReadService.PipelineV2ReadService>(
|
||||
PipelineProtoServices.PipelineV2ReadService,
|
||||
);
|
||||
}
|
||||
|
||||
async getLinks(customerId: string) {
|
||||
@@ -139,21 +120,4 @@ export class CustomersService implements OnModuleInit {
|
||||
// const decoded = jwt.decode(jwt_token, { complete: true });
|
||||
return jwt_token;
|
||||
}
|
||||
async getMonitoringDashboardUrl(metadata: Metadata) {
|
||||
logger.info('CustomersService - getMonitoringDashboardUrl');
|
||||
|
||||
const res = await lastValueFrom(
|
||||
this.pipelineReadService.PipelineV2GetDashboardUrl(
|
||||
{
|
||||
dashboard_id: '98',
|
||||
exp: '15m',
|
||||
metabase_customer_name: 'dadosferatech',
|
||||
},
|
||||
metadata,
|
||||
),
|
||||
);
|
||||
logger.info('Done');
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
import { Link } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/entities';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CustomerLink implements Link {
|
||||
export class CustomerDto {
|
||||
@ApiProperty()
|
||||
href: string;
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
@ApiProperty()
|
||||
description: string;
|
||||
@ApiPropertyOptional()
|
||||
iconSrc: string;
|
||||
}
|
||||
export class CustomerLinkRequest {
|
||||
@ApiProperty({ type: [CustomerLink] })
|
||||
links: CustomerLink[];
|
||||
links: Link[];
|
||||
}
|
||||
|
||||
export class CustomerLinksResponse {
|
||||
@ApiProperty({ type: [CustomerLink] })
|
||||
links: CustomerLink[];
|
||||
@ApiProperty()
|
||||
links: Link[];
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,7 @@ import {
|
||||
ProtoPaths,
|
||||
} from '@dadosfera/protospack-v2/dist/lib/Duc';
|
||||
|
||||
const isLocalConnection =
|
||||
process.env.DUC_URL.startsWith('duc:') ||
|
||||
process.env.DUC_URL.includes('0.0.0.0');
|
||||
const isLocalConnection = !!process.env.DUC_URL?.includes('0.0.0.0');
|
||||
|
||||
export class DucClient {
|
||||
public name = 'DucClient';
|
||||
@@ -29,8 +27,6 @@ export class DucClient {
|
||||
objects: true,
|
||||
arrays: true,
|
||||
},
|
||||
maxSendMessageLength: 15 * 1024 * 1024, // 15 MB por mensagem
|
||||
maxReceiveMessageLength: 15 * 1024 * 1024,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Info } from '@dadosfera/protospack/dist/lib/interfaces';
|
||||
import { Info } from 'protospack/dist/lib/interfaces';
|
||||
|
||||
interface Values {
|
||||
jdbc_user: string;
|
||||
|
||||
@@ -6,9 +6,7 @@ import {
|
||||
type GrpcOptions,
|
||||
} from '@nestjs/microservices';
|
||||
|
||||
const isLocalConnection =
|
||||
process.env.INFACTORY_URL.startsWith('in-factory:') ||
|
||||
process.env.INFACTORY_URL.includes('0.0.0.0');
|
||||
const isLocalConnection = !!process.env.INFACTORY_URL?.includes('0.0.0.0');
|
||||
export class InputsGrpcClient {
|
||||
public readonly name = 'InputsGrpcClient';
|
||||
private config: GrpcOptions = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Body, Controller, Inject, Param, Post, Req } from '@nestjs/common';
|
||||
import { Body, Controller, Inject, Param, Post } from '@nestjs/common';
|
||||
import { init } from 'mixpanel';
|
||||
import { Authenticated } from 'src/decorators/authentication.decorator';
|
||||
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
|
||||
@@ -13,13 +13,10 @@ export class MixpanelController {
|
||||
private readonly mixpanelToken: string,
|
||||
) {}
|
||||
@Post(':id')
|
||||
async trackEvent(
|
||||
@Param('id') id,
|
||||
@Body() body,
|
||||
@User() user: RequestUser,
|
||||
@Req() request
|
||||
) {
|
||||
async trackEvent(@Param('id') id, @Body() body, @User() user: RequestUser) {
|
||||
delete body.info;
|
||||
//TODO: remove this when mixpanel track is working
|
||||
return true;
|
||||
const mixpanel = init(this.mixpanelToken);
|
||||
|
||||
const separator = user.username.includes('-') ? '-' : '.';
|
||||
@@ -71,16 +68,13 @@ export class MixpanelController {
|
||||
$email: user.username.includes('@')
|
||||
? user.username
|
||||
: user.username + '@dadosfera.ai',
|
||||
customer_name: user.customer_name
|
||||
customer_name: user.customer_name,
|
||||
});
|
||||
|
||||
await mixpanel.track(id, {
|
||||
distinct_id: user.username,
|
||||
customer: user.customer_name,
|
||||
env: process.env.ENV,
|
||||
$ip: request.ip,
|
||||
$os: request.headers['sec-ch-ua-platform'] || '',
|
||||
$browser: request.headers['user-agent'],
|
||||
...body,
|
||||
});
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ import {
|
||||
} from '@nestjs/microservices';
|
||||
import { NetworkConfig } from '@dadosfera/protospack-v2';
|
||||
|
||||
const isLocalConnection =
|
||||
process.env.INFACTORY_URL.startsWith('in-factory:') ||
|
||||
process.env.INFACTORY_URL.includes('0.0.0.0');
|
||||
const isLocalConnection = !!process.env.INFACTORY_URL?.includes('0.0.0.0');
|
||||
|
||||
export class NetworkConfigGrpcClient {
|
||||
public name = 'NetworkConfigGrpcClient';
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
|
||||
export class CreateUserOpenDataDTO {
|
||||
@ApiProperty()
|
||||
firstName: string;
|
||||
@ApiProperty()
|
||||
lastName: string;
|
||||
@ApiProperty()
|
||||
email: string;
|
||||
@ApiProperty()
|
||||
organization: string;
|
||||
@ApiProperty()
|
||||
enquiryType: string;
|
||||
}
|
||||
|
||||
type FormField = {
|
||||
id: string;
|
||||
type: string;
|
||||
title: string;
|
||||
value: string;
|
||||
raw_value: string;
|
||||
required: string;
|
||||
};
|
||||
|
||||
type MetaData = {
|
||||
title: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type WordpressForm = {
|
||||
form: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
fields: {
|
||||
[key: string]: FormField;
|
||||
};
|
||||
meta: {
|
||||
date: MetaData;
|
||||
time: MetaData;
|
||||
page_url: MetaData;
|
||||
user_agent: MetaData;
|
||||
remote_ip: MetaData;
|
||||
credit: MetaData;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
import DadosferaLogger from '@dadosfera/dadosfera-logs';
|
||||
import { Body, Controller, ForbiddenException, Header, Headers, HttpCode, HttpException, Inject, Param, Post, Query, Req, Res, UseFilters, UseGuards, UseInterceptors } from '@nestjs/common';
|
||||
import { ApiCreatedResponse, ApiHeaders, ApiOkResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
|
||||
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
|
||||
import { LanguageEnum } from 'src/utils/languages.enum';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { Language } from 'src/decorators/language.decorator';
|
||||
import { OpenDataService } from './open-data.service';
|
||||
import { CreateUserOpenDataDTO, WordpressForm } from './dto/wordpres-form';
|
||||
import { Metadata } from '@grpc/grpc-js';
|
||||
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
|
||||
import { request } from 'http';
|
||||
import { Request } from 'express';
|
||||
|
||||
@Controller('open-data')
|
||||
@ApiInternalOnlyController()
|
||||
@ApiTags('OpenData')
|
||||
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
|
||||
@UseFilters(GrpcToHttpExceptionFilter)
|
||||
export class OpenDataController {
|
||||
logger: DadosferaLogger;
|
||||
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
private openDataService: OpenDataService,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
@Post("/sharing-ocean-data")
|
||||
@HttpCode(200)
|
||||
@Header('content-type', 'application/json')
|
||||
@ApiOkResponse()
|
||||
async createUser(
|
||||
@Body()
|
||||
body: WordpressForm,
|
||||
@Query('language')
|
||||
language: string,
|
||||
@Req()
|
||||
request: Request,
|
||||
@Headers('origin')
|
||||
origin: string
|
||||
) {
|
||||
this.logger.info('createUser for open data' + JSON.stringify(request.headers));
|
||||
|
||||
const corslist = ["https://devsbm.dadosfera.io", "https://sharingoceandata.com"];
|
||||
if (!corslist.includes(origin)) {
|
||||
this.logger.info('block request by cors list: '+ origin);
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
const OPENDATA_CUSTOMER_ID = process.env.OPEN_CUSTOMER_ID;
|
||||
const OPENDATA_GROUP_ID = process.env.OPEN_GROUP_ID;
|
||||
const roles = [process.env.OPEN_GROUP_ID];
|
||||
const metadata = PackTheMetadata({
|
||||
language: language || 'en-us'
|
||||
});
|
||||
|
||||
const data = {}
|
||||
|
||||
try {
|
||||
Object.keys(body.fields)
|
||||
.filter(key => body.fields[key].required === "1")
|
||||
.forEach(key => {
|
||||
const field = body.fields[key]
|
||||
data[field.id] = field.value
|
||||
});
|
||||
} catch (e) {
|
||||
this.logger.error('user data ' + e.message);
|
||||
}
|
||||
|
||||
const user: CreateUserOpenDataDTO = {
|
||||
email: data["email"],
|
||||
enquiryType: data["enquiry_type"],
|
||||
firstName: data["first_name"],
|
||||
lastName: data["last_name"],
|
||||
organization: data["organization"]
|
||||
}
|
||||
this.logger.info(`user request to group ${OPENDATA_CUSTOMER_ID} with role ${OPENDATA_GROUP_ID}`);
|
||||
|
||||
try {
|
||||
const id = await this.openDataService.createUser(OPENDATA_CUSTOMER_ID, user, roles, metadata);
|
||||
this.logger.info('user created with id: '+ id);
|
||||
return {
|
||||
success: true,
|
||||
status: 'success',
|
||||
message: 'user created with succesfull'
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error('failed with exception: ' + e.message);
|
||||
return {
|
||||
success: false,
|
||||
status: 'failed',
|
||||
message: e.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { OpenDataController } from './open-data.controller';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { ClientsModule } from '@nestjs/microservices'
|
||||
import { DucClient } from '../duc/client.config';
|
||||
import DadosferaLogger from '@dadosfera/dadosfera-logs';
|
||||
import { RolesModule } from '../roles/roles.module';
|
||||
import { PermissionsModule } from '../permissions/permissions.module';
|
||||
import { OpenDataService } from './open-data.service';
|
||||
|
||||
const client = new DucClient();
|
||||
|
||||
@Module({
|
||||
controllers: [OpenDataController],
|
||||
imports: [
|
||||
ClientsModule.register([client.providerOptions]),
|
||||
RolesModule,
|
||||
// PermissionsModule,
|
||||
],
|
||||
providers: [DadosferaLogger, UsersService, OpenDataService]
|
||||
})
|
||||
export class OpenDataModule {}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { CreateUserOpenDataDTO } from './dto/wordpres-form';
|
||||
import DadosferaLogger from '@dadosfera/dadosfera-logs';
|
||||
import { UsersProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
|
||||
import { DucClient } from '../duc/client.config';
|
||||
import { ClientGrpc } from '@nestjs/microservices';
|
||||
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
|
||||
import { Metadata } from '@grpc/grpc-js';
|
||||
|
||||
@Injectable()
|
||||
export class OpenDataService implements OnModuleInit {
|
||||
logger: DadosferaLogger;
|
||||
|
||||
private usersClientService: UsersProtoService;
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
private dadosferaLogger: DadosferaLogger,
|
||||
@Inject(DucClient.name)
|
||||
private readonly grpcClient: ClientGrpc,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
onModuleInit() {
|
||||
this.usersClientService = this.grpcClient.getService(
|
||||
ProtoServices.UsersProtoService,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
async createUser(customerId: string, data: CreateUserOpenDataDTO, roleIds: string[], metadata: Metadata) {
|
||||
const body = {
|
||||
email: data.email,
|
||||
name: data.firstName + " " + data.lastName,
|
||||
department: data.organization,
|
||||
jobTitle: data.enquiryType,
|
||||
customerId: customerId,
|
||||
roleIds: roleIds
|
||||
}
|
||||
|
||||
try {
|
||||
const { user } = await lastValueFrom(
|
||||
this.usersClientService.SimpleUserCreate(body, metadata),
|
||||
);
|
||||
return user.id;
|
||||
} catch(err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
import { ConflictException, Inject, OnModuleInit } from '@nestjs/common';
|
||||
import { ClientGrpc } from '@nestjs/microservices';
|
||||
import {
|
||||
PipelineServicesNames,
|
||||
PipelinesServiceInterface,
|
||||
} from '@dadosfera/protospack';
|
||||
import { PipelineServicesNames, PipelinesServiceInterface } from 'protospack';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
import { IIdRequest } from './interfaces';
|
||||
@@ -14,10 +11,9 @@ import { PipelinesClientConfiguration } from './pipelines-client';
|
||||
export class PipelinesClientService implements OnModuleInit {
|
||||
private pipelineService: PipelinesServiceInterface;
|
||||
logger: DadosferaLogger;
|
||||
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
@Inject(PipelinesClientConfiguration.name)
|
||||
private readonly grpcClient: ClientGrpc,
|
||||
) {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Info } from '@dadosfera/protospack/dist/lib/interfaces';
|
||||
import { Info } from 'protospack/dist/lib/interfaces';
|
||||
|
||||
export interface ICreatePipelineDto {
|
||||
input: IdRequest;
|
||||
|
||||
@@ -3,12 +3,10 @@ import {
|
||||
GrpcOptions,
|
||||
Transport,
|
||||
} from '@nestjs/microservices';
|
||||
import { PipelinePackages, PipelineProtoFilePath } from '@dadosfera/protospack';
|
||||
import { PipelinePackages, PipelineProtoFilePath } from 'protospack';
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
|
||||
const isLocalConnection =
|
||||
process.env.PIFACTORY_URL.startsWith('pi-factory:') ||
|
||||
process.env.PIFACTORY_URL.includes('0.0.0.0');
|
||||
const isLocalConnection = !!process.env.PIFACTORY_URL?.includes('0.0.0.0');
|
||||
|
||||
export class PipelinesClientConfiguration {
|
||||
public name = 'PipelinesClientConfiguration';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger';
|
||||
import { Info } from '@dadosfera/protospack/dist/lib/interfaces';
|
||||
import { Info } from 'protospack/dist/lib/interfaces';
|
||||
|
||||
export class IPipelineV2 {
|
||||
@ApiProperty()
|
||||
|
||||
@@ -9,9 +9,7 @@ import {
|
||||
} from '@dadosfera/protospack-v2/dist/lib/PipelineV2';
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
|
||||
const isLocalConnection =
|
||||
process.env.PIFACTORY_URL.startsWith('pi-factory:') ||
|
||||
process.env.PIFACTORY_URL.includes('0.0.0.0');
|
||||
const isLocalConnection = !!process.env.PIFACTORY_URL?.includes('0.0.0.0');
|
||||
|
||||
export class PipelinesClientConfiguration {
|
||||
public name = 'PipelinesClientConfiguration';
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
Patch,
|
||||
HttpException,
|
||||
BadRequestException,
|
||||
CacheTTL,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiCreatedResponse,
|
||||
@@ -87,18 +86,6 @@ export class PipelinesController {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
@Get('monitoring-dashboard')
|
||||
async getMonitoringDashboard(@User() user: RequestUser) {
|
||||
this.logger.info('PipelinesController - getMonitoringDashboard', { user });
|
||||
|
||||
const metadata = PackTheMetadata(user);
|
||||
|
||||
const response =
|
||||
await this.pipelinesClientService.getMonitoringDashboardUrl(metadata);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiCreatedResponse({ type: IPipelineV2 })
|
||||
async create(
|
||||
|
||||
@@ -321,22 +321,4 @@ export class PipelinesService implements OnModuleInit {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async getMonitoringDashboardUrl(metadata: Metadata) {
|
||||
this.logger.info('PipelinesClientService - getMonitoringDashboardUrl');
|
||||
|
||||
const res = await lastValueFrom(
|
||||
this.pipelineReadService.PipelineV2GetDashboardUrl(
|
||||
{
|
||||
dashboard_id: '95',
|
||||
exp: '15m',
|
||||
metabase_customer_name: 'dadosferatech',
|
||||
},
|
||||
metadata,
|
||||
),
|
||||
);
|
||||
this.logger.info('Done');
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import { Link, Theme } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/entities';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
export class CustomerLink implements Link {
|
||||
@ApiProperty()
|
||||
href: string;
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
@ApiProperty()
|
||||
description: string;
|
||||
@ApiPropertyOptional()
|
||||
iconSrc: string;
|
||||
}
|
||||
export class CustomerLinkRequest {
|
||||
@ApiProperty({ type: [CustomerLink] })
|
||||
links: CustomerLink[];
|
||||
}
|
||||
|
||||
export class CustomerLinksResponse {
|
||||
@ApiProperty({ type: [CustomerLink] })
|
||||
links: CustomerLink[];
|
||||
}
|
||||
|
||||
export class CustomerTheme implements Theme {
|
||||
@ApiProperty()
|
||||
backgroundColor: string;
|
||||
@ApiProperty()
|
||||
textColor: string;
|
||||
@ApiProperty()
|
||||
logo: string;
|
||||
@ApiProperty()
|
||||
logoLogin: string;
|
||||
}
|
||||
|
||||
export class CustomerThemeResponse {
|
||||
@ApiProperty()
|
||||
theme: CustomerTheme;
|
||||
}
|
||||
|
||||
export class CustomerThemeRequest {
|
||||
@ApiProperty()
|
||||
displayName: string;
|
||||
@ApiProperty()
|
||||
backgroundColor: string;
|
||||
@ApiProperty()
|
||||
textColor: string;
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
UploadedFiles,
|
||||
UseFilters,
|
||||
UseInterceptors,
|
||||
HttpCode
|
||||
} from '@nestjs/common';
|
||||
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
|
||||
import {
|
||||
Authenticated,
|
||||
RequireAllPermissions,
|
||||
} from 'src/decorators/authentication.decorator';
|
||||
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
|
||||
import { CustomerThemeRequest, CustomerThemeResponse } from './dtos/customers';
|
||||
|
||||
import ErrorCodes from 'src/utils/errorCodes';
|
||||
import { AnyFilesInterceptor } from '@nestjs/platform-express';
|
||||
import { ThemeService } from './theme.service';
|
||||
|
||||
@ApiTags('Theme')
|
||||
@Controller('customers')
|
||||
@UseFilters(GrpcToHttpExceptionFilter)
|
||||
export class ThemeController {
|
||||
logger: DadosferaLogger;
|
||||
|
||||
constructor(
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
private themeService: ThemeService,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
|
||||
@Post('/:id/theme')
|
||||
@Authenticated()
|
||||
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
|
||||
@ApiOkResponse({ type: CustomerThemeResponse })
|
||||
@UseInterceptors(AnyFilesInterceptor())
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async saveCustomertheme(
|
||||
@Param('id') id: string,
|
||||
@Body() data: CustomerThemeRequest,
|
||||
@UploadedFiles() files: Array<Express.Multer.File>
|
||||
) {
|
||||
|
||||
this.logger.info('saveCustomertheme' + JSON.stringify({
|
||||
id,
|
||||
}));
|
||||
|
||||
const logo = files.find(file => file.fieldname === 'logo');
|
||||
const logoLogin = files.find(file => file.fieldname === 'logoLogin');
|
||||
|
||||
this.validFileSize(logo);
|
||||
this.validFileSize(logoLogin);
|
||||
this.validMimeType(logo);
|
||||
this.validMimeType(logoLogin);
|
||||
|
||||
try {
|
||||
const theme = await this.themeService.createThemeByCustomer(id, {
|
||||
...data,
|
||||
logo,
|
||||
logoLogin
|
||||
});
|
||||
this.logger.info('saveCustomertheme' + JSON.stringify(theme));
|
||||
return theme;
|
||||
} catch (err) {
|
||||
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) {
|
||||
this.logger.error('Error - saveCustomertheme - Expect CUSTOMER.NOT_FOUND');
|
||||
throw new HttpException(err.details, HttpStatus.NOT_FOUND);
|
||||
} else {
|
||||
this.logger.error('Error - saveCustomertheme Unknown Error:' + err?.message);
|
||||
return { theme: null };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private validFileSize(file: Express.Multer.File) {
|
||||
const maxFileSize = 10 * 1024 * 1024; // 10MB
|
||||
|
||||
if (file && file.size > maxFileSize) {
|
||||
throw new HttpException(`O Arquivo ${file.filename} possui mais de 10MB`, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
private validMimeType(file: Express.Multer.File) {
|
||||
const mimeTypesValid = ['image/jpeg', 'image/jpg', 'image/png'];
|
||||
|
||||
if (file && !mimeTypesValid.includes(file.mimetype)) {
|
||||
throw new HttpException(`O Arquivo ${file.fieldname} deve ser jpeg, jpg, ou png`, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
@Get('/:id/theme')
|
||||
@ApiOkResponse({ type: CustomerThemeResponse })
|
||||
async getCustomerTheme(@Param('id') id: string) {
|
||||
this.logger.info('getCustomerTheme with id' + id);
|
||||
|
||||
try {
|
||||
const data = await this.themeService.getThemeByCustomer(id);
|
||||
this.logger.info('Success - getCustomerTheme'+ JSON.stringify(data));
|
||||
if (data?.theme) return data;
|
||||
|
||||
return { theme: null };
|
||||
}catch (err) {
|
||||
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) {
|
||||
this.logger.error('Error - getCustomerTheme - Expect CUSTOMER.NOT_FOUND');
|
||||
throw new HttpException(err.details, HttpStatus.NOT_FOUND);
|
||||
} else {
|
||||
this.logger.error('Error - getCustomerTheme Unknown Error:' + err?.message);
|
||||
return { theme: null };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||
import { ClientsModule } from '@nestjs/microservices';
|
||||
import { ThemeController } from './theme.controller';
|
||||
import { ThemeService } from './theme.service';
|
||||
import { DucClient } from '../duc/client.config';
|
||||
|
||||
const ducClient = new DucClient();
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ClientsModule.register([
|
||||
ducClient.providerOptions,
|
||||
]),
|
||||
],
|
||||
controllers: [ThemeController],
|
||||
providers: [ThemeService, DadosferaLogger],
|
||||
exports: [ThemeService],
|
||||
})
|
||||
export class ThemeModule {}
|
||||
@@ -1,158 +0,0 @@
|
||||
import {
|
||||
OnModuleInit,
|
||||
Inject,
|
||||
Injectable,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
InternalServerErrorException,
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { firstValueFrom, lastValueFrom, ReplaySubject } from 'rxjs';
|
||||
import { Link } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/entities';
|
||||
import { DucClient } from '../duc/client.config';
|
||||
import { ClientGrpc } from '@nestjs/microservices';
|
||||
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
|
||||
import { CustomerUpdateRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
|
||||
import { ThemeProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
|
||||
import { ThemeRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
|
||||
import { CustomerThemeRequest, CustomerThemeResponse } from './dtos/customers';
|
||||
import DadosferaLogger from '@dadosfera/dadosfera-logs';
|
||||
import { resolve } from 'path';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
type Files = {
|
||||
logo: Express.Multer.File,
|
||||
logoLogin: Express.Multer.File,
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ThemeService implements OnModuleInit {
|
||||
private themeService: ThemeProtoService;
|
||||
private logger: DadosferaLogger;
|
||||
|
||||
constructor(
|
||||
@Inject(DucClient.name) private readonly grpcClient: ClientGrpc,
|
||||
@Inject(DadosferaLogger)
|
||||
dadosferaLogger: DadosferaLogger,
|
||||
) {
|
||||
this.logger = dadosferaLogger.logger;
|
||||
}
|
||||
|
||||
onModuleInit() {
|
||||
this.themeService = this.grpcClient.getService<ThemeProtoService>(
|
||||
ProtoServices.ThemeProtoService,
|
||||
);
|
||||
}
|
||||
|
||||
async createThemeByCustomer(id: string, theme: CustomerThemeRequest & Files) {
|
||||
if (!id) {
|
||||
this.logger.error('Error - saveCustomertheme - not found id:' + id);
|
||||
throw new HttpException(null, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
const customerThemeRequest$ = new ReplaySubject<ThemeRequest>();
|
||||
|
||||
customerThemeRequest$.next({
|
||||
customerId: id,
|
||||
displayName: theme.displayName,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
textColor: theme.textColor,
|
||||
isMetadata: true,
|
||||
filename: '',
|
||||
chunk: Buffer.alloc(0)
|
||||
})
|
||||
|
||||
if(theme.logo) {
|
||||
await this.sendFile(theme.logo, customerThemeRequest$);
|
||||
}
|
||||
|
||||
if(theme.logoLogin) {
|
||||
await this.sendFile(theme.logoLogin, customerThemeRequest$);
|
||||
}
|
||||
customerThemeRequest$.complete();
|
||||
|
||||
const stream = this.themeService.CustomerCreateTheme(customerThemeRequest$);
|
||||
|
||||
return lastValueFrom(stream);
|
||||
}
|
||||
|
||||
async getThemeByCustomer(id: string): Promise<CustomerThemeResponse> {
|
||||
if (!id) {
|
||||
this.logger.error('Error - getCustomerTheme - not found id:' + id);
|
||||
throw new HttpException(null, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
const { theme } = await firstValueFrom(
|
||||
this.themeService.CustomerGetTheme({
|
||||
id
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
theme
|
||||
}
|
||||
}
|
||||
|
||||
private async sendFile(file: Express.Multer.File, stream$: ReplaySubject<ThemeRequest>) {
|
||||
const chunkSize = 4 * 1024 * 1024;
|
||||
const bufferStream = new CustomBufferStream(file.buffer, chunkSize);
|
||||
const parseMimitypeForExtension = {
|
||||
'image/jpeg': '.jpeg',
|
||||
'image/jpg': '.jpg',
|
||||
'image/png': '.png',
|
||||
'image/svg+xml': '.svg',
|
||||
}
|
||||
|
||||
const extension = parseMimitypeForExtension[file.mimetype];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
bufferStream.on('data', (chunk) => {
|
||||
const filename = file.fieldname.concat(extension);
|
||||
stream$.next({
|
||||
customerId: '',
|
||||
displayName: '',
|
||||
backgroundColor: '',
|
||||
textColor: '',
|
||||
isMetadata: false,
|
||||
filename: filename,
|
||||
chunk: chunk
|
||||
});
|
||||
});
|
||||
|
||||
bufferStream.on('end', () => {
|
||||
resolve(file.filename)
|
||||
});
|
||||
|
||||
bufferStream.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CustomBufferStream extends Readable {
|
||||
buffer: Buffer;
|
||||
offset: number;
|
||||
chunkSize: number;
|
||||
|
||||
constructor(buffer: Buffer, chunkSize: number) {
|
||||
super({ highWaterMark: chunkSize }); // Configura o tamanho do chunk
|
||||
this.buffer = buffer;
|
||||
this.offset = 0;
|
||||
this.chunkSize = chunkSize;
|
||||
}
|
||||
|
||||
_read() {
|
||||
if (this.offset < this.buffer.length) {
|
||||
const end = Math.min(this.offset + this.chunkSize, this.buffer.length);
|
||||
|
||||
const copiedBuf = Uint8Array.prototype.slice.call(this.buffer);
|
||||
const chunk = copiedBuf.slice(this.offset, end);
|
||||
this.offset = end;
|
||||
this.push(chunk);
|
||||
} else {
|
||||
this.push(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Info } from '@dadosfera/protospack/dist/lib/interfaces';
|
||||
import { Info } from 'protospack/dist/lib/interfaces';
|
||||
|
||||
export interface ICreateTransformationsRequest {
|
||||
transformations: Transformation[];
|
||||
|
||||
@@ -6,9 +6,7 @@ import {
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
import { Transformation } from '@dadosfera/protospack-v2';
|
||||
|
||||
const isLocalConnection =
|
||||
process.env.INFACTORY_URL.startsWith('in-factory:') ||
|
||||
process.env.INFACTORY_URL.includes('0.0.0.0');
|
||||
const isLocalConnection = !!process.env.INFACTORY_URL?.includes('0.0.0.0');
|
||||
|
||||
export class TransformationsClientConfiguration {
|
||||
public name = 'TransformationsClientConfiguration';
|
||||
|
||||
@@ -152,7 +152,9 @@ export class UsersController {
|
||||
});
|
||||
this.logger.info('createUser', { user });
|
||||
this.userService.setLanguage(language);
|
||||
|
||||
this.logger.info(`user: ${user}`);
|
||||
this.logger.info(`body: ${body}`);
|
||||
this.logger.info(`metadata: ${metadata}`);
|
||||
return await this.userService.createUser(body, metadata);
|
||||
}
|
||||
|
||||
@@ -171,7 +173,9 @@ export class UsersController {
|
||||
access_token: user.access_token,
|
||||
language,
|
||||
});
|
||||
|
||||
this.logger.info(`user: ${user}`);
|
||||
this.logger.info(`body: ${body}`);
|
||||
this.logger.info(`metadata: ${metadata}`);
|
||||
return await this.userService.batchCreateUser(body, metadata);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import getEnv from './getEnv';
|
||||
|
||||
export function getFrontendUrl(path?: string): URL {
|
||||
const frontendUrl = new URL('https://app.dadosfera.ai');
|
||||
if (path) frontendUrl.pathname = path;
|
||||
|
||||
const env = getEnv();
|
||||
if (process.env.ENV === 'local') {
|
||||
frontendUrl.protocol = 'http';
|
||||
frontendUrl.host = 'localhost:4200';
|
||||
return frontendUrl;
|
||||
}
|
||||
|
||||
if (process.env.CLOUD_ENVIRONMENT === 'mgc')
|
||||
frontendUrl.host = 'app.dadosfera.com';
|
||||
|
||||
if (env !== 'prd')
|
||||
frontendUrl.host = frontendUrl.host.replace('app.', `app.${env}.`);
|
||||
return frontendUrl;
|
||||
}
|
||||
Reference in New Issue
Block a user