diff --git a/.github/workflows/deploy-manually.yml b/.github/workflows/deploy-manually.yml index 2ce4395..a845884 100644 --- a/.github/workflows/deploy-manually.yml +++ b/.github/workflows/deploy-manually.yml @@ -144,8 +144,7 @@ jobs: docker system df api_docs: needs: [extract_environment, semantic_release, deploy] - runs-on: - [self-hosted, "${{ needs.extract_environment.outputs.environment }}"] + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 @@ -175,12 +174,16 @@ jobs: - name: Generate API docs env: - ENV: ${{ needs.extract_environment.outputs.environment }} 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 diff --git a/docsfera.json b/docsfera.json index cfe4b05..2042ccd 100644 --- a/docsfera.json +++ b/docsfera.json @@ -5,6 +5,265 @@ "schema": { "openapi": "3.0.0", "paths": { + "/auth/sign-in": { + "post": { + "operationId": "AuthController_signIn", + "parameters": [ + { + "name": "Dadosfera-Lang", + "required": true, + "in": "header", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthSignInReq" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthSignInRes" + } + } + } + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/sso/snowflake": { + "post": { + "operationId": "AuthController_snowflakeSignIn", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/refresh-access-token": { + "post": { + "operationId": "AuthController_refreshAccessToken", + "parameters": [ + { + "name": "Dadosfera-Lang", + "required": true, + "in": "header", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/change-password": { + "post": { + "operationId": "AuthController_changePassword", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/reset-password": { + "post": { + "operationId": "AuthController_resetPassword", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/verify-reset-password-code": { + "post": { + "operationId": "AuthController_verifyResetPasswordCode", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/confirm-reset-password": { + "post": { + "operationId": "AuthController_confirmResetPassword", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/enable-totp": { + "post": { + "operationId": "AuthController_enableTotpMFA", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/disable-totp": { + "post": { + "operationId": "AuthController_disableTotpMFA", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/dismiss-totp": { + "post": { + "operationId": "AuthController_dismissTotpMFA", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/verify-totp": { + "post": { + "operationId": "AuthController_verifyTotp", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Auth" + ] + } + }, "/connectors": { "post": { "operationId": "ConnectorController_uploadConnector", @@ -385,255 +644,6 @@ ] } }, - "/auth/sso/snowflake": { - "post": { - "operationId": "AuthController_snowflakeSignIn", - "parameters": [], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/sign-in": { - "post": { - "operationId": "AuthController_signIn", - "parameters": [ - { - "name": "Dadosfera-Lang", - "required": true, - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/refresh-access-token": { - "post": { - "operationId": "AuthController_refreshAccessToken", - "parameters": [ - { - "name": "Dadosfera-Lang", - "required": true, - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/change-password": { - "post": { - "operationId": "AuthController_changePassword", - "parameters": [], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/reset-password": { - "post": { - "operationId": "AuthController_resetPassword", - "parameters": [], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/verify-reset-password-code": { - "post": { - "operationId": "AuthController_verifyResetPasswordCode", - "parameters": [], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/confirm-reset-password": { - "post": { - "operationId": "AuthController_confirmResetPassword", - "parameters": [], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/enable-totp": { - "post": { - "operationId": "AuthController_enableTotpMFA", - "parameters": [], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/disable-totp": { - "post": { - "operationId": "AuthController_disableTotpMFA", - "parameters": [], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/dismiss-totp": { - "post": { - "operationId": "AuthController_dismissTotpMFA", - "parameters": [], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "tags": [ - "Auth" - ] - } - }, - "/auth/verify-totp": { - "post": { - "operationId": "AuthController_verifyTotp", - "parameters": [], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - }, - "tags": [ - "Auth" - ] - } - }, "/permissions": { "get": { "operationId": "PermissionsController_getPublicPermissions", @@ -3274,6 +3284,17 @@ "Productboard" ] } + }, + "/trackEvent/{id}": { + "post": { + "operationId": "MixpanelController_trackEvent", + "parameters": [], + "responses": { + "201": { + "description": "" + } + } + } } }, "info": { @@ -3293,6 +3314,176 @@ } }, "schemas": { + "AuthSignInReq": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "totp": { + "type": "string" + } + }, + "required": [ + "username", + "password" + ] + }, + "AuthCustomer": { + "type": "object", + "properties": { + "modules": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tier": { + "type": "string" + } + }, + "required": [ + "modules", + "id", + "name", + "tier" + ] + }, + "AuthUser": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "username": { + "type": "string" + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "username", + "createdAt" + ] + }, + "AuthTokens": { + "type": "object", + "properties": { + "accessToken": { + "type": "string" + }, + "refreshToken": { + "type": "string" + }, + "termsOfUseToken": { + "type": "string" + }, + "idToken": { + "type": "string", + "deprecated": true + } + }, + "required": [ + "accessToken", + "refreshToken", + "idToken" + ] + }, + "TermsOfUse": { + "type": "object", + "properties": { + "version": { + "type": "number" + }, + "publicUrl": { + "type": "string" + }, + "enforceDate": { + "type": "string" + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "version", + "publicUrl", + "enforceDate", + "createdAt" + ] + }, + "TermsOfUseStatus": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "pending", + "required", + "ok" + ] + }, + "lastSigned": { + "$ref": "#/components/schemas/TermsOfUse" + }, + "next": { + "$ref": "#/components/schemas/TermsOfUse" + } + }, + "required": [ + "status" + ] + }, + "AuthSignInRes": { + "type": "object", + "properties": { + "permissions": { + "type": "array", + "items": { + "type": "string" + } + }, + "mfaStatus": { + "type": "string", + "enum": [ + "pending", + "none", + "totp" + ] + }, + "customer": { + "$ref": "#/components/schemas/AuthCustomer" + }, + "user": { + "$ref": "#/components/schemas/AuthUser" + }, + "tokens": { + "$ref": "#/components/schemas/AuthTokens" + }, + "termsOfUse": { + "$ref": "#/components/schemas/TermsOfUseStatus" + } + }, + "required": [ + "permissions", + "mfaStatus" + ] + }, "CreateConnectorDto": { "type": "object", "properties": { diff --git a/package-lock.json b/package-lock.json index 964b3fc..f8c9084 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "dependencies": { "@aws-sdk/client-secrets-manager": "^3.112.0", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4", - "@dadosfera/protospack-v2": "3.19.2", + "@dadosfera/protospack-v2": "3.19.4", "@grpc/grpc-js": "^1.6.7", "@grpc/proto-loader": "^0.6.13", "@nestjs/common": "^8.4.7", @@ -33,6 +33,7 @@ "helmet": "^5.1.0", "jsonwebtoken": "^8.5.1", "jwk-to-pem": "^2.0.5", + "mixpanel": "^0.17.0", "passport": "^0.6.0", "passport-facebook": "^3.0.0", "passport-forcedotcom": "^0.1.5", @@ -1726,9 +1727,9 @@ } }, "node_modules/@dadosfera/protospack-v2": { - "version": "3.19.2", - "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.19.2.tgz", - "integrity": "sha512-0+5V4zo7x36Y9xojYnktTllm41uhAttEFTT13i2sBXn4Cb6xSCX5rOyMHwRHb4TOHTpWvaO58lGjP/i0x+lpEg==", + "version": "3.19.4", + "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.19.4.tgz", + "integrity": "sha512-Vb6bdFgQKYHf0kDK9kuMtN+nt3nkaMhJZ3wNVaUmQNxsnpGyARWBL29R2n+YnxS8g23tRwuj331sjg+DY0Fnhg==", "dependencies": { "@grpc/grpc-js": "^1.6.7", "rxjs": "^7.5.5", @@ -3543,7 +3544,6 @@ }, "node_modules/agent-base": { "version": "6.0.2", - "dev": true, "license": "MIT", "dependencies": { "debug": "4" @@ -8205,6 +8205,29 @@ "version": "1.2.6", "license": "MIT" }, + "node_modules/mixpanel": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/mixpanel/-/mixpanel-0.17.0.tgz", + "integrity": "sha512-DY5WeOy/hmkPrNiiZugJpWR0iMuOwuj1a3u0bgwB2eUFRV6oIew/pIahhpawdbNjb+Bye4a8ID3gefeNPvL81g==", + "dependencies": { + "https-proxy-agent": "5.0.0" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/mixpanel/node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "license": "MIT", @@ -12307,9 +12330,9 @@ } }, "@dadosfera/protospack-v2": { - "version": "3.19.2", - "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.19.2.tgz", - "integrity": "sha512-0+5V4zo7x36Y9xojYnktTllm41uhAttEFTT13i2sBXn4Cb6xSCX5rOyMHwRHb4TOHTpWvaO58lGjP/i0x+lpEg==", + "version": "3.19.4", + "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.19.4.tgz", + "integrity": "sha512-Vb6bdFgQKYHf0kDK9kuMtN+nt3nkaMhJZ3wNVaUmQNxsnpGyARWBL29R2n+YnxS8g23tRwuj331sjg+DY0Fnhg==", "requires": { "@grpc/grpc-js": "^1.6.7", "rxjs": "^7.5.5", @@ -13556,7 +13579,6 @@ }, "agent-base": { "version": "6.0.2", - "dev": true, "requires": { "debug": "4" } @@ -16720,6 +16742,25 @@ "minimist": { "version": "1.2.6" }, + "mixpanel": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/mixpanel/-/mixpanel-0.17.0.tgz", + "integrity": "sha512-DY5WeOy/hmkPrNiiZugJpWR0iMuOwuj1a3u0bgwB2eUFRV6oIew/pIahhpawdbNjb+Bye4a8ID3gefeNPvL81g==", + "requires": { + "https-proxy-agent": "5.0.0" + }, + "dependencies": { + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + } + } + }, "mkdirp": { "version": "0.5.6", "requires": { diff --git a/package.json b/package.json index 357f9ed..03bfb2a 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "start": "nest start", "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", + "start:prod": "npm run build && node dist/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "test": "jest", "test:watch": "jest --watch", @@ -28,7 +28,7 @@ "dependencies": { "@aws-sdk/client-secrets-manager": "^3.112.0", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4", - "@dadosfera/protospack-v2": "3.19.2", + "@dadosfera/protospack-v2": "3.19.4", "@grpc/grpc-js": "^1.6.7", "@grpc/proto-loader": "^0.6.13", "@nestjs/common": "^8.4.7", @@ -49,6 +49,7 @@ "helmet": "^5.1.0", "jsonwebtoken": "^8.5.1", "jwk-to-pem": "^2.0.5", + "mixpanel": "^0.17.0", "passport": "^0.6.0", "passport-facebook": "^3.0.0", "passport-forcedotcom": "^0.1.5", diff --git a/src/app.module.ts b/src/app.module.ts index 31bf962..d736888 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -24,6 +24,7 @@ import { CatalogModule } from './modules/catalog/catalog.module'; import { ConfigModule } from '@nestjs/config'; import { PipelinesV2Module } from './modules/pipelinesV2/pipelines.module'; import { ProductboardModule } from './modules/productboard/productboard.module'; +import { MixpanelModule } from './modules/mixpanel/mixpanel.module'; @Module({ controllers: [], @@ -38,8 +39,8 @@ import { ProductboardModule } from './modules/productboard/productboard.module'; ConfigModule.forRoot({ isGlobal: true, }), - ConnectorModule, AuthModule, + ConnectorModule, PermissionsModule, TermsOfUseModule, ConnectionModule, @@ -56,6 +57,7 @@ import { ProductboardModule } from './modules/productboard/productboard.module'; OauthModule, CatalogModule, ProductboardModule, + MixpanelModule, ], }) export class AppModule {} diff --git a/src/authentication/permissions.enum.ts b/src/authentication/permissions.enum.ts index 5e5da45..a4a95c8 100644 --- a/src/authentication/permissions.enum.ts +++ b/src/authentication/permissions.enum.ts @@ -449,11 +449,11 @@ export const PERMISSIONS_GROUPS = { }, }, - ORCHEST: { + MODULES: { title: { - 'pt-br': 'Analisar | Inteligência', - 'en-us': 'Analyze | Intelligence', - 'es-es': 'Analizar | Inteligencia', + 'pt-br': 'Módulos da Dadosfera', + 'en-us': 'Dadosfera Modules', + 'es-es': 'Módulos de la Dadosfera', }, permissions: { INTELLIGENCE: { @@ -461,9 +461,9 @@ export const PERMISSIONS_GROUPS = { claim: 'intelligence:open', usage: PermissionUsages.INTERNAL, name: { - 'pt-br': 'Acessar Orchest', - 'en-us': 'Access Orchest', - 'es-s': 'Acceso Orchest', + 'pt-br': 'Acessar Módulo de Inteligência', + 'en-us': 'Access Intelligence Module', + 'es-s': 'Acceder Módulo de Inteligencia', }, }, APP_BUILDER: { @@ -473,7 +473,7 @@ export const PERMISSIONS_GROUPS = { name: { 'pt-br': 'Acessar App Builder', 'en-us': 'Access App Builder', - 'es-es': 'Acceso App Builder', + 'es-es': 'Acceder App Builder', }, }, MACHINE_LEARNING: { @@ -483,7 +483,17 @@ export const PERMISSIONS_GROUPS = { name: { 'pt-br': 'Acessar Machine Learning', 'en-us': 'Access Machine Learning', - 'es-es': 'Acceso Machine Learning', + 'es-es': 'Acceder Machine Learning', + }, + }, + PROCESS: { + seqid: 43, + claim: 'process:open', + usage: PermissionUsages.INTERNAL, + name: { + 'pt-br': 'Acessar Módulo de Processamento', + 'en-us': 'Access Processing Module', + 'es-es': 'Acceder Módulo de Tramitación', }, }, }, diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 4e4714c..503dc64 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -10,7 +10,6 @@ import { } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { - AuthSignInRequest, AuthRefreshAccessTokenRequest, AuthChangePasswordRequest, AuthResetPasswordRequest, @@ -28,6 +27,7 @@ import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; import { GrpcToHttpExceptionFilter } from '../../error/grpc-to-http-exception.filter'; import { RequestUser, User } from 'src/authentication/user.decorator'; import { Metadata } from '@grpc/grpc-js'; +import { AuthSignInReq, AuthSignInRes } from './dtos/login'; @ApiTags('Auth') @UseFilters(new GrpcToHttpExceptionFilter()) @@ -43,6 +43,18 @@ export class AuthController { this.logger = dadosferaLogger.logger; } + @Post('sign-in') + @HttpCode(HttpStatus.OK) + async signIn( + @Body() { username, password, totp }: AuthSignInReq, + @Headers('Dadosfera-Lang') language: string, + ): Promise { + this.logger.info('/auth - SignIn'); + const metadata = new Metadata(); + metadata.add('language', language || 'pt-br'); + return this.authClient.signIn({ username, password, totp }, metadata); + } + @Post('/sso/snowflake') @RequireAllPermissions(PERMISSIONS_GROUPS.SNOWFLAKE.permissions.OPEN) @HttpCode(HttpStatus.OK) @@ -58,18 +70,6 @@ export class AuthController { }); } - @Post('sign-in') - @HttpCode(HttpStatus.OK) - async signIn( - @Body() { username, password, totp }: AuthSignInRequest, - @Headers('Dadosfera-Lang') language: string, - ) { - this.logger.info('/auth - SignIn'); - const metadata = new Metadata(); - metadata.add('language', language || 'pt-br'); - return this.authClient.signIn({ username, password, totp }, metadata); - } - @Post('refresh-access-token') @HttpCode(HttpStatus.OK) async refreshAccessToken( diff --git a/src/modules/auth/dtos/login.ts b/src/modules/auth/dtos/login.ts index cae27fd..32e1885 100644 --- a/src/modules/auth/dtos/login.ts +++ b/src/modules/auth/dtos/login.ts @@ -1,4 +1,8 @@ -import { ApiProperty } from '@nestjs/swagger'; +import { + AuthSignInRequest, + AuthSignInResponse, +} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; export class LoginDto { @ApiProperty() @@ -10,3 +14,86 @@ export class LoginDto { @ApiProperty({ required: false }) totp: string; } +export class AuthTokens { + @ApiProperty() + accessToken: string; + @ApiProperty() + refreshToken: string; + @ApiPropertyOptional() + termsOfUseToken: string; + @ApiProperty({ deprecated: true }) + idToken: string; +} +export enum MfaStatuses { + PENDING = 'pending', + NONE = 'none', + TOTP = 'totp', +} + +export enum TouStatuses { + PENDING = 'pending', + REQUIRED = 'required', + OK = 'ok', +} +export class TermsOfUse { + @ApiProperty() + version: number; + @ApiProperty() + publicUrl: string; + @ApiProperty() + enforceDate: string; + @ApiProperty() + createdAt: string; +} +export class TermsOfUseStatus { + @ApiProperty({ enum: TouStatuses }) + status: string; + @ApiPropertyOptional() + lastSigned?: TermsOfUse; + @ApiPropertyOptional() + next?: TermsOfUse; +} +export class AuthUser { + @ApiProperty() + id: string; + @ApiProperty() + name: string; + @ApiProperty() + username: string; + @ApiProperty() + createdAt: string; +} +export class AuthCustomer { + @ApiProperty() + modules: string[]; + @ApiProperty() + id: string; + @ApiProperty() + name: string; + @ApiProperty() + tier: string; +} + +export class AuthSignInReq implements AuthSignInRequest { + @ApiProperty() + username: string; + @ApiProperty() + password: string; + @ApiPropertyOptional() + totp: string; +} + +export class AuthSignInRes implements AuthSignInResponse { + @ApiProperty() + permissions: string[]; + @ApiProperty({ enum: MfaStatuses }) + mfaStatus: string; + @ApiPropertyOptional() + customer?: AuthCustomer; + @ApiPropertyOptional() + user?: AuthUser; + @ApiPropertyOptional() + tokens?: AuthTokens; + @ApiPropertyOptional() + termsOfUse?: TermsOfUseStatus; +} diff --git a/src/modules/connector/client.service.ts b/src/modules/connector/client.service.ts index 2a2f155..eeb4cfe 100644 --- a/src/modules/connector/client.service.ts +++ b/src/modules/connector/client.service.ts @@ -84,7 +84,7 @@ export class ConnectorClientService implements OnModuleInit { }); const responseParsed = JSON.parse(res.message); const connectorErrors = responseParsed.items - .filter((i) => i.update._index === 'connector' && i.update.error) + .filter((i) => i.update._index === 'connectors' && i.update.error) .map((i) => ({ id: i.update._id, reason: i.update?.error, @@ -115,7 +115,8 @@ export class ConnectorClientService implements OnModuleInit { })); const connectorCreated = responseParsed.items .filter( - (i) => i.update._index === 'connector' && i.update.result === 'created', + (i) => + i.update._index === 'connectors' && i.update.result === 'created', ) .map((i) => ({ id: i.update._id, @@ -149,7 +150,8 @@ export class ConnectorClientService implements OnModuleInit { })); const connectorUpdated = responseParsed.items .filter( - (i) => i.update._index === 'connector' && i.update.result === 'updated', + (i) => + i.update._index === 'connectors' && i.update.result === 'updated', ) .map((i) => ({ id: i.update._id, @@ -183,7 +185,7 @@ export class ConnectorClientService implements OnModuleInit { })); const connectorNoop = responseParsed.items .filter( - (i) => i.update._index === 'connector' && i.update.result === 'noop', + (i) => i.update._index === 'connectors' && i.update.result === 'noop', ) .map((i) => ({ id: i.update._id, diff --git a/src/modules/mixpanel/mixpanel.controller.ts b/src/modules/mixpanel/mixpanel.controller.ts new file mode 100644 index 0000000..e7005ee --- /dev/null +++ b/src/modules/mixpanel/mixpanel.controller.ts @@ -0,0 +1,31 @@ +import { Body, Controller, Inject, Param, Post } from '@nestjs/common'; +import { init } from 'mixpanel'; +import { RequestUser, User } from 'src/authentication/user.decorator'; + +@Controller('trackEvent') +export class MixpanelController { + constructor( + @Inject('MIXPANEL_TOKEN') + private readonly mixpanelToken: string, + ) {} + @Post(':id') + async trackEvent(@Param('id') id, @Body() body, @User() user: RequestUser) { + delete body.info; + const mixpanel = init(this.mixpanelToken); + + await mixpanel.people.set(user.username, { + $name: user.username, + $email: user.username, + customer_name: user.customer_name, + }); + + await mixpanel.track(id, { + distinct_id: user.username, + customer: user.customer_name, + env: process.env.ENV, + ...body, + }); + + return { id, body, user: user.username }; + } +} diff --git a/src/modules/mixpanel/mixpanel.module.ts b/src/modules/mixpanel/mixpanel.module.ts new file mode 100644 index 0000000..41b4a68 --- /dev/null +++ b/src/modules/mixpanel/mixpanel.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { getSecreteFromSecreteManager } from 'src/utils/SecretManager'; +import { MixpanelController } from './mixpanel.controller'; + +@Module({ + controllers: [MixpanelController], + providers: [ + { + provide: 'MIXPANEL_TOKEN', + useValue: getSecreteFromSecreteManager( + `${process.env.ENV}/root/mixpanel_token`, + ), + }, + ], +}) +export class MixpanelModule {} diff --git a/src/modules/permissions/permissions.controller.ts b/src/modules/permissions/permissions.controller.ts index e73b17f..0383d9f 100644 --- a/src/modules/permissions/permissions.controller.ts +++ b/src/modules/permissions/permissions.controller.ts @@ -1,14 +1,8 @@ import { Controller, Get, Headers, Inject } from '@nestjs/common'; import { ApiHeader, ApiOkResponse, ApiTags } from '@nestjs/swagger'; import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; -import { - Authenticated, - RequireAllPermissions, -} from 'src/authentication/authentication.decorator'; -import { - PERMISSIONS_GROUPS, - PermissionUsages, -} from 'src/authentication/permissions.enum'; +import { Authenticated } from 'src/authentication/authentication.decorator'; +import { PermissionUsages } from 'src/authentication/permissions.enum'; import { RequestUser, User } from 'src/authentication/user.decorator'; import { LanguageEnum } from 'src/utils/languages.enum'; import { GetPublicPermissionsRes } from './dto/entities'; diff --git a/src/modules/pipelinesV2/pipelines.service.ts b/src/modules/pipelinesV2/pipelines.service.ts index 755bb52..d3c1a8e 100644 --- a/src/modules/pipelinesV2/pipelines.service.ts +++ b/src/modules/pipelinesV2/pipelines.service.ts @@ -18,6 +18,7 @@ import { InputsService } from '../inputs/inputs.service'; import { RequestUser } from 'src/authentication/user.decorator'; import { TransformationsService } from '../transformations/transformations.service'; import ErrorBuilder from 'src/utils/ErrorBuilder'; +import { getObjValueFromPath } from 'src/utils/ObjValueFromPath'; export class PipelinesService implements OnModuleInit { logger: DadosferaLogger; @@ -209,16 +210,14 @@ export class PipelinesService implements OnModuleInit { const connector = JSON.parse(res.connector); const config_controls = connector.config_controls || []; - for (const key in pipelineProperties) { - const i = config_controls - .map((cc) => cc.name) - .indexOf(`properties.${key}`); - if (i > -1) { - config_controls[i] = { - ...config_controls[i], - default_value: pipelineProperties[key], - }; - } + for (const config_control of config_controls) { + const path: string = config_control.name; + const default_value = getObjValueFromPath({ + object: { properties: pipelineProperties }, + path, + }); + if (default_value != undefined) + config_control.default_value = default_value; } return { diff --git a/src/utils/ObjValueFromPath.ts b/src/utils/ObjValueFromPath.ts new file mode 100644 index 0000000..43674bd --- /dev/null +++ b/src/utils/ObjValueFromPath.ts @@ -0,0 +1,10 @@ +export function getObjValueFromPath(data: { object: T; path: string }): T { + const { object, path } = data; + const keys = path.split('.'); + + let value = JSON.parse(JSON.stringify(object)); + for (const key of keys) { + if (value != undefined) value = value[key]; + } + return value; +} diff --git a/tsconfig.build.json b/tsconfig.build.json index 64f86c6..ca83a6b 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,4 +1,4 @@ { "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] + "exclude": ["node_modules", "test", "dist", "**/*spec.ts", "jest.config.ts"] }