Compare commits

...
537 Commits
Author SHA1 Message Date
Gabriel Amorim ee8c5e7a93 Merge pull request #206 from dadosfera/feat/docs-refactor
Feat: docs refactor
2023-02-17 14:36:19 -03:00
Gabriel Rosa 7ecb50315f Update action 2023-02-17 11:01:29 -03:00
Gabriel Rosa 84b3370826 Update action 2023-02-17 11:01:08 -03:00
Gabriel Rosa aa9e0536a7 FIX: tests 2023-02-17 09:55:10 -03:00
Gabriel Rosa 544d13fac5 FIX: readme improved 2023-02-16 17:01:49 -03:00
Gabriel Rosa a4ff07a285 FIX: removed connection test from external docs 2023-02-16 12:07:47 -03:00
Gabriel Rosa 28578d3aa8 FIX: better query params on catalog get all 2023-02-15 16:46:32 -03:00
Gabriel Rosa b8ea729a33 FIX: add external docs to docsfera 2023-02-15 14:16:28 -03:00
Gabriel Rosa b383070b68 Internal docs 2023-02-15 14:07:26 -03:00
Gabriel Rosa 87882b5afc FIX: add multiple servers 2023-02-15 13:14:53 -03:00
Gabriel Rosa 6a14c2b459 FIX: hide internal endpoints 2023-02-15 13:13:43 -03:00
Gabriel Rosa f0bfb6ba8e FIX: new semantic release 2023-02-14 16:57:45 -03:00
Gabriel Rosa 99288fa805 FIX: new sematic release version 2023-02-14 16:55:39 -03:00
Gabriel Rosa 35cc0094b3 FIX: new sematic release 2023-02-14 16:51:53 -03:00
Gabriel Rosa 98262bcba9 FIX: Run semantic release on manual deploy 2023-02-14 16:48:46 -03:00
Gabriel Rosa a2c9a462e4 Run semantic release on manual deploy 2023-02-14 16:46:54 -03:00
Gabriel Rosa c5c089e5d5 Better error handling and documentation on refresh token 2023-02-14 09:25:14 -03:00
Gabriel Rosa de06512249 FIX: changed language strategy to use new custom decorator 2023-02-14 08:56:23 -03:00
Gabriel Rosa 9058b80383 Merge branch 'main' into feat/docs-refactor 2023-02-13 12:16:12 -03:00
Gabriel Rosa be64c582e1 Inject permissions more safe 2023-02-13 12:03:28 -03:00
Gabriel Rosa d6b3e02890 FIX: automattically skip ssl when microservices connection are local 2023-02-13 10:30:50 -03:00
Gabriel Amorim d06910515b FIX: force deploy 2023-02-10 15:53:10 -03:00
Gabriel Amorim d1b328d481 Merge pull request #204 from dadosfera/feat/oauth-login
Feat/oauth login
2023-02-10 14:58:20 -03:00
Gabriel Rosa fbc0d2eede FIX: moved decorators to a separate folder 2023-02-10 14:23:27 -03:00
Gabriel Rosa 945955a71c Merge branch 'main' into feat/oauth-login 2023-02-09 15:21:05 -03:00
Gabriel Rosa 9fcbeda470 FIX: deprecating old pipeline routes 2023-02-08 18:07:06 -03:00
Gabriel Rosa 8043635b1c FIX: add authentication to swagger 2023-02-08 15:23:49 -03:00
Gabriel Amorim cdba41dffa Merge pull request #203 from dadosfera/force-deploy
FIX: force deploy
2023-02-03 17:24:13 -03:00
Gabriel Rosa 5020e8913e FIX: force deploy 2023-02-03 17:21:53 -03:00
Gabriel Amorim 89369270b9 Merge pull request #202 from dadosfera/feat/embed-private
Feat/embed private
2023-02-03 17:14:14 -03:00
Gabriel Rosa 52f17725af new protospack version 2023-02-03 15:03:51 -03:00
Gabriel Rosa 6da213aebc starting module strategy 2023-02-03 11:14:15 -03:00
Gabriel Rosa a353a1b45c FIX: new permission 2023-02-02 14:00:24 -03:00
Gabriel Rosa b36ff624b5 FIX: add logic for fetching shared data_assets 2023-02-01 18:00:36 -03:00
Gabriel Rosa 03c09a525b DOCS: documenting oauth flow 2023-02-01 10:07:45 -03:00
Gabriel Amorim 9ea610a405 Merge pull request #201 from dadosfera/fix/oauth-facebook
FIX: add all scopes to facebook oauth
2023-01-31 16:08:15 -03:00
Gabriel Rosa cd97bcbac0 FIX: add all scopes to facebook oauth 2023-01-31 16:04:57 -03:00
Gabriel Rosa 52bf2bdef3 teste new docsfera cloud function url 2023-01-31 09:34:34 -03:00
Gabriel Rosa 161d1fa05d Added some logs 2023-01-30 16:35:05 -03:00
Gabriel Rosa 642bf462ad FIX: add fallback language to pt-br 2023-01-26 17:00:27 -03:00
Gabriel Rosa 6d61d74d0c FIX: set google callback 2023-01-25 09:51:22 -03:00
Gabriel Rosa 2bc060b13d FEAT: oauth sign in 2023-01-24 19:19:00 -03:00
Anderson Feitosa 28efdf95d9 Merge pull request #200 from dadosfera/feature/upload-to-s3-presigned
feature/upload-to-s3-presigned
2023-01-24 06:39:49 -03:00
Anderson Feitosa 1ef9fd4298 FEAT: update packages 2023-01-23 13:56:51 -03:00
Anderson Feitosa 5c24ec5560 FEAT: fix upload id 2023-01-20 10:05:36 -03:00
Anderson Feitosa 962e094a65 FEAT: add upload init multi part upload 2023-01-19 17:17:33 -03:00
Anderson Feitosa 131b166d8d REFACTOR: get data from body 2023-01-16 10:56:03 -03:00
Anderson Feitosa 5419f3690c FEAT: update endpoint to get signed url to upload csv file 2023-01-16 10:55:27 -03:00
Gabriel Amorim 6f3eb7965b Merge pull request #199 from dadosfera/fix/can-untoggle
FIX: canUntoggle logic
2023-01-12 18:10:23 -03:00
Gabriel Rosa 909f0d4a4e new protospack version 2023-01-12 17:59:02 -03:00
Gabriel Rosa 8c8fdf5f88 FIX: canUntoggle logic 2023-01-12 16:47:43 -03:00
Anderson Feitosa e9582a51c2 Merge pull request #198 from dadosfera/feature/mixpanel-first-name
feature/mixpanel-first-name
2023-01-12 12:28:24 -03:00
Anderson Feitosa 0590c20af8 FEAT: add more prefixed values 2023-01-12 12:03:04 -03:00
Anderson Feitosa 05eefe866b FEAT: add suffix on email 2023-01-12 12:02:16 -03:00
Anderson Feitosa fa023756c7 FEAT: replace dadosfera with empty string 2023-01-12 11:56:47 -03:00
Anderson Feitosa 7f967e5e75 FEAT: update hardcoded separator 2023-01-12 11:52:51 -03:00
Anderson Feitosa 2a0b17e45a FEAT: add more prefixed values 2023-01-12 11:49:32 -03:00
Anderson Feitosa b1f6d765b2 FEAT: add more prefixed values 2023-01-12 11:47:04 -03:00
Anderson Feitosa 3c9ed08515 FEAT: split email into fist name and last name 2023-01-12 11:40:14 -03:00
Gabriel Amorim c7ca8297c9 Merge pull request #197 from dadosfera/fix/reset-password-email
FIX: send language on reset password email
2023-01-10 20:29:58 -03:00
Gabriel Rosa d269e9efe4 FIX: send language on reset password email 2023-01-10 18:50:32 -03:00
Gabriel Amorim 61cc238891 Merge pull request #196 from dadosfera/feat/cognito-mailing
FIX: send language on create users and resend invite
2023-01-10 18:06:06 -03:00
Gabriel Rosa b5df916511 Merge branch 'main' into feat/cognito-mailing 2023-01-10 15:05:18 -03:00
Gabriel Amorim 95da106fb8 Merge pull request #195 from dadosfera/fix/customer-schedule-limit
fix/customer-schedule-limit
2023-01-06 18:29:57 -03:00
Gabriel Rosa 5730402312 update protospack 2023-01-06 18:04:16 -03:00
Gabriel Rosa c2ea8b69da FIX: customer scheduleLimit on login 2023-01-06 15:24:01 -03:00
Gabriel Rosa 14081bb9ca FIX: send language on create users and resend invite 2023-01-06 15:18:49 -03:00
Victor Radael 82d9024f24 Merge pull request #193 from dadosfera/feat/update-connection
Feat/update connection
2022-12-27 17:21:25 -03:00
Gabriel Rosa 53a6d3957a Merge branch 'main' into feat/update-connection 2022-12-27 17:01:32 -03:00
Anderson Feitosa 7dab555009 Merge pull request #194 from dadosfera/feature/download-logs
feature/download-logs
2022-12-27 16:41:11 -03:00
Anderson Feitosa 27dacf1257 FEAT: update protospack version 2022-12-27 12:24:15 -03:00
Anderson Feitosa f71bb523ff FEAT: add download logs api route 2022-12-27 12:23:03 -03:00
Victor Radael db55a8abde FEAT: Update OAuth Connection 2022-12-26 17:47:30 -03:00
Victor Radael 8f40b159d6 FEAT: MERGE main to deploy 2022-12-26 17:43:31 -03:00
Anderson Feitosa 5c6f36a760 Merge pull request #192 from dadosfera/feature/pipeline-object-to-data-asset
feature/pipeline-object-to-data-asset
2022-12-23 14:07:33 -03:00
Anderson Feitosa db4a2d9d2e FEAT: update protospack version 2022-12-22 17:43:54 -03:00
Anderson Feitosa 9a1b3e0bf4 FEAT: update protospack 2022-12-22 17:38:58 -03:00
Gabriel Rosa 67d9b3a06e Send metabaseUserId undefined to user update 2022-12-22 17:14:51 -03:00
Anderson Feitosa 1150d21763 FEAT: remove metabase user id 2022-12-22 17:02:03 -03:00
Anderson Feitosa b38e9f26c3 FEAT: add route to get data asset by pipeline and object 2022-12-22 17:00:56 -03:00
Anderson Feitosa 5a23923e53 DOCS: docsfera 2022-12-22 13:16:56 -03:00
Anderson Feitosa 3db7e99d9e FEAT: parse objects 2022-12-22 13:16:44 -03:00
Anderson Feitosa c85cd37f3a FEAT: add new api route to get pipeline objects 2022-12-22 13:08:08 -03:00
Victor Radael 190a63f0eb FEAT: Update OAuth Connection 2022-12-20 17:01:00 -03:00
Gabriel Amorim 0fc8c427a2 Merge pull request #191 from dadosfera/fix/refresh-token
Fix/refresh token
2022-12-16 09:40:56 -03:00
Gabriel Rosa 59c3ce7ed9 FIX: do not use User() on non-authenticated routes 2022-12-16 08:09:20 -03:00
Victor Radael c5c428d3d4 FIX: Refresh token 2022-12-15 19:57:52 -03:00
Victor Radael cda7f9a9b5 FEAT: Edit connection 2022-12-15 16:01:24 -03:00
Victor Radael bb3b71d5f0 Merge remote-tracking branch 'origin' into feat/upload-csv 2022-12-15 16:01:02 -03:00
Victor Radael e47459eb9a FEAT: Edit connection 2022-12-15 16:00:54 -03:00
Gabriel Amorim a2de729e0e Merge pull request #190 from dadosfera/feat/cognito-app-client
Feat/cognito app client
2022-12-14 13:20:29 -03:00
Gabriel Rosa e2cb02e51a new protospack version 2022-12-14 10:49:33 -03:00
Gabriel Rosa 8a0c91eca5 new protospack version 2022-12-14 10:02:02 -03:00
Gabriel Rosa 15302d80bc Merge branch 'main' into feat/cognito-app-client 2022-12-13 16:12:19 -03:00
Gabriel Rosa 5aa63154c5 new protospack version 2022-12-13 15:34:57 -03:00
Gabriel Rosa 48af897e79 FEAT: send customer_name on refresh token 2022-12-13 09:28:42 -03:00
Victor Radael 9eb61a0ac9 Merge pull request #189 from dadosfera/feat/upload-csv
FIX: Upload CSV
2022-12-12 17:41:44 -03:00
Victor Radael 4a9e5b0ffc FIX: Upload CSV 2022-12-12 17:04:16 -03:00
Victor Radael 23fe522cc1 Merge pull request #188 from dadosfera/feat/upload-csv
Feat/upload csv
2022-12-09 17:14:36 -03:00
Victor Radael ea8dfa2e12 FEAT: Deploy Merge 2022-12-09 17:10:50 -03:00
Anderson Feitosa 9477223bec Merge pull request #187 from dadosfera/hotfix/semantic-release
hotfix/semantic-release
2022-12-09 11:32:54 -03:00
Anderson Feitosa 519070bd82 FIX: change docs 2022-12-09 11:32:23 -03:00
Anderson Feitosa 99fc7e8a96 Merge pull request #186 from dadosfera/feature/verify-refresh-token
feature/verify-refresh-token
2022-12-09 11:25:44 -03:00
Anderson Feitosa 9fedd4bb80 Merge branch 'main' into feature/verify-refresh-token 2022-12-09 11:19:45 -03:00
Victor Radael ff5e735121 FEAT: Find a file pipeline 2022-12-09 08:29:27 -03:00
Gabriel Amorim c450d16445 Merge pull request #184 from dadosfera/feat/embed-data-asset
Feat/embed data asset
2022-12-07 16:26:49 -03:00
Gabriel Rosa 3acda53e8a npm audit fix 2022-12-07 16:17:21 -03:00
Gabriel Rosa a0f21b763a new protospack version 2022-12-07 16:14:57 -03:00
Gabriel Amorim c83d8ab1b2 Merge branch 'main' into feat/embed-data-asset 2022-12-07 15:48:04 -03:00
Gabriel Amorim 610d08fa11 Merge pull request #185 from dadosfera/fix/data-asset-docs
FIX: removed split on id on catalog POST docs route
2022-12-07 14:55:49 -03:00
Gabriel Rosa 21f68b466d FIX: removed split on id on catalog POST docs route 2022-12-07 14:49:58 -03:00
Anderson Feitosa 18b5dc980c FEAT: add verify access token path 2022-12-07 12:58:58 -03:00
Gabriel Rosa 6fdd73c58c removing treatment of undefined embed field 2022-12-07 08:36:21 -03:00
Victor Radael eaddca7eb3 FEAT: Creat a Upload CSV Pipeline 2022-12-06 18:06:32 -03:00
Gabriel Rosa 1c0377a4fa FIX: treat empty embed on update 2022-12-06 11:04:18 -03:00
Gabriel Rosa 64816e5578 FIX: treat empty embed 2022-12-06 10:07:30 -03:00
Gabriel Rosa f9064417ce docsfera.json 2022-12-06 09:11:32 -03:00
Gabriel Rosa 0b8bf9a535 Merge branch 'main' into feat/embed-data-asset 2022-12-06 09:09:43 -03:00
Victor Radael 98e865c48a FEAT: Create Upload CSV Pipeline 2022-12-05 20:35:35 -03:00
Victor Radael 2c207f068c FEAT: Create Upload CSV Pipeline 2022-12-05 20:34:04 -03:00
Gabriel Amorim 6f1e5d08c9 Merge pull request #182 from dadosfera/feat/view-table
Feat/view table
2022-12-05 18:15:05 -03:00
Gabriel Rosa b611c1eb9d Merge branch 'main' into feat/view-table 2022-12-05 18:13:36 -03:00
Gabriel Amorim d7062efc60 Merge pull request #183 from dadosfera/feat/rest-api
Feat/rest api
2022-12-05 15:22:05 -03:00
Gabriel Rosa 4a3dd98977 Merge branch 'main' into feat/rest-api 2022-12-05 13:58:22 -03:00
Gabriel Rosa 81d93ff103 FEAT: embed data asset 2022-12-05 13:22:23 -03:00
Victor Radael ea7ec52e1f FEAT: upload 50MB file 2022-12-01 18:55:48 -03:00
Victor Radael be228b7425 FEAT: upload 50MB file 2022-12-01 17:50:44 -03:00
Victor Radael 90edc6552a FEAT: Upload CSV 2022-12-01 14:57:44 -03:00
Victor Radael ed96609011 FEAT: Upload CSV 2022-12-01 14:56:41 -03:00
Gabriel Rosa d55563f62f FIX: removed split on data_asset id on DELETE route 2022-11-30 16:21:36 -03:00
Gabriel Rosa ba9575c53e FIX: return only owner username 2022-11-30 15:43:30 -03:00
Gabriel Rosa d88b154d43 FIX: new id strategy for data assets 2022-11-30 15:29:31 -03:00
Gabriel Amorim 06f4260b61 Merge pull request #181 from dadosfera/stg2
Stg2
2022-11-29 19:16:48 -03:00
Gabriel Rosa 64717c8d92 Merge branch 'main' into stg2 2022-11-29 14:41:11 -03:00
Anderson Feitosa e6e66c77e1 Merge pull request #180 from dadosfera/feature/update-pipeline-cron
feature/update-pipeline-cron
2022-11-29 14:04:31 -03:00
Gabriel Rosa 131e82b4c5 FIX: connection details improvements 2022-11-29 09:47:19 -03:00
Gabriel Rosa 32d37ecadf Removed dev deploy environment 2022-11-29 08:42:26 -03:00
Gabriel Rosa 4d667acb86 removed unused import 2022-11-25 18:21:58 -03:00
Gabriel Rosa 9d417ff61b FIX: treating new connectors upload reponse 2022-11-25 18:21:43 -03:00
Anderson Feitosa b01c4c6c25 UPGRADE: change protospack version 2022-11-25 13:00:09 -03:00
Anderson Feitosa 449a1abb63 FEAT: update auth guard to patch method 2022-11-25 12:11:51 -03:00
Anderson Feitosa 04ca17a69d FEAT: update protospack 2022-11-25 11:37:34 -03:00
Anderson Feitosa 34a0eb40a7 DOCS: docsfera patch 2022-11-23 14:58:17 -03:00
Anderson Feitosa 2df69fba84 FEAT: add log on update 2022-11-23 13:35:02 -03:00
Anderson Feitosa dcbcc5ff36 FEAT: add patch method on pipeline controller 2022-11-23 13:02:32 -03:00
Gabriel Rosa 703b5f36b3 Merge branch 'main' into feat/rest-api 2022-11-21 13:36:13 -03:00
Gabriel Rosa 54d10cb873 FIX: try to delete pipeline even if it does not exists in opensearch 2022-11-17 09:25:05 -03:00
Gabriel Amorim 569c3cf573 Merge pull request #179 from dadosfera/feat/incremental-table
Feat/incremental table
2022-11-16 16:52:35 -03:00
Gabriel Rosa c420ef62ed new protospack version 2022-11-16 16:46:38 -03:00
Alexsander Pereira 966ba00fec Merge pull request #178 from dadosfera/stg2
FEAT: Adding stg2
2022-11-16 15:47:49 -03:00
Alexsander Pereira 0aadf7fc40 FEAT: Adding stg2 2022-11-16 15:18:18 -03:00
Gabriel Rosa 3e3268cb26 FIX: New getAvailableEntities payload 2022-11-16 10:37:21 -03:00
Anderson Feitosa 09bd623b62 Merge pull request #177 from dadosfera/catalog-permissions
FIX: create data asset permission public
2022-11-14 16:19:09 -03:00
Gabriel Rosa 4bf9a5831a FIX: create data asset permission public 2022-11-14 16:15:57 -03:00
Anderson Feitosa 264077289b Merge pull request #176 from dadosfera/catalog-permissions
FIX: renamed catalog create permission
2022-11-14 15:52:04 -03:00
Gabriel Rosa 14c686cff4 FIX: renamed catalog create permission
- Catalog controller using permission decorator on every route
2022-11-14 15:40:38 -03:00
Gabriel Amorim 0e57667419 Merge pull request #175 from dadosfera/feat/modules-permissions
Feat/modules permissions
2022-11-11 12:38:04 -03:00
Gabriel Rosa ac7dc4905e FIX: changed PROCESS permisison group title 2022-11-11 12:35:04 -03:00
Gabriel Rosa 94282718ad Changed permission group name 2022-11-11 12:27:57 -03:00
Gabriel Rosa 7c228c5c53 FIX: set new module permissions public 2022-11-11 12:26:14 -03:00
Gabriel Amorim 14e0f3d2d3 Merge pull request #174 from dadosfera/fix/event-tracker
Fix/event tracker
2022-11-11 10:18:42 -03:00
Victor Radael 85ef0d7c74 Merge remote-tracking branch 'origin' into fix/event-tracker 2022-11-11 10:16:07 -03:00
Victor Radael e00b6481d0 FIX: Tracker 2022-11-11 10:15:49 -03:00
Victor Radael 8906c73d84 Revert "FIX: Event Tracker"
This reverts commit 3bc37db891.
2022-11-11 10:13:24 -03:00
Victor Radael 3bc37db891 FIX: Event Tracker 2022-11-11 10:12:20 -03:00
Gabriel Rosa 3c877b8b8d Merge branch 'main' into feat/rest-api 2022-11-10 15:14:45 -03:00
Gabriel Amorim fe44664970 Merge pull request #173 from dadosfera/fix/modules-permissions
FIX: new modules permissions
2022-11-10 15:13:01 -03:00
Gabriel Rosa 0503732c1e new protospack version 2022-11-10 14:43:46 -03:00
Gabriel Rosa fa0855d631 FIX: new modules permissions 2022-11-10 12:57:15 -03:00
Gabriel Amorim 38aee30f2a Merge pull request #172 from dadosfera/fix/pipeline-details-bug
Fix/pipeline details bug
2022-11-10 10:32:42 -03:00
Gabriel Rosa 19f5c947bd Set release version on swagger when deploying main 2022-11-10 10:12:14 -03:00
Gabriel Rosa 44aa0e6010 improved run prod command 2022-11-10 09:47:30 -03:00
Gabriel Rosa cf4e7da1d8 Typing login route 2022-11-10 09:46:22 -03:00
Gabriel Rosa 8bd7d54aa7 new protospack version 2022-11-10 09:16:02 -03:00
Gabriel Rosa a00a66e0d0 Merge branch 'main' into fix/pipeline-details-bug 2022-11-10 08:41:36 -03:00
Victor Radael f4c5d03269 Merge pull request #171 from dadosfera/ft/mixpanel-track
FEAT: Mixpanel Tracker
2022-11-10 08:35:55 -03:00
Victor Radael 060ba7da8a FEAT: Mixpanel Tracker 2022-11-09 20:46:40 -03:00
Victor Radael 5ec2c01b20 FEAT: Mixpanel Tracker 2022-11-09 20:37:39 -03:00
Gabriel Rosa 4a7e660a49 FIX: fixed condition of getObjValueFromPath 2022-11-09 20:06:23 -03:00
Gabriel Rosa abc6258492 FIX: fixed condition to set default value on pipeline details 2022-11-09 19:30:13 -03:00
Gabriel Rosa 7ee1215da2 Deploy docs using github hosted runner 2022-11-09 18:48:50 -03:00
Gabriel Rosa cd5886337b FIX: customer modules 2022-11-09 18:40:53 -03:00
Gabriel Rosa e6b0034805 Merge branch 'main' into feat/rest-api 2022-11-09 18:29:04 -03:00
Gabriel Rosa b9c1f784d3 FIX: treatingconnector upload response 2022-11-09 18:25:57 -03:00
Gabriel Rosa 5fb8dae312 FIX: pipeline details now showing correct default values on s3 pipelines 2022-11-09 17:35:18 -03:00
Gabriel Amorim 5072a54ef6 Merge pull request #170 from dadosfera/fix/revert-action-version
FIX: return semantic release action to previous version
2022-11-09 16:05:04 -03:00
Gabriel Rosa 718f5cb12a FIX: return sematic release action to previous version 2022-11-09 15:56:28 -03:00
Gabriel Amorim 4c41dd8029 Merge pull request #169 from dadosfera/fix/revert-action-version
FIX: remove `conventional-changelog-eslint` plugin
2022-11-09 15:41:08 -03:00
Gabriel Rosa fe8a0b88b0 FIX: remove conventional-changelog-eslint plugin 2022-11-09 15:39:37 -03:00
Gabriel Rosa 3edf5c2416 Merge branch 'main' into feat/rest-api 2022-11-09 15:03:09 -03:00
Gabriel Amorim cd67ca61cd Merge pull request #168 from dadosfera/fix/customer-modules
FIX: customer modules
2022-11-09 14:59:54 -03:00
Gabriel Rosa b9439685f3 FIX: new swagger info 2022-11-09 14:29:32 -03:00
Gabriel Rosa 04edce0c56 FIX: new protospack version 2022-11-09 14:08:05 -03:00
Gabriel Rosa f3e00a15c8 Remove unused imports 2022-11-09 14:06:30 -03:00
Gabriel Rosa 0422c7a369 Set branch name on swagger version 2022-11-09 14:06:12 -03:00
Gabriel Rosa f22d9370db test new upload documentation 2022-11-09 11:27:54 -03:00
Victor Radael ef098dd74d Merge pull request #167 from dadosfera/ft/create-data-asset
FEAT: Ft/create data asset
2022-11-08 19:48:48 -03:00
Victor Radael 2541960b44 FEAT: Create Data Asset 2022-11-08 19:32:26 -03:00
Victor Radael 2dc965ed90 FIX:delete and update data asset 2022-11-08 14:18:31 -03:00
Gabriel Rosa a9ee74724f FIX: better error handling 2022-11-08 12:11:05 -03:00
Victor Radael b2f1ce669d FIX: Save 2022-11-08 11:41:34 -03:00
Victor Radael 602d81e51f FIX: Save 2022-11-08 11:39:48 -03:00
Rodrigo Zamboni bbdb023dde Merge pull request #165 from dadosfera/feat/newCustomersInfo
Feat/new customers info
2022-11-07 16:05:33 -03:00
Gabriel Rosa 9cb4cd7093 FIX: fixed protospack version 2022-11-07 13:48:28 -03:00
Gabriel Rosa efc5870d87 FIX: fixed protospack version 2022-11-07 13:48:09 -03:00
Rodrigo Zamboni c3d33f8230 Merge branch 'main' into feat/newCustomersInfo 2022-11-07 12:38:13 -03:00
rodrigo.zamboni 0654e8fa04 FIX: updaetd protospack version 2022-11-07 12:35:51 -03:00
Gabriel Amorim 28ab892208 Merge pull request #164 from dadosfera/catalog-permissions
FIX: Catalog permissions
2022-11-07 10:44:02 -03:00
Gabriel Rosa d1522eb0ea FIX: new protospack version 2022-11-04 17:57:48 -03:00
Gabriel Rosa 6be9e06b22 FIX: new protospack version 2022-11-04 14:22:07 -03:00
Gabriel Rosa 7af261d56c New permissions for transformations 2022-11-04 12:03:15 -03:00
Gabriel Rosa 8fa924fdfb Changed delete description 2022-11-04 09:27:53 -03:00
Victor Radael 4b4bdfea61 FEAT: Create Data Asset 2022-11-03 17:18:20 -03:00
Gabriel Rosa d1064c852b rename workflow job 2022-11-03 12:03:25 -03:00
Gabriel Rosa 63338f5250 fixed deploy worflow file 2022-11-03 11:59:01 -03:00
Gabriel Rosa 5e1fb41930 FIX: identation 2022-11-03 11:57:52 -03:00
Gabriel Rosa a807da2414 test deploy 2022-11-03 11:48:46 -03:00
Gabriel Rosa 85f14fcf4b FIX: generate better summary on deploy 2022-11-03 11:28:09 -03:00
Gabriel Rosa d95898ce55 FIX: generate better summary on deploy 2022-11-03 11:18:02 -03:00
Gabriel Rosa 33075b0f85 FIX: generate better summary on deploy 2022-11-03 11:10:38 -03:00
Gabriel Rosa ca75f8f6e7 FIX: generate better summary on deploy 2022-11-03 11:00:55 -03:00
Gabriel Rosa af12a39959 FEAT: Deploy summary 2022-11-03 10:53:37 -03:00
Gabriel Rosa f77476b9ae FIX: echo text 2022-11-03 10:33:55 -03:00
Gabriel Rosa fb0c659cd1 FIX: permissions for catalog 2022-11-03 10:27:50 -03:00
Gabriel Rosa aea6815de7 Merge branch 'main' into catalog-permissions 2022-11-03 10:20:10 -03:00
Gabriel Amorim 0103f100f5 Merge pull request #163 from dadosfera/new-ecr-image
FIX: new ecr image
2022-11-01 15:59:49 -03:00
Gabriel Rosa 66b6be43b7 FIX: new ecr image 2022-11-01 15:33:14 -03:00
Gabriel Rosa c029e286b5 WIP 2022-11-01 15:31:45 -03:00
Gabriel Amorim a8f94a2d53 Merge pull request #162 from dadosfera/feat/platform-delete-pipeline
Feat: platform delete pipeline
2022-10-31 16:43:44 -03:00
Gabriel Rosa aadb3fcdee Better inputs error codes 2022-10-31 16:20:11 -03:00
Gabriel Rosa a26e88d2b6 Merge branch 'main' into feat/platform-delete-pipeline 2022-10-31 15:37:13 -03:00
Victor Radael b20f67e8b8 Merge pull request #161 from dadosfera/ft/delete-data-assets
Ft/delete data assets
2022-10-27 18:44:08 -03:00
Victor Radael 4a8a8eb463 FIX: Pipeline delete 2022-10-27 18:20:56 -03:00
Victor Radael d52f8b78c8 FIX: Pipeline delete 2022-10-27 18:06:39 -03:00
Victor Radael bfbe7cce87 Merge pull request #160 from dadosfera/ft/delete-data-assets
Ft/delete data assets
2022-10-27 17:14:22 -03:00
Victor Radael 1ab8c1dd10 FIX: Merge origin main 2022-10-27 15:24:04 -03:00
Gabriel Amorim 246de83142 Merge pull request #159 from dadosfera/ref/catalog
Catalog Refactor
2022-10-26 17:40:42 -03:00
Victor Radael dfa6222538 FIX: Delete data asset 2022-10-26 14:22:29 -03:00
Gabriel Rosa fc7a9c7909 FIX: do not throw errors when deleting input or transformation on pipeline delete 2022-10-26 10:52:12 -03:00
Gabriel Rosa 932616a578 Delete connection permission public 2022-10-26 09:09:28 -03:00
Victor Radael b64619ede1 FIX: Remove trash code 2022-10-25 19:29:23 -03:00
Gabriel Rosa 4a08e5f560 Merge branch 'main' into ref/catalog 2022-10-25 17:33:04 -03:00
Gabriel Rosa 4ffd0916c3 FIX: pipeline delete working 2022-10-25 16:21:58 -03:00
Victor Radael 92041f453c FIX: Remove trash code 2022-10-25 14:00:26 -03:00
Gabriel Rosa f2f09cc9c7 FIX: oauth redirect adjusted 2022-10-24 15:24:14 -03:00
Victor Radael d486231fd6 FEAT: RLS new barnch 2022-10-21 15:44:21 -03:00
Victor Radael 526a3bddcd Merge pull request #158 from dadosfera/ft/rls-old-catalog
FEAT: Rls first change
2022-10-21 15:18:01 -03:00
Victor Radael 45108d3019 FEAT: Rls first change 2022-10-21 14:49:28 -03:00
Victor Radael ac3e26b851 FIX: RLS 2022-10-21 14:36:59 -03:00
Gabriel Rosa 7fbc919716 FIX: catalog filters 2022-10-20 11:23:30 -03:00
Gabriel Rosa 5b1eb19511 FIX: catalog sort by 2022-10-20 10:27:38 -03:00
Victor Radael b714f3cf36 FIX: Filters 2022-10-20 10:04:45 -03:00
Victor Radael 48c1e29168 FIX: Filters 2022-10-20 09:45:12 -03:00
Victor Radael 04ea1d0f04 FIX: Get a total and fix owner type 2022-10-19 17:01:22 -03:00
Victor Radael 0de5e1d5d1 PATCH: Ref/Catalog new functions 2022-10-18 14:13:24 -03:00
Victor Radael b8c41fad1b PATCH: Ref/Catalog new functions 2022-10-18 13:43:18 -03:00
Victor Radael ef9ec5ad42 PATCH: Ref/Catalog new functions 2022-10-18 13:05:47 -03:00
Victor Radael 65e71f21a0 Merge branch 'ref/catalog' of github.com:dadosfera/maestro into ref/catalog 2022-10-18 13:05:27 -03:00
Victor Radael b921393ba1 PATCH: Ref/Catalog new functions 2022-10-18 13:05:05 -03:00
Gabriel Rosa 18aa7d8674 FIX: permission language fixed 2022-10-17 17:53:47 -03:00
Victor Radael f2dd871de2 FIX: Ref Catalogo new functions models 2022-10-17 16:16:31 -03:00
Victor Radael cf3fa88196 FIX: Ref Catalogo new functions models 2022-10-17 15:00:29 -03:00
Victor Radael 131810533c FIX: Ref Catalogo new functions models 2022-10-17 14:58:11 -03:00
Victor Radael ea06f3f571 FIX: Ref Catalogo new functions models 2022-10-17 14:51:28 -03:00
Victor Radael 81921a7eb2 FIX: Ref Catalogo new functions models 2022-10-17 14:38:31 -03:00
Victor Radael e9fdd0285b FIX: Ref Catalogo new functions models 2022-10-17 13:42:13 -03:00
Victor Radael 167470adba FIX: Ref Catalogo new functions models 2022-10-17 13:35:13 -03:00
Victor Radael fbb9a59214 FIX: Ref Catalogo new functions models 2022-10-17 13:32:41 -03:00
Gabriel Amorim ef6d8594a3 Merge pull request #157 from dadosfera/fix/get-user-permissions
FIX: new function to get all user permissions
2022-10-14 11:57:05 -03:00
Gabriel Rosa ac73c0b046 FIX: new function to get all user permissions 2022-10-14 11:33:15 -03:00
Gabriel Amorim 5a30bf08b2 Merge pull request #156 from dadosfera/fix/pipeline-properties
Fix/pipeline properties
2022-10-13 16:39:41 -03:00
Gabriel Rosa 62b4eb709e Return properties object on findOneProperties 2022-10-13 16:24:32 -03:00
Gabriel Rosa b37a18d1da Merge branch 'main' into fix/pipeline-properties 2022-10-13 15:59:53 -03:00
Gabriel Rosa 379bac704f FIX: getPipelineproperties 2022-10-13 14:59:27 -03:00
Gabriel Rosa 9178530a21 FEAT: get pipeline properties 2022-10-13 14:58:41 -03:00
Gabriel Amorim 667d3f874d Merge pull request #155 from dadosfera/fix/user-permissions
FIX: permissions array on user findOneById
2022-10-13 14:03:02 -03:00
Victor Radael 475af224bf Merge remote-tracking branch 'origin' into ref/catalog 2022-10-13 11:43:16 -03:00
Gabriel Rosa 11f1df5537 FIX: permissions array on user findOneById 2022-10-13 09:23:56 -03:00
Victor Radael e3b02571c8 Merge pull request #154 from dadosfera/fix/oauth
FIX: Public oauth route
2022-10-13 09:05:29 -03:00
Gabriel Rosa 979a12aa7f Merge branch 'main' into fix/oauth 2022-10-13 09:02:17 -03:00
Gabriel Rosa 7646d7a3c5 WIP 2022-10-13 08:57:21 -03:00
Victor Radael 8f0e851a88 FIX: Public oauth route 2022-10-12 23:12:33 -03:00
Victor Radael 779418f471 clear catalog 2022-10-11 22:23:36 -03:00
Gabriel Amorim 6e6d03f302 Merge pull request #153 from dadosfera/fix/user-roles
Fix/user roles
2022-10-11 16:40:12 -03:00
Victor Radael 85f41521ed Merge remote-tracking branch 'origin' into ref/catalog 2022-10-11 15:36:08 -03:00
Victor Radael e2bb119745 refactor/catalog 2022-10-11 15:35:29 -03:00
Gabriel Rosa 3d5ce59b52 FIX: user can get own info, even with no users:admin permission 2022-10-11 11:54:39 -03:00
Gabriel Rosa 1c63271f64 FIX: send language to request that need terms of use 2022-10-11 10:26:13 -03:00
Gabriel Rosa 31a51c20aa FIX: pass metadata to signIn request 2022-10-11 08:41:59 -03:00
Gabriel Rosa 62730eb969 FIX: terms of use dynamic language 2022-10-10 19:56:57 -03:00
Victor Radael 5a2c760bab Merge pull request #152 from dadosfera/fix/dashboards
FIX: Dashboard get all permisisons
2022-10-08 18:00:24 -03:00
Victor Radael 28b4f3ac4b FIX: Dashboard get all permisisons 2022-10-08 17:56:19 -03:00
Gabriel Amorim 43d181a429 Merge pull request #151 from dadosfera/fix/old-pipelines
FIX: Stop using old pipelines
2022-10-07 15:41:13 -03:00
Gabriel Rosa a72fe1b60d FIX: Stop using old pipelines 2022-10-07 15:37:36 -03:00
Gabriel Amorim b52a1227b1 Merge pull request #150 from dadosfera/fix/bulk-connectors-upload
fix/bulk-connectors-upload
2022-10-06 10:11:34 -03:00
Gabriel Rosa 31478ed61d FIX: new protospack version 2022-10-06 09:41:00 -03:00
Gabriel Rosa 79b1ba910e FIX: removed unused import 2022-10-06 09:17:27 -03:00
Gabriel Rosa 7fd929915b FIX: new bulk upload request 2022-10-06 09:15:42 -03:00
Victor Radael e4b27c5f76 Merge pull request #149 from dadosfera/ft/+200meltano
Ft/+200meltano
2022-10-05 17:22:40 -03:00
Victor Radael 92e449d6d3 FIX: Permissions and paths 2022-10-05 17:13:06 -03:00
Victor Radael 5073b2e188 FIX: Permissions and paths 2022-10-05 16:58:02 -03:00
Victor Radael 8a11eda24d FIX: +200 Meltano - Productboard API 2022-10-05 16:44:07 -03:00
Victor Radael 9d732d4a48 FIX: +200 Meltano - Productboard API 2022-10-05 16:42:41 -03:00
Gabriel Amorim 5fa10a939b Merge pull request #148 from dadosfera/fix/data-asset-sharing-permission
FIX: no permission needed for sharing data assets
2022-10-05 14:49:47 -03:00
Gabriel Rosa 4be3ffe3a2 FIX: no permission needed for sharing data assets 2022-10-05 14:45:06 -03:00
Victor Radael 8af9c638ef Merge pull request #147 from dadosfera/feat/openSearchCatalog
FIX: Individual data assets permissions
2022-10-05 08:45:58 -03:00
Victor Radael 97d6f914cc FIX: Individual data assets permissions 2022-10-05 08:43:20 -03:00
Victor Radael 9ad2e3770e FIX: Individual data assets permissions 2022-10-05 08:41:48 -03:00
Victor Radael ea2cc8809f FIX: Individual data assets permissions 2022-10-05 08:40:57 -03:00
Victor Radael 410e190e7c FIX: Individual data assets permissions 2022-10-05 08:39:40 -03:00
Victor Radael c1b213c34c Merge pull request #146 from dadosfera/feat/openSearchCatalog
Feat/open search catalog
2022-10-04 18:34:50 -03:00
Victor Radael a6c58eb82d FIX: unitary permitting 2022-10-04 18:29:39 -03:00
Victor Radael b756cb5a1e FIX: unitary permitting 2022-10-04 18:26:49 -03:00
Victor Radael 46cafc2d50 FIX: unitary permitting 2022-10-04 18:26:27 -03:00
Victor Radael a087775e3a Merge pull request #145 from dadosfera/feat/openSearchCatalog
Feat/open search catalog
2022-10-04 17:55:07 -03:00
Victor Radael 69305610ff FIX: unitary permitting 2022-10-04 17:41:01 -03:00
Victor Radael 69008a9ffb FIX: unitary permitting 2022-10-04 17:19:09 -03:00
Victor Radael 687b4e40e1 FIX: unitary permitting 2022-10-04 16:49:41 -03:00
Victor Radael d56544568b FIX: unitary permitting 2022-10-04 16:47:42 -03:00
Gabriel Amorim cad1e4b3de Merge pull request #144 from dadosfera/fix/delete-connection-permission
FIX: delete connection permission
2022-10-04 15:40:14 -03:00
Gabriel Rosa b311a84bde FIX: delete route reimplemented 2022-10-04 15:22:43 -03:00
Gabriel Rosa 28280dca8d FIX: Delete connections permission 2022-10-04 14:46:52 -03:00
Victor Radael 63fbfa6b52 FIX: unitary permitting 2022-10-04 13:20:34 -03:00
Victor Radael 2b03c4e4a9 FIX: Individual data assets permissions 2022-10-04 11:10:40 -03:00
Victor Radael 18c3c9c58c FIX: Permission data manager 2022-10-03 18:07:38 -03:00
Victor Radael 450c82446e Merge pull request #143 from dadosfera/fix/dadoferademo
FIX: unkown error get user by id
2022-10-03 16:56:55 -03:00
Victor Radael beb27a0f60 FIX: unkown error get user by id 2022-10-03 16:52:42 -03:00
Victor Radael 166c45728f FIX: unkown error get user by id 2022-10-03 16:48:12 -03:00
Victor Radael d0f5da0270 Merge remote-tracking branch 'origin' into feat/openSearchCatalog 2022-10-03 16:46:08 -03:00
Victor Radael dade8d59e5 FIX: Get user by id unkown error 2022-10-03 16:45:50 -03:00
Victor Radael fe261667f8 FIX: Change Revoke Permissions 2022-10-03 10:43:17 -03:00
Victor Radael 722bb1ec7c FIX: Data asset sharing 2022-09-30 18:38:45 -03:00
Victor Radael a371db355b FIX: Data asset sharing 2022-09-30 18:22:29 -03:00
Victor Radael 1ff135c0d7 FIX: Data asset sharing 2022-09-30 18:13:23 -03:00
Victor Radael 0360711ab4 Merge pull request #141 from dadosfera/feat/openSearchCatalog
Feat/open search catalog
2022-09-30 15:38:17 -03:00
Victor Radael b6ec2a83b2 FIX: Swagger 2022-09-30 15:15:24 -03:00
Victor Radael 812dc0c735 FIX: Swagger 2022-09-30 15:12:45 -03:00
Victor Radael 11715dd528 FIX: Swagger 2022-09-30 15:12:19 -03:00
Victor Radael be476c1a6d FIX: data asset owner 2022-09-30 14:38:42 -03:00
Victor Radael a8414f780a FIX: data asset owner 2022-09-30 14:28:06 -03:00
Victor Radael 1da61a4c13 FIX: Data asset permission owner 2022-09-30 12:27:55 -03:00
Victor Radael 7e8f5c49b5 FIX: Data asset permission owner 2022-09-30 11:11:59 -03:00
Victor Radael 96a485afd1 FIX: Revoke Permissions 2022-09-29 18:05:27 -03:00
Victor Radael e3c203d673 FIX: Data assets permissions 2022-09-29 15:04:31 -03:00
Gabriel Amorim d7da68241e Merge pull request #140 from dadosfera/fix/create-user
FIX: create user
2022-09-29 09:19:20 -03:00
Victor Radael 5d924f8a92 FIX: Manage Permissions Catalog 2022-09-28 23:06:52 -03:00
Victor Radael f9b4ddd070 FIX: Manage Permissions Catalog 2022-09-28 17:02:22 -03:00
Victor Radael c0e0732b7e FIX: Manage Permissions Catalog 2022-09-28 17:00:15 -03:00
Gabriel Rosa 37ea7c2f4d FIX: GetOneDataAsset only when table metadata has only one result 2022-09-28 16:59:45 -03:00
Victor Radael d42584fcfc FIX: Manage Permissions Catalog 2022-09-28 16:58:52 -03:00
Victor Radael a28781423c FIX: Manage Permissions Catalog 2022-09-28 14:39:25 -03:00
Gabriel Rosa 8c638610c7 Update description on open search 2022-09-28 11:52:59 -03:00
Victor Radael 55ff485fd0 FIX: Catalog Manage Permissions 2022-09-28 11:48:41 -03:00
Gabriel Rosa 4877fab945 new protospack 2022-09-28 11:20:17 -03:00
rodrigo.zamboni e724ef5ccc FIX: fix get all tags route 2022-09-27 16:33:56 -03:00
rodrigo.zamboni 9dc96416e8 Merge branch 'feat/openSearchCatalog' of https://github.com/dadosfera/maestro into feat/openSearchCatalog 2022-09-27 09:43:32 -03:00
rodrigo.zamboni 49663c4115 UPDATE: modifyed catalog tags route to use opensearch instead of nimbus 2022-09-27 09:35:48 -03:00
Gabriel Rosa c16664c7f9 new protospack version 2022-09-23 14:58:26 -03:00
Gabriel Rosa 7245942707 Function to synchronize user 2022-09-23 09:25:41 -03:00
Gabriel Rosa c29cf5aee1 FIX: sending metadata on creation of users 2022-09-20 17:48:27 -03:00
rodrigo.zamboni d96658c9cd FIX: fix catalog response 2022-09-19 17:42:30 -03:00
rodrigo.zamboni cbd736b87d FIX: fix catalog return response/ adding empty dashboard array 2022-09-19 16:28:33 -03:00
rodrigo.zamboni e4e6d95f8a Merge branch 'feat/openSearchCatalog' of https://github.com/dadosfera/maestro into feat/openSearchCatalog 2022-09-19 15:08:35 -03:00
rodrigo.zamboni a8e13c01d2 FIX: passing params with query params 2022-09-19 15:06:16 -03:00
Gabriel Rosa cdf2ce3cc2 FIX: create credentials on catalog's pi-factory client 2022-09-19 14:51:28 -03:00
rodrigo.zamboni c3877657ef FIX: fix catalog route to use openSearch 2022-09-19 11:14:23 -03:00
Gabriel Amorim 3d39affc61 Merge pull request #139 from dadosfera/fix/network-config-permissions
FIX: remove permission to delete VPNS
2022-09-16 11:55:50 -03:00
Gabriel Rosa 5d045b675c FIX: remove permission to delete VPNS 2022-09-15 09:48:37 -03:00
Gabriel Amorim bb61990f6e Merge pull request #138 from dadosfera/fix/permission-delete-connection
FIX: remove permission to delete connection
2022-09-15 08:50:41 -03:00
Gabriel Rosa 80442c509a FIX: remove permission to delete connection 2022-09-15 08:33:47 -03:00
Gabriel Amorim 8551239038 Merge pull request #137 from dadosfera/fix/update-user-email
FIX: update user email
2022-09-14 11:32:58 -03:00
Gabriel Rosa 4cf7c6e17b FIX: update user email 2022-09-14 10:18:03 -03:00
Gabriel Amorim 88c9c36eb0 Merge pull request #136 from dadosfera/fix/connections-migration
Fix: connections migration
2022-09-13 08:58:29 -03:00
Gabriel Rosa 4b317778f7 FIX: connections migration agora vai 2022-09-12 20:13:12 -03:00
Gabriel Rosa 2eea9787cd FIX: connections migration por favor funciona 2022-09-12 19:52:38 -03:00
Gabriel Rosa c1ab5d152f FIX: reseting productConnectionsMissing and productNetworkConfigsMissing 2022-09-12 18:58:24 -03:00
Gabriel Rosa 8798f5d54e FIX: DEBUG_ConnectionsMigrations - customer_connections 2022-09-12 18:47:42 -03:00
Gabriel Amorim 1da733b5bc Merge pull request #135 from dadosfera/fix/connections-migration
FIX: DEBUG-ConnectionsMigrations
2022-09-12 17:17:54 -03:00
Gabriel Rosa 930c8bc037 FIX: DEBUG-ConnectionsMigrations 2022-09-12 17:14:37 -03:00
Gabriel Amorim 0913741500 Merge pull request #134 from dadosfera/fix/connections-migration
Fix: connections migration
2022-09-12 16:42:08 -03:00
Gabriel Rosa 3e9d0063ae FIX: using DadosferaLogger 2022-09-12 16:35:58 -03:00
Gabriel Rosa 2bc0fe1e9e FIX: get logger from DadosferaLogger 2022-09-12 16:35:08 -03:00
Gabriel Rosa f0fb6e6b4a FIX: DEBUG-ConnectionsMigrations 2022-09-12 16:31:10 -03:00
Gabriel Amorim 74c27d9637 Merge pull request #133 from dadosfera/fix/connections-migration
FIX: get all connectors fixed
2022-09-12 16:11:08 -03:00
Gabriel Rosa 216c83d7fa FIX: get all connectors fixed 2022-09-12 16:08:11 -03:00
Gabriel Amorim 08e66eeb1e Merge pull request #132 from dadosfera/fix/connections-migration
Fix: connections migration
2022-09-12 15:28:45 -03:00
Gabriel Rosa cf3fd90787 FIX: network_config migration 2022-09-12 15:27:54 -03:00
Gabriel Rosa bb1decb08f FIX: typo on dadosfera:connections-migration fixed 2022-09-12 13:01:54 -03:00
Gabriel Rosa 642cb0e70b FIX: connections migration 2022-09-12 13:01:11 -03:00
Gabriel Amorim 0acf759e84 FIX: treating undefined responses from OpenSearch
Merge pull request #131 from dadosfera/fix/connections-undefineds
2022-09-12 07:41:56 -03:00
Gabriel Rosa 7bbb87a54a FIX: removed fake customer_name 2022-09-11 13:08:00 -03:00
Gabriel Rosa 69af7ba626 FIX: new route for synchronizing connections with platform 2022-09-11 13:07:21 -03:00
Gabriel Amorim 0b14221d5a FEAT: Connections manager
FEAT: Connections manager
2022-09-09 17:46:50 -03:00
Gabriel Rosa 16bddf3d40 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-09-09 17:42:29 -03:00
Gabriel Rosa fb6ea7ba14 FIX: tests skipped 2022-09-09 17:42:01 -03:00
Gabriel Amorim 83d649a210 Merge branch 'main' into ft/connection-manager 2022-09-09 17:23:59 -03:00
Gabriel Rosa 319fc80bfe FIX: Checking if old pipelines exists 2022-09-09 11:56:39 -03:00
Gabriel Rosa 35172681ce FIX: checking if status is undefined 2022-09-09 10:17:22 -03:00
Gabriel Rosa 35db24dc59 FIX: find all pipelines 2022-09-09 09:48:19 -03:00
Gabriel Rosa ad9c1fc40c REFACTOR: changed old pipeline service to use lastValueFrom 2022-09-08 19:14:40 -03:00
Gabriel Rosa 4660751023 FIX: getting query params on find all pipelines 2022-09-08 17:59:15 -03:00
Gabriel Rosa 25cf13923b FIX: treating undefined language 2022-09-08 17:50:45 -03:00
Gabriel Rosa d724c1644b FIX: Findone pipeline 2022-09-08 17:12:24 -03:00
Gabriel Rosa c64529a37c FIX: adjust old pipeline input payload 2022-09-08 16:13:14 -03:00
Gabriel Rosa 8b06f0b12c Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-09-08 15:12:00 -03:00
Gabriel Rosa 311a4e4b93 FIX: salesforce oauth tye 2022-09-08 15:11:54 -03:00
rodrigo.zamboni 6f8985baa7 Merge branch 'ft/connection-manager' of https://github.com/dadosfera/maestro into ft/connection-manager 2022-09-08 15:06:52 -03:00
rodrigo.zamboni 11cb5a61da FIX: removing failing objectSnailtoCamel function 2022-09-08 15:06:12 -03:00
Gabriel Rosa 35c0520750 FIX: oauth conditionally setting state 2022-09-08 13:44:44 -03:00
Gabriel Rosa 8c6ec8b1b9 FIX: oauth creating connection 2022-09-08 13:34:51 -03:00
Gabriel Rosa 77db6f3771 New protospack 2022-09-07 17:31:21 -03:00
rodrigo.zamboni 952a76e905 FIX: fixing empty string netwoer_config_id field on test credentials 2022-09-07 11:22:03 -03:00
rodrigo.zamboni 2ec62dae45 FIX: Fix createInput columns field 2022-09-07 10:54:26 -03:00
rodrigo.zamboni 0d431a816b FIX: fix connection service client 2022-09-06 17:13:41 -03:00
rodrigo.zamboni 932e7413bd FIX: Solve merge conflicts 2022-09-06 17:02:55 -03:00
rodrigo.zamboni 5cb83ee679 FIX: Update protospack lib 2022-09-06 17:00:28 -03:00
Gabriel Rosa dcd558bd58 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-09-06 15:38:39 -03:00
Gabriel Rosa 01e74473c0 FIX: pipeline accepting properties on payload 2022-09-06 15:38:25 -03:00
Victor Radael d0c15fe290 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-09-06 12:29:39 -03:00
Victor Radael b9d07e3677 FIX: Connection Details 2022-09-06 12:22:30 -03:00
Gabriel Rosa 2c8ed40bc6 Inputs models refactor 2022-09-06 11:45:12 -03:00
Victor Radael 46d9fd8446 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-09-06 10:10:05 -03:00
Victor Radael 1ca7e647d3 new local env 2022-09-06 10:07:56 -03:00
Gabriel Rosa 469f165cba DOCS: all deprecated fields on create input request 2022-09-06 09:17:24 -03:00
Gabriel Rosa 203a161879 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-09-06 08:39:03 -03:00
Gabriel Rosa 31e9a07489 FIX: create input generic adjusted to new pipeline format 2022-09-06 08:38:46 -03:00
rodrigo.zamboni e24925036d Merge branch 'ft/connection-manager' of https://github.com/dadosfera/maestro into ft/connection-manager 2022-09-05 17:39:01 -03:00
rodrigo.zamboni 53f8f6986f FIX: fix default_value property parse 2022-09-05 17:38:22 -03:00
Gabriel Rosa 5e0a422b35 FIX: checking permission seqid on @AuthenticateCondition 2022-09-05 17:12:44 -03:00
rodrigo.zamboni 8294b5591b FIX: fix connectors permissions 2022-09-05 16:53:15 -03:00
rodrigo.zamboni 01d41a4586 FIX: fix merge conflicts 2022-09-05 16:22:34 -03:00
rodrigo.zamboni b8afdcf87d FIX: fixed permissions 2022-09-05 16:21:01 -03:00
Victor Radael fa216053c2 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-09-05 15:54:40 -03:00
Victor Radael e9a4a77eae Local Connections 2022-09-05 15:53:45 -03:00
Gabriel Rosa fe041262eb FIX: pack metadata before sending to createConnection on connection-test 2022-09-05 15:35:09 -03:00
Victor Radael 7ef0ff1502 FIX: Transformations 2022-09-05 13:06:31 -03:00
Victor Radael d8c55def7e FIX: Transformations 2022-09-05 13:05:53 -03:00
rodrigo.zamboni dfcc3dcb53 FIX: fixed some merge conflicts 2022-09-05 11:30:16 -03:00
rodrigo.zamboni 080c8fc52a FIX: Ping route adapted to use connection_id 2022-09-05 11:01:18 -03:00
Victor Radael ecf0b1de51 FIX: Ad SSL 2022-09-05 09:37:04 -03:00
Victor Radael 2248dbe5a3 FIX: Get All Pipelines 2022-09-04 19:36:42 -03:00
Victor Radael f5ec6661ff FIX: Old Pipeline KeepCase 2022-09-02 20:17:23 -03:00
Victor Radael 0deee0ef9c FIX: Old Pipeline KeepCase 2022-09-02 20:15:15 -03:00
Victor Radael 85f79f14cd FIX: Pipeline Imports 2022-09-02 18:14:41 -03:00
Victor Radael 01bdf134ca FEAT: GET One Pipelinev2 2022-09-02 14:15:36 -03:00
Victor Radael 10a89632b5 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-09-02 12:20:50 -03:00
Victor Radael 24d8f832fa Local Connections 2022-09-02 12:19:44 -03:00
Gabriel Rosa 8efe3d26f8 Add connection update permission (Internal only for now) 2022-09-02 09:11:52 -03:00
Victor Radael 6d21f29005 FEAT: Pipeline Version 2 2022-09-01 20:47:46 -03:00
Victor Radael e526180902 FEAT: Pipeline Version 2 2022-09-01 20:46:02 -03:00
Gabriel Rosa be37eb0321 New permissions 2022-09-01 17:49:38 -03:00
rodrigo.zamboni 8c85172554 Merge branch 'ft/connection-manager' of https://github.com/dadosfera/maestro into ft/connection-manager 2022-09-01 12:45:00 -03:00
rodrigo.zamboni 61296555fe FIX: not deleting failed connection when it comes from request body 2022-09-01 12:41:46 -03:00
Gabriel Rosa 5b6673a367 FIX: port can be either string or number on connection test ping 2022-09-01 09:15:06 -03:00
rodrigo.zamboni 2968647e73 FIX: add connection add parameter to function 2022-08-30 16:44:27 -03:00
rodrigo.zamboni a1a55d645a FIX: update protospack v2 version 2022-08-30 16:23:50 -03:00
rodrigo.zamboni b7748970cd FIX: Fix connectiontest ping dto 2022-08-30 15:37:11 -03:00
Victor Radael 8fb487435a FEAT: Connection Details new version 2022-08-26 19:25:01 -03:00
Victor Radael e94ace09d0 FEAT: Connection Details new version 2022-08-26 19:24:03 -03:00
Gabriel Rosa c713b65979 Better error handling on oauth 2022-08-23 16:14:57 -03:00
Gabriel Rosa 195fdae0e6 Saving oauth connections 2022-08-23 15:42:33 -03:00
Victor Radael 0f079a7b22 DOCS: Swagger 2022-08-23 15:03:13 -03:00
Victor Radael 93b44e8249 DOCS: Swagger 2022-08-23 15:01:28 -03:00
Victor Radael e18dc21670 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-08-23 14:52:47 -03:00
Victor Radael ae443b5b03 DOCS: Swagger 2022-08-23 14:52:36 -03:00
Gabriel Rosa 3bfcc7a412 checking if env is local to disable ssl 2022-08-23 09:01:17 -03:00
Gabriel Rosa 09e3b3fd33 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-08-22 11:35:22 -03:00
Gabriel Rosa 6b7500136f swagger back to normal 2022-08-22 11:35:08 -03:00
Victor Radael 7fa903fa1c FIX: Swagger 2022-08-22 11:27:46 -03:00
Victor Radael 19c35f5cc4 FIX: Swagger 2022-08-22 11:25:58 -03:00
Victor Radael 7b8b1bd8e0 FIX: Swagger 2022-08-22 11:22:41 -03:00
Gabriel Rosa 5cc2d704ea test swagger deploy 2022-08-22 11:13:22 -03:00
Gabriel Rosa d09bf96e6f FIX: added reference columns on get table metadata connection test 2022-08-22 10:23:06 -03:00
Victor Radael 0759f66144 FIX: Docs 2022-08-19 11:08:24 -03:00
Victor Radael 8bd5bf1526 FIX: Docs 2022-08-19 11:07:29 -03:00
Gabriel Rosa 9772e18332 Removed type and plugin from CreateNetworkConfigReq 2022-08-19 10:53:37 -03:00
Victor Radael 6f0f93d80d FIX: Docs Flow 2022-08-18 15:30:39 -03:00
Victor Radael 4b6d30859c FIX: Docs Flow 2022-08-18 15:24:30 -03:00
Victor Radael 5b4b1ae55e FIX: Docs Flow 2022-08-18 15:23:46 -03:00
Victor Radael 00dacfdbb9 FIX: Network_config 2022-08-18 14:22:14 -03:00
Victor Radael 7671567c8c FIX: Network_config 2022-08-18 14:21:46 -03:00
Victor Radael 64a8110649 FIX: Docs Flow 2022-08-18 12:03:31 -03:00
Victor Radael 4cbeea8281 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-08-18 11:38:00 -03:00
Victor Radael adf9c11358 FIX: Docs Flow 2022-08-18 11:37:49 -03:00
Gabriel Rosa 033752789a FIX: connection test ping and connection test credentials 2022-08-17 19:25:03 -03:00
Victor Radael 71617612b9 DOCS: Add Docs to Docsfera 2022-08-17 18:43:14 -03:00
Victor Radael 6219d61fe1 DOCS: Add Docs to Docsfera 2022-08-17 18:32:47 -03:00
Victor Radael 2fd51804fc DOCS: Add Docs to Docsfera 2022-08-17 18:12:32 -03:00
Victor Radael 874a3aa3a0 FIX: Connection and Network Flows (Create and Delete) 2022-08-17 16:03:37 -03:00
Victor Radael a9f68b87ca FIX: Connection and Network Flows (Create and Delete) 2022-08-17 15:58:00 -03:00
Gabriel Rosa 6807b3e1a9 FEAT: inputs and transformation using protospack-v2 2022-08-16 10:18:54 -03:00
Victor Radael 5f17a7106f Update Network Config Manager 2022-08-15 14:49:54 -03:00
Victor Radael 0a0ecb20bb FIX: Swagger 2022-08-12 16:35:36 -03:00
Victor Radael 35abb24ea7 FIX: Swagger 2022-08-12 16:21:26 -03:00
Victor Radael 372fc74bcb FEAT: Managers 2022-08-12 14:47:44 -03:00
Victor Radael 626f785994 O auth module 2022-08-12 13:57:44 -03:00
Victor Radael f88f559b70 Remove Separated Clients 2022-08-12 13:36:20 -03:00
Victor Radael 3981751ac0 Transformations Refactor 2022-08-12 12:49:40 -03:00
Victor Radael 61752a547f Pipelines Refactor 2022-08-12 12:38:34 -03:00
Victor Radael 3356f306b3 Client Refactor 2022-08-12 12:24:22 -03:00
Victor Radael 6dd894e11b Client Refactor 2022-08-12 12:22:41 -03:00
Victor Radael 7a1b5c7dbf Duc Client Refactor 2022-08-12 12:13:04 -03:00
Victor Radael afc187d01a Merge Input Refactor 2022-08-12 10:44:09 -03:00
Victor Radael d0cd62cd3c Merge Input Refactor 2022-08-12 10:43:20 -03:00
Victor Radael acd4dafc57 Update feature 2022-08-12 10:38:20 -03:00
Gabriel Rosa d8ce82793d REFACTOR: removed inputs separated client service 2022-08-11 17:39:50 -03:00
Gabriel Rosa 117704cd8c REFACTOR: inputs inside its own module 2022-08-11 14:28:10 -03:00
Gabriel Rosa 430e09c159 removed some unused tests and new test config 2022-08-11 10:08:06 -03:00
Gabriel Rosa fa11df5684 FIX: create network_config type and plugin default values 2022-08-10 12:33:35 -03:00
Gabriel Amorim e9b85537d7 Merge pull request #129 from dadosfera/feat/role-usage
FEAT: role usage
2022-08-09 09:38:09 -03:00
Gabriel Rosa 17060332fd new protospack release 2022-08-09 09:34:52 -03:00
Gabriel Rosa fde6d0a74c remove unused imports 2022-08-08 12:07:43 -03:00
Gabriel Rosa 0ca6ed8636 MERGE MAIN 2022-08-08 12:07:37 -03:00
Victor Radael 7c5cf3f541 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-08-08 11:01:50 -03:00
Victor Radael 54f4331bcc Update Managers 2022-08-08 10:55:22 -03:00
Gabriel Rosa 2f006c607c FIX: add usage on interfaces 2022-08-05 16:32:10 -03:00
Gabriel Rosa 9fbbfc2064 FIX: new protospack and better roles error handling 2022-08-05 16:20:20 -03:00
Gabriel Rosa cbee264182 FIX: Create VPN using multipart form data to fetch file 2022-08-03 16:30:32 -03:00
Gabriel Rosa 2514245a44 Merge main and conflicts solving 2022-08-03 13:36:31 -03:00
Gabriel Rosa 0761c42041 removed unused import 2022-08-03 11:34:02 -03:00
Gabriel Rosa 688bf49f3b FEAT: Connection test creating connection
- If any error occurs, connection is deleted
 - If operation_result is false, connection is deleted
2022-08-03 11:31:57 -03:00
Gabriel Rosa a1cdad42ab FIX: fixed tests modules 2022-08-03 10:37:17 -03:00
Gabriel Rosa 7546e6100d MERGE MAIN 2022-08-03 10:36:57 -03:00
Victor Radael 4e6883a404 Update Protospack Name and Network Config 2022-08-02 11:47:13 -03:00
Victor Radael 00687dc84e Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-07-28 17:31:38 -03:00
Victor Radael 61a1939c02 Save Connection Changes 2022-07-28 17:30:10 -03:00
Gabriel Rosa 43cec9b6fa FEAT: network config module using in-factory 2022-07-28 15:41:21 -03:00
Gabriel Rosa 97214280bc FEAT: network config module 2022-07-28 10:43:21 -03:00
Victor Radael 0de4a72388 Add OpenSearch 2022-07-27 15:56:04 -03:00
Victor Radael f65f1d01ce Add OpenSearch 2022-07-27 15:54:29 -03:00
Gabriel Rosa 6161865f1a FIX: Add authentication on connection-test routes 2022-07-27 14:25:25 -03:00
Gabriel Rosa d28684a9d0 Connected Connection Test routes to microservice 2022-07-27 14:19:25 -03:00
Gabriel Rosa 54c2a41466 Merge branch 'ft/connection-manager' of github.com:dadosfera/maestro into ft/connection-manager 2022-07-27 08:42:33 -03:00
Gabriel Rosa 037f5cfec0 FEAT: new conection test module 2022-07-27 08:41:32 -03:00
Victor Radael 22631478b9 CRUD Connection 2022-07-25 17:08:01 -03:00
Victor Radael 349ae780a7 FEAT: Connection Manager 2022-07-21 15:19:05 -03:00
Victor Radael 60fb2ee7cb FEAT: Connection Manager 2022-07-21 09:31:37 -03:00
135 changed files with 17419 additions and 3622 deletions
+85 -15
View File
@@ -6,12 +6,12 @@ on:
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
description: "Deployment environment"
required: true
type: choice
options:
- dev
- stg
- stg2
- prd
jobs:
@@ -26,9 +26,9 @@ jobs:
- name: Extract Environment
run: |
if [ ${EVENT} == "workflow_dispatch" ]; then
echo "##[set-output name=environment;]$(echo ${DEPLOY_ENV})"
echo "environment=${DEPLOY_ENV}" >> $GITHUB_OUTPUT
elif [ ${GITHUB_REF} == "refs/heads/main" ]; then
echo "##[set-output name=environment;]$(echo "prd")"
echo "environment=prd" >> $GITHUB_OUTPUT
fi
id: extract_environment
@@ -39,14 +39,13 @@ jobs:
new_release_version: ${{ (steps.semantic.outputs.new_release_published == 'true' && steps.semantic.outputs.new_release_version) || (github.event_name == 'workflow_dispatch' && '0.0.0') }}
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- if: github.event_name != 'workflow_dispatch'
name: Semantic Release
uses: cycjimmy/semantic-release-action@v2
uses: cycjimmy/semantic-release-action@v3
id: semantic
with:
semantic_version: 16
extra_plugins: |
conventional-changelog-eslint
branches: |
@@ -56,22 +55,28 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
deploy:
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, '${{ needs.extract_environment.outputs.environment }}']
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@v2
uses: actions/checkout@v3
- name: Update Pip
run: |
@@ -90,7 +95,7 @@ jobs:
python3 -m pip install awsebcli --upgrade
- name: Configure AWS Region
uses: aws-actions/configure-aws-credentials@v1
uses: aws-actions/configure-aws-credentials@v1-node16
id: aws
with:
aws-region: us-east-1
@@ -113,10 +118,12 @@ jobs:
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
@@ -134,3 +141,66 @@ jobs:
run: |
docker system prune --volumes -a -f
docker system df
api_docs:
needs: [extract_environment, semantic_release, deploy]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Extract Docs BlockId and PageId
env:
ENV: ${{ needs.extract_environment.outputs.environment }}
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
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
runs-on: self-hosted
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Build
env:
+1 -1
View File
@@ -37,4 +37,4 @@ lerna-debug.log*
# Ignore .env files
*.env
.env
.env
+22
View File
@@ -0,0 +1,22 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug NestJs",
"request": "launch",
"runtimeArgs": [
"run",
"start:debug"
],
"runtimeExecutable": "npm",
"skipFiles": [
"<node_internals>/**"
],
"type": "node",
"console": "integratedTerminal"
}
]
}
Binary file not shown.
+123 -74
View File
@@ -1,67 +1,98 @@
<p align="center">
<image src="./assets/maestro.svg" style="width:10rem">
</p>
</p>
# Maestro
Maestro is the Dadosfera's gateway, it's responsible for the communication between the frontend application and Dadosfera's microservices.
## 🚀 Starting
These instructions will allow you to get a working copy of the project on your local machine for development and testing purposes.
### 📋 Requirements
- [NodeJS v18.3.0 LTS / NPM v8.11](https://nodejs.org/pt-br/download/) (you can opt to use [NVM](https://github.com/nvm-sh/nvm) to easily manage node versions)
- Request access to AWS Console dev account for **all services** (avoid gradually asking for each needed service. it will slow down your development cycle)
- Create your Access Key on the "Security credentials" menu
- Set the Access Key on your local development machine
- Create your Access Key on the "Security credentials" menu
- Set the Access Key on your local development machine
- Request access to the dev, stg and prd VPNs
### 🔧 Installation<a id="installation"></a>
- Clone the repository
- SSH
```
git clone git@github.com:dadosfera/maestro.git
```
or
- HTTPS
```
git clone https://github.com/dadosfera/maestro.git
```
- Clone the repository
- SSH
```
git clone git@github.com:dadosfera/maestro.git
```
or
- HTTPS
```
git clone https://github.com/dadosfera/maestro.git
```
- Select the correct node version (optional, only if using [NVM](https://github.com/nvm-sh/nvm)):
```sh
nvm use
```
```sh
nvm use
```
- Install the project dependencies:
```sh
npm i
```
```sh
npm i
```
- Setup the following enviroment variables:
```
ENV=
DUC_URL=
INFACTORY_URL=
OTFACTORY_URL=
PIFACTORY_URL=
SM_OAUTH_PATH=
```
```
ENV=
DUC_URL=
INFACTORY_URL=
OTFACTORY_URL=
PIFACTORY_URL=
SM_OAUTH_PATH=
```
- Start the server:
```sh
# dev mode
npm run start:dev
# or in debug mode
npm run start:debug
```
The service should start successfully.
```sh
# dev mode
npm run start:dev
# or in debug mode
npm run start:debug
```
The service should start successfully.
## 📄 Documentation
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).
### - Multiple documentations
We are currently generating **2 different** documentations: Internal and External.
All routes that have the decorator `@ApiInternalOnly()` will not be visible on the **External** API swagger.
- 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;
It is important to run `npm run docs` before every deploy so we can always have the most updated docs published.
## Authentication decorators
Maestro have utilities to ease the user authentication on every controller and route. The following decorators are available:
### `@Authenticated`
If the user must be authenticated to make request, we can use the `@Authenticated` decorator in the controller or route, as needed.
```ts
@@ -106,11 +137,12 @@ class FooController {
```
### `@RequireAllPermissions`
This decorator requires that **all permissions** listed are granted to the requesting user.
```ts
import { RequireAllPermissions } from '../../authentication/authentication.decorator';
import { Permissions } from '../../authentication/permissions.enum';
import { PERMISSIONS } from '../../authentication/permissions.enum';
@Controller('foo')
class FooController {
@@ -125,11 +157,12 @@ class FooController {
```
### `@RequireSomePermission`
In the following case, the user is required to have **at least one** listed permission.
```ts
import { RequireSomePermission } from '../../authentication/authentication.decorator';
import { Permissions } from '../../authentication/permissions.enum';
import { PERMISSIONS } from '../../authentication/permissions.enum';
@Controller('foo')
@RequireSomePermission(Permissions.FOO.MANAGE, Permissions.FOO.CREATE)
@@ -144,9 +177,11 @@ class FooController {
```
### `@AuthenticateCondition`
If a more complicated authentication check needs to be done, we can use the `@AuthenticateCondition` decorator to define it. The custom function must return `true` to authenticate the request.
In the following example:
- all routes on the `FooController` controller can only be requested from localhost
- `POST /foo/bar` can only be requested from localhost **and** by users from customer id `111...eef`
@@ -166,7 +201,10 @@ class FooController {
@Post('bar')
// allow requests only from a specific customer
@AuthenticateCondition((request: Request, user: RequestUser) => user.customer_id === '1113e943-2187-4fdd-9c2c-54338fedaeef')
@AuthenticateCondition(
(request: Request, user: RequestUser) =>
user.customer_id === '1113e943-2187-4fdd-9c2c-54338fedaeef',
)
async postBar() {
/* ... */
}
@@ -174,51 +212,55 @@ class FooController {
```
### Note on authentication decorators
- The old authentication method placed the user data in the `request.body.info` field, this imposes certain issues regarding the request body because this data should be from the frontend without any modification by Maestro. Now this usage is ⚠️ **DEPRECATED** ⚠️. We are working to migrate to the `@User` parameter decorator. The old method is working while the migration is in progress.
- Authentication decorators can be used together and all of them **must** pass to the request be authenticated, but in the general case you don't need to (*and wouldn't like to...*) use all of them together, as you can code all of the authentication logic in the `@AuthenticateCondition` decorator.
- Authentication decorators can be used together and all of them **must** pass to the request be authenticated, but in the general case you don't need to (_and wouldn't like to..._) use all of them together, as you can code all of the authentication logic in the `@AuthenticateCondition` decorator.
```ts
import {
RequireAllPermissions,
RequireSomePermission,
AuthenticateCondition,
} from '../../authentication/authentication.decorator';
import { Permissions } from '../../authentication/permissions.enum';
import { Waa, Baz } from './authenticationFunctions'
```ts
import {
RequireAllPermissions,
RequireSomePermission,
AuthenticateCondition,
} from '../../authentication/authentication.decorator';
import { PERMISSIONS } from '../../authentication/permissions.enum';
import { Waa, Baz } from './authenticationFunctions';
@Controller('foo')
@RequireAllPermissions(Permissions.FOO.USE, Permissions.FOO.REQUEST)
@RequireSomePermission(Permissions.FOO.MANAGE, Permissions.FOO.ADMIN)
@AuthenticateCondition(Waa)
@AuthenticateCondition(Baz)
class FooController {
@Controller('foo')
@RequireAllPermissions(Permissions.FOO.USE, Permissions.FOO.REQUEST)
@RequireSomePermission(Permissions.FOO.MANAGE, Permissions.FOO.ADMIN)
@AuthenticateCondition(Waa)
@AuthenticateCondition(Baz)
class FooController {
/* ... */
@Get('bar')
async getBar() {
/* ... */
@Get('bar')
async getBar() {
/* ... */
}
@Post('bar')
@RequireAllPermissions(Permissions.BAR.MANAGE, Permissions.BAR.USE, Permissions.BAR.REQUEST)
async postBar() {
/* ... */
}
}
```
@Post('bar')
@RequireAllPermissions(
Permissions.BAR.MANAGE,
Permissions.BAR.USE,
Permissions.BAR.REQUEST,
)
async postBar() {
/* ... */
}
}
```
- If `@RequireAllPermissions` and `@RequireSomePermission` are used with **only a single permission**, they present the **exactly same behavior**.
```ts
// same behavior
@RequireAllPermissions(Permissions.BAR.MANAGE)
@RequireSomePermission(Permissions.BAR.MANAGE)
```
```ts
// same behavior
@RequireAllPermissions(Permissions.BAR.MANAGE)
@RequireSomePermission(Permissions.BAR.MANAGE)
```
## `@User` parameter decorator
The requesting user data can be obtained using the @User parameter decorator, like in the following snippet:
```ts
@@ -264,12 +306,15 @@ class UserController {
```
## 📦 Development
### ⌨️ Coding Style
By default, we use [ESLint](https://eslint.org/) + [Prettier](https://prettier.io/) with default settings.
**We recommend using Visual Studio Code and installing the recommended extensions to ease the development process.**
### Commits pattern
Our workflow pipeline follows the conventional commits specs ([cheat sheets](https://cheatography.com/albelop/cheat-sheets/conventional-commits/)) to release versions accordingly.
Format: `<type>[optional scope]: <description>`
@@ -277,25 +322,29 @@ Format: `<type>[optional scope]: <description>`
Example: `FIX: ensure Range headers adhere more closely to RFC 2616`
### Branching naming convention
- **Feature**: Any code changes for a new module or use case should be done on a feature branch. This branch is created based on the `main` branch. When all changes are done, a Pull Request/Merge Request is needed to put all of these changes back to the `main` branch. Examples: `feature/integrate-swagger`, `feature/JIRA-1234`, `feature/JIRA-1234_support-dark-theme`.
**It is recommended to use all lower caps letters and hyphen (-) to separate words unless it is a specific item name or ID. Underscore (_) could be used to separate the ID and description.**
**It is recommended to use all lower caps letters and hyphen (-) to separate words unless it is a specific item name or ID. Underscore (\_) could be used to separate the ID and description.**
- **Bug Fix**: If the code changes made from the feature branch were rejected after a release, sprint or demo, any necessary fixes after that should be done on the bugfix branch. Examples: `bugfix/more-gray-shades`, `bugfix/JIRA-1444_gray-on-blur-fix`.
- **Bug Fix**: If the code changes made from the feature branch were rejected after a release, sprint or demo, any necessary fixes after that should be done on the bugfix branch. Examples: `bugfix/more-gray-shades`, `bugfix/JIRA-1444_gray-on-blur-fix`.
- **Hot Fix**: If there is a need to fix a blocker, do a temporary patch, apply a critical framework or configuration change that should be handled immediately, it should be created as a Hotfix. Examples: `hotfix/disable-endpoint-zero-day-exploit`, `hotfix/increase-scaling-threshold`.
- **Experimental**: A branch for playing around. Any new feature or idea that is not part of a release or a sprint. Example: `experimental/dark-theme-support`.
### Making a Pull Request
1. Commit your changes
2. Open the Pull Request on GitHub
3. Send Pull Request link in microsfera Google Chat Group for review and possible approval
3. Send Pull Request link in Microsfera Google Chat Group for review and possible approval
## 🛠️ Built with
Some technologies used in this project:
- [NestJS](https://docs.nestjs.com) - Framework for building efficient and scalable NodeJS server-side applications
## ⚙️ Back-end Architecture
The architecture can be found at [this link](https://sites.google.com/dadosfera.ai/wikidoproduto/time/back-end).
+1 -1
View File
@@ -10,7 +10,7 @@ services:
- .env
node_exporter:
image: prom/node-exporter:v1.3.1
image: ${NODE_EXPORTER_URL}
container_name: node_exporter
restart: always
volumes:
File diff suppressed because it is too large Load Diff
+7344
View File
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
declare global {
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'test';
ENV: 'local' | 'stg' | 'prd' | 'test';
LOCAL_ENV: 'stg' | 'prd';
DUC_URL: string;
INFACTORY_URL: string;
PIFACTORY_URL: string;
INTERNAL_SWAGGER: 'true' | 'false';
AWS_REGION: string;
}
}
}
export {};
+18
View File
@@ -0,0 +1,18 @@
import type { Config } from '@jest/types';
const config: Config.InitialOptions = {
moduleFileExtensions: ['ts', 'js', 'json'],
modulePaths: ['<rootDir>'],
roots: ['<rootDir>/src/', '<rootDir>/test/'],
testRegex: '.*\\.(test|spec)\\.[jt]s$',
transform: { '\\.[jt]s$': 'ts-jest' },
collectCoverageFrom: ['**/*.[jt]s'],
coverageDirectory: 'coverage',
coveragePathIgnorePatterns: [
'<rootDir>/node_modules/',
'.*\\.module\\.[jt]s$',
],
// moduleDirectories: ['node_modules'], // default is already 'node_modules'
// rootDir: '.', //No need
// testEnvironment: 'node', //Defaults to 'node'
};
export default config;
+276 -274
View File
@@ -7,11 +7,12 @@
"": {
"name": "maestro",
"version": "0.0.0",
"hasInstallScript": true,
"license": "UNLICENSED",
"dependencies": {
"@aws-sdk/client-secrets-manager": "^3.112.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.3",
"@dadosfera/protospack-v2": "3.11.0-beta.4",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
"@dadosfera/protospack-v2": "3.29.0",
"@grpc/grpc-js": "^1.6.7",
"@grpc/proto-loader": "^0.6.13",
"@nestjs/common": "^8.4.7",
@@ -23,16 +24,19 @@
"@nestjs/platform-express": "^8.4.7",
"@nestjs/schedule": "^1.1.0",
"@nestjs/swagger": "^5.2.1",
"axios": "^0.25.0",
"axios": "^0.27.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"cron-parser": "^4.4.0",
"dotenv": "^14.3.2",
"elastic-apm-node": "^3.36.0",
"helmet": "^5.1.0",
"jsonwebtoken": "^8.5.1",
"jsonwebtoken": "^9.0.0",
"jwk-to-pem": "^2.0.5",
"mixpanel": "^0.17.0",
"passport": "^0.6.0",
"passport-facebook": "^3.0.0",
"passport-forcedotcom": "^0.1.5",
"passport-forcedotcom": "^0.2.0",
"passport-google-oauth20": "^2.0.0",
"passport-hubspot-oauth2": "^1.0.3",
"passport-mailchimp": "^1.1.0",
@@ -1713,9 +1717,9 @@
}
},
"node_modules/@dadosfera/dadosfera-logs": {
"version": "1.0.0-beta.3",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/dadosfera-logs/-/dadosfera-logs-1.0.0-beta.3.tgz",
"integrity": "sha512-VuGiQlpDulcl1+FSFoT4/6PA9Pr+DAzbFHAYesz9rPinR46ejG7ZBZk3m86NYqQtgMyurqXvgCR8GPy9FpwqFw==",
"version": "1.0.0-beta.4",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/dadosfera-logs/-/dadosfera-logs-1.0.0-beta.4.tgz",
"integrity": "sha512-fFncbNVMbfQa2EFl7NJgmtuSRWHGtmwCbl5p++lSLvGcTIMjLGJ4HAMzSMemQhSxiiYlokdwweb7jJY7Ms62SQ==",
"dependencies": {
"@elastic/ecs-winston-format": "^1.3.1",
"winston": "^3.7.2",
@@ -1723,9 +1727,9 @@
}
},
"node_modules/@dadosfera/protospack-v2": {
"version": "3.11.0-beta.4",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.11.0-beta.4.tgz",
"integrity": "sha512-TbRiEkSh7TJXHPiwOD+k22gDV2YIkjL6rSlNzqRTpeWo7Qp3gdkNIiWhUKUMeSIJyeJ9Qo1OKyvE8N/MWfRxKQ==",
"version": "3.29.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.29.0.tgz",
"integrity": "sha512-G66u9V+/+5rQb+fpJdDXydTaji2Mlr0Ro5TTYcQleITKU9DjF1WZs/jP/Qu0Y224nCb8HhePvaVl/Rr0wtLt4w==",
"dependencies": {
"@grpc/grpc-js": "^1.6.7",
"rxjs": "^7.5.5",
@@ -2427,26 +2431,6 @@
}
}
},
"node_modules/@nestjs/common/node_modules/axios": {
"version": "0.27.2",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
}
},
"node_modules/@nestjs/common/node_modules/form-data": {
"version": "4.0.0",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/@nestjs/config": {
"version": "1.2.1",
"license": "MIT",
@@ -3049,7 +3033,7 @@
},
"node_modules/@types/object-hash": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@types/object-hash/-/object-hash-1.3.4.tgz",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@types/object-hash/-/object-hash-1.3.4.tgz",
"integrity": "sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA=="
},
"node_modules/@types/parse-json": {
@@ -3560,7 +3544,6 @@
},
"node_modules/agent-base": {
"version": "6.0.2",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "4"
@@ -3716,8 +3699,9 @@
},
"node_modules/asap": {
"version": "2.0.6",
"dev": true,
"license": "MIT"
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
"dev": true
},
"node_modules/asn1.js": {
"version": "5.4.1",
@@ -3783,10 +3767,25 @@
}
},
"node_modules/axios": {
"version": "0.25.0",
"license": "MIT",
"version": "0.27.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"dependencies": {
"follow-redirects": "^1.14.7"
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
}
},
"node_modules/axios/node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/babel-jest": {
@@ -4257,12 +4256,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/class-transformer": {
"version": "0.5.1",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/class-transformer/-/class-transformer-0.5.1.tgz",
"integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw=="
},
"node_modules/class-validator": {
"version": "0.13.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/class-validator/-/class-validator-0.13.2.tgz",
"integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==",
"optional": true,
"peer": true,
"dependencies": {
"libphonenumber-js": "^1.9.43",
"validator": "^13.7.0"
@@ -4599,7 +4601,7 @@
},
"node_modules/dataloader": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/dataloader/-/dataloader-1.4.0.tgz",
"integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw=="
},
"node_modules/debug": {
@@ -4684,6 +4686,17 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/detect-newline": {
"version": "3.1.0",
"dev": true,
@@ -4693,9 +4706,10 @@
}
},
"node_modules/dezalgo": {
"version": "1.0.3",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
"integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
"dev": true,
"license": "ISC",
"dependencies": {
"asap": "^2.0.0",
"wrappy": "1"
@@ -4770,6 +4784,14 @@
"version": "5.1.0",
"license": "BSD-2-Clause"
},
"node_modules/dprint-node": {
"version": "1.0.7",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/dprint-node/-/dprint-node-1.0.7.tgz",
"integrity": "sha512-NTZOW9A7ipb0n7z7nC3wftvsbceircwVHSgzobJsEQa+7RnOMbhrfX5IflA6CtC4GA63DSAiHYXa4JKEy9F7cA==",
"dependencies": {
"detect-libc": "^1.0.3"
}
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"license": "Apache-2.0",
@@ -5882,23 +5904,28 @@
}
},
"node_modules/formidable": {
"version": "2.0.1",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz",
"integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"dezalgo": "1.0.3",
"hexoid": "1.0.0",
"once": "1.4.0",
"qs": "6.9.3"
"dezalgo": "^1.0.4",
"hexoid": "^1.0.0",
"once": "^1.4.0",
"qs": "^6.11.0"
},
"funding": {
"url": "https://ko-fi.com/tunnckoCore/commissions"
}
},
"node_modules/formidable/node_modules/qs": {
"version": "6.9.3",
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
@@ -7759,30 +7786,18 @@
}
},
"node_modules/jsonwebtoken": {
"version": "8.5.1",
"license": "MIT",
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
"integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
"dependencies": {
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"lodash": "^4.17.21",
"ms": "^2.1.1",
"semver": "^5.6.0"
"semver": "^7.3.8"
},
"engines": {
"node": ">=4",
"npm": ">=1.4.28"
}
},
"node_modules/jsonwebtoken/node_modules/semver": {
"version": "5.7.1",
"license": "ISC",
"bin": {
"semver": "bin/semver"
"node": ">=12",
"npm": ">=6"
}
},
"node_modules/jwa": {
@@ -7845,11 +7860,9 @@
}
},
"node_modules/libphonenumber-js": {
"version": "1.10.7",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/libphonenumber-js/-/libphonenumber-js-1.10.7.tgz",
"integrity": "sha512-jZXLCCWMe1b/HXkjiLeYt2JsytZMcqH26jLFIdzFDFF0xvSUWrYKyvPlyPG+XJzEyKUFbcZxLdWGMwQsWaHDxQ==",
"optional": true,
"peer": true
"version": "1.10.9",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/libphonenumber-js/-/libphonenumber-js-1.10.9.tgz",
"integrity": "sha512-BvPlectKyHuA8xVBU4PerKdI1t+l71F/GCh6CQhe4j5bTwibF6Jcle88kDcyvq4aW55CXxWZg7yIYhzmjgrjkQ=="
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
@@ -7883,30 +7896,6 @@
"version": "4.3.0",
"license": "MIT"
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"license": "MIT"
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"license": "MIT"
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"license": "MIT"
},
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"license": "MIT"
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"license": "MIT"
},
"node_modules/lodash.isstring": {
"version": "4.0.1",
"license": "MIT"
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
"dev": true,
@@ -7917,10 +7906,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/lodash.once": {
"version": "4.1.1",
"license": "MIT"
},
"node_modules/lodash.sortby": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
@@ -7957,9 +7942,9 @@
}
},
"node_modules/logform": {
"version": "2.4.1",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/logform/-/logform-2.4.1.tgz",
"integrity": "sha512-7XB/tqc3VRbri9pRjU6E97mQ8vC27ivJ3lct4jhyT+n0JNDd4YKldFl0D75NqDp46hk8RC7Ma1Vjv/UPf67S+A==",
"version": "2.4.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/logform/-/logform-2.4.2.tgz",
"integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==",
"dependencies": {
"@colors/colors": "1.5.0",
"fecha": "^4.2.0",
@@ -8187,6 +8172,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",
@@ -8203,15 +8211,17 @@
"integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A=="
},
"node_modules/moment": {
"version": "2.29.3",
"license": "MIT",
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"engines": {
"node": "*"
}
},
"node_modules/moment-timezone": {
"version": "0.5.34",
"license": "MIT",
"version": "0.5.38",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.38.tgz",
"integrity": "sha512-nMIrzGah4+oYZPflDvLZUgoVUO4fvAqHstvG3xAUnMolWncuAiLDWNnJZj6EwJGMGfb1ZcuTFE6GI3hNOVWI/Q==",
"dependencies": {
"moment": ">= 2.9.0"
},
@@ -8670,24 +8680,11 @@
}
},
"node_modules/passport-forcedotcom": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/passport-forcedotcom/-/passport-forcedotcom-0.1.5.tgz",
"integrity": "sha512-raDRQqBJtTEHDxLkxLG+9sYE0xpPy9PqriMPbMy9jmpIcU2+mOq9PgczvUPrU8UjfcZp3gqkDHqujcUWiGOVNQ==",
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/passport-forcedotcom/-/passport-forcedotcom-0.2.0.tgz",
"integrity": "sha512-gRI5+zQK9iVAZ1hJgt3TkWLeDw8SYrNWUsmVk0d0RyvUviQ4mT52x3JbYRiyZefoeID8afmLsJH8VOHjWdPf3Q==",
"dependencies": {
"passport-oauth2": "1.3.x"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/passport-forcedotcom/node_modules/passport-oauth2": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.3.0.tgz",
"integrity": "sha512-AisRXcC7Vx8WSx5C9O8UNib/Y91/IOphs5tImlOMs7T+QTEJdmS+OxgXSskbnDdGQT/8mU5T3yMT6136rBjYXg==",
"dependencies": {
"oauth": "0.9.x",
"passport-strategy": "1.x.x",
"uid2": "0.0.x"
"passport-oauth2": "^1.6.1"
},
"engines": {
"node": ">= 0.4.0"
@@ -8918,6 +8915,7 @@
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
@@ -9446,9 +9444,9 @@
"license": "MIT"
},
"node_modules/semver": {
"version": "7.3.7",
"dev": true,
"license": "ISC",
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -9985,9 +9983,10 @@
}
},
"node_modules/terser": {
"version": "5.14.0",
"version": "5.14.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
"integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
@@ -10308,34 +10307,33 @@
}
},
"node_modules/ts-poet": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-4.14.0.tgz",
"integrity": "sha512-hLGzWo3H3GLjVubftvaycGSconJrNpjjdpn43Lthnu61l/R2CaPIu+1JhaWZyqh2yj5NaOv+wZ5zoSJzDouAHw==",
"version": "6.1.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/ts-poet/-/ts-poet-6.1.0.tgz",
"integrity": "sha512-PFwbNJjGrb44wzHUGQicG2/nhjR+3+k7zYLDTa8D61NVUitl7K/JgIc9/P+8oMNenntKzLc8tjLDOkPrxIhm6A==",
"dependencies": {
"lodash": "^4.17.15",
"prettier": "^2.5.1"
"dprint-node": "^1.0.7"
}
},
"node_modules/ts-proto": {
"version": "1.115.5",
"resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-1.115.5.tgz",
"integrity": "sha512-JGk6hln3JP0T0wJlqIOUGE/qHDYcI547hrJwAzeLq1E2l7IdYnYiAJRsvJEOsvotIyZXMbHNlZedIZEVIPAspQ==",
"version": "1.131.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/ts-proto/-/ts-proto-1.131.0.tgz",
"integrity": "sha512-oj/kWrDz3JkkPQwumQDKfwHroH9iNz2alvrbCYzNYJlb/2MAUzFNfh9T+SZUPDfoXXK6oJoW1hGCHznj67yHNg==",
"dependencies": {
"@types/object-hash": "^1.3.0",
"dataloader": "^1.4.0",
"object-hash": "^1.3.1",
"protobufjs": "^6.11.3",
"ts-poet": "^4.11.0",
"ts-proto-descriptors": "1.6.0"
"ts-poet": "^6.1.0",
"ts-proto-descriptors": "1.7.1"
},
"bin": {
"protoc-gen-ts_proto": "protoc-gen-ts_proto"
}
},
"node_modules/ts-proto-descriptors": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-1.6.0.tgz",
"integrity": "sha512-Vrhue2Ti99us/o76mGy28nF3W/Uanl1/8detyJw2yyRwiBC5yxy+hEZqQ/ZX2PbZ1vyCpJ51A9L4PnCCnkBMTQ==",
"version": "1.7.1",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/ts-proto-descriptors/-/ts-proto-descriptors-1.7.1.tgz",
"integrity": "sha512-oIKUh3K4Xts4v29USGLfUG+2mEk32MsqpgZAOUyUlkrcIdv34yE+k2oZ2Nzngm6cV/JgFdOxRCqeyvmWHuYAyw==",
"dependencies": {
"long": "^4.0.0",
"protobufjs": "^6.8.8"
@@ -10343,7 +10341,7 @@
},
"node_modules/ts-proto/node_modules/object-hash": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/object-hash/-/object-hash-1.3.1.tgz",
"integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==",
"engines": {
"node": ">= 0.10.0"
@@ -10593,8 +10591,6 @@
"version": "13.7.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/validator/-/validator-13.7.0.tgz",
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==",
"optional": true,
"peer": true,
"engines": {
"node": ">= 0.10"
}
@@ -10838,10 +10834,11 @@
}
},
"node_modules/winston": {
"version": "3.7.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/winston/-/winston-3.7.2.tgz",
"integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==",
"version": "3.8.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/winston/-/winston-3.8.2.tgz",
"integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==",
"dependencies": {
"@colors/colors": "1.5.0",
"@dabh/diagnostics": "^2.0.2",
"async": "^3.2.3",
"is-stream": "^2.0.0",
@@ -12277,9 +12274,9 @@
}
},
"@dadosfera/dadosfera-logs": {
"version": "1.0.0-beta.3",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/dadosfera-logs/-/dadosfera-logs-1.0.0-beta.3.tgz",
"integrity": "sha512-VuGiQlpDulcl1+FSFoT4/6PA9Pr+DAzbFHAYesz9rPinR46ejG7ZBZk3m86NYqQtgMyurqXvgCR8GPy9FpwqFw==",
"version": "1.0.0-beta.4",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/dadosfera-logs/-/dadosfera-logs-1.0.0-beta.4.tgz",
"integrity": "sha512-fFncbNVMbfQa2EFl7NJgmtuSRWHGtmwCbl5p++lSLvGcTIMjLGJ4HAMzSMemQhSxiiYlokdwweb7jJY7Ms62SQ==",
"requires": {
"@elastic/ecs-winston-format": "^1.3.1",
"winston": "^3.7.2",
@@ -12287,9 +12284,9 @@
}
},
"@dadosfera/protospack-v2": {
"version": "3.11.0-beta.4",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.11.0-beta.4.tgz",
"integrity": "sha512-TbRiEkSh7TJXHPiwOD+k22gDV2YIkjL6rSlNzqRTpeWo7Qp3gdkNIiWhUKUMeSIJyeJ9Qo1OKyvE8N/MWfRxKQ==",
"version": "3.29.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.29.0.tgz",
"integrity": "sha512-G66u9V+/+5rQb+fpJdDXydTaji2Mlr0Ro5TTYcQleITKU9DjF1WZs/jP/Qu0Y224nCb8HhePvaVl/Rr0wtLt4w==",
"requires": {
"@grpc/grpc-js": "^1.6.7",
"rxjs": "^7.5.5",
@@ -12782,23 +12779,6 @@
"iterare": "1.2.1",
"tslib": "2.4.0",
"uuid": "8.3.2"
},
"dependencies": {
"axios": {
"version": "0.27.2",
"requires": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
}
},
"form-data": {
"version": "4.0.0",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
}
},
"@nestjs/config": {
@@ -13186,7 +13166,7 @@
},
"@types/object-hash": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@types/object-hash/-/object-hash-1.3.4.tgz",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/@types/object-hash/-/object-hash-1.3.4.tgz",
"integrity": "sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA=="
},
"@types/parse-json": {
@@ -13553,7 +13533,6 @@
},
"agent-base": {
"version": "6.0.2",
"dev": true,
"requires": {
"debug": "4"
}
@@ -13650,6 +13629,8 @@
},
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
"dev": true
},
"asn1.js": {
@@ -13712,9 +13693,24 @@
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="
},
"axios": {
"version": "0.25.0",
"version": "0.27.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"requires": {
"follow-redirects": "^1.14.7"
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
},
"dependencies": {
"form-data": {
"version": "4.0.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
}
},
"babel-jest": {
@@ -14018,12 +14014,15 @@
"version": "1.2.2",
"dev": true
},
"class-transformer": {
"version": "0.5.1",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/class-transformer/-/class-transformer-0.5.1.tgz",
"integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw=="
},
"class-validator": {
"version": "0.13.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/class-validator/-/class-validator-0.13.2.tgz",
"integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==",
"optional": true,
"peer": true,
"requires": {
"libphonenumber-js": "^1.9.43",
"validator": "^13.7.0"
@@ -14270,7 +14269,7 @@
},
"dataloader": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/dataloader/-/dataloader-1.4.0.tgz",
"integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw=="
},
"debug": {
@@ -14319,12 +14318,19 @@
"destroy": {
"version": "1.2.0"
},
"detect-libc": {
"version": "1.0.3",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="
},
"detect-newline": {
"version": "3.1.0",
"dev": true
},
"dezalgo": {
"version": "1.0.3",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
"integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
"dev": true,
"requires": {
"asap": "^2.0.0",
@@ -14374,6 +14380,14 @@
"dotenv-expand": {
"version": "5.1.0"
},
"dprint-node": {
"version": "1.0.7",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/dprint-node/-/dprint-node-1.0.7.tgz",
"integrity": "sha512-NTZOW9A7ipb0n7z7nC3wftvsbceircwVHSgzobJsEQa+7RnOMbhrfX5IflA6CtC4GA63DSAiHYXa4JKEy9F7cA==",
"requires": {
"detect-libc": "^1.0.3"
}
},
"ecdsa-sig-formatter": {
"version": "1.0.11",
"requires": {
@@ -15158,18 +15172,25 @@
}
},
"formidable": {
"version": "2.0.1",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz",
"integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==",
"dev": true,
"requires": {
"dezalgo": "1.0.3",
"hexoid": "1.0.0",
"once": "1.4.0",
"qs": "6.9.3"
"dezalgo": "^1.0.4",
"hexoid": "^1.0.0",
"once": "^1.4.0",
"qs": "^6.11.0"
},
"dependencies": {
"qs": {
"version": "6.9.3",
"dev": true
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dev": true,
"requires": {
"side-channel": "^1.0.4"
}
}
}
},
@@ -16394,23 +16415,14 @@
}
},
"jsonwebtoken": {
"version": "8.5.1",
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
"integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
"requires": {
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"lodash": "^4.17.21",
"ms": "^2.1.1",
"semver": "^5.6.0"
},
"dependencies": {
"semver": {
"version": "5.7.1"
}
"semver": "^7.3.8"
}
},
"jwa": {
@@ -16458,11 +16470,9 @@
}
},
"libphonenumber-js": {
"version": "1.10.7",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/libphonenumber-js/-/libphonenumber-js-1.10.7.tgz",
"integrity": "sha512-jZXLCCWMe1b/HXkjiLeYt2JsytZMcqH26jLFIdzFDFF0xvSUWrYKyvPlyPG+XJzEyKUFbcZxLdWGMwQsWaHDxQ==",
"optional": true,
"peer": true
"version": "1.10.9",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/libphonenumber-js/-/libphonenumber-js-1.10.9.tgz",
"integrity": "sha512-BvPlectKyHuA8xVBU4PerKdI1t+l71F/GCh6CQhe4j5bTwibF6Jcle88kDcyvq4aW55CXxWZg7yIYhzmjgrjkQ=="
},
"lines-and-columns": {
"version": "1.2.4",
@@ -16485,24 +16495,6 @@
"lodash.camelcase": {
"version": "4.3.0"
},
"lodash.includes": {
"version": "4.3.0"
},
"lodash.isboolean": {
"version": "3.0.3"
},
"lodash.isinteger": {
"version": "4.0.4"
},
"lodash.isnumber": {
"version": "3.0.3"
},
"lodash.isplainobject": {
"version": "4.0.6"
},
"lodash.isstring": {
"version": "4.0.1"
},
"lodash.memoize": {
"version": "4.1.2",
"dev": true
@@ -16511,9 +16503,6 @@
"version": "4.6.2",
"dev": true
},
"lodash.once": {
"version": "4.1.1"
},
"lodash.sortby": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
@@ -16538,9 +16527,9 @@
}
},
"logform": {
"version": "2.4.1",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/logform/-/logform-2.4.1.tgz",
"integrity": "sha512-7XB/tqc3VRbri9pRjU6E97mQ8vC27ivJ3lct4jhyT+n0JNDd4YKldFl0D75NqDp46hk8RC7Ma1Vjv/UPf67S+A==",
"version": "2.4.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/logform/-/logform-2.4.2.tgz",
"integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==",
"requires": {
"@colors/colors": "1.5.0",
"fecha": "^4.2.0",
@@ -16688,6 +16677,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": {
@@ -16700,10 +16708,14 @@
"integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A=="
},
"moment": {
"version": "2.29.3"
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
},
"moment-timezone": {
"version": "0.5.34",
"version": "0.5.38",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.38.tgz",
"integrity": "sha512-nMIrzGah4+oYZPflDvLZUgoVUO4fvAqHstvG3xAUnMolWncuAiLDWNnJZj6EwJGMGfb1ZcuTFE6GI3hNOVWI/Q==",
"requires": {
"moment": ">= 2.9.0"
}
@@ -17016,23 +17028,11 @@
}
},
"passport-forcedotcom": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/passport-forcedotcom/-/passport-forcedotcom-0.1.5.tgz",
"integrity": "sha512-raDRQqBJtTEHDxLkxLG+9sYE0xpPy9PqriMPbMy9jmpIcU2+mOq9PgczvUPrU8UjfcZp3gqkDHqujcUWiGOVNQ==",
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/passport-forcedotcom/-/passport-forcedotcom-0.2.0.tgz",
"integrity": "sha512-gRI5+zQK9iVAZ1hJgt3TkWLeDw8SYrNWUsmVk0d0RyvUviQ4mT52x3JbYRiyZefoeID8afmLsJH8VOHjWdPf3Q==",
"requires": {
"passport-oauth2": "1.3.x"
},
"dependencies": {
"passport-oauth2": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.3.0.tgz",
"integrity": "sha512-AisRXcC7Vx8WSx5C9O8UNib/Y91/IOphs5tImlOMs7T+QTEJdmS+OxgXSskbnDdGQT/8mU5T3yMT6136rBjYXg==",
"requires": {
"oauth": "0.9.x",
"passport-strategy": "1.x.x",
"uid2": "0.0.x"
}
}
"passport-oauth2": "^1.6.1"
}
},
"passport-google-oauth20": {
@@ -17184,7 +17184,8 @@
"prettier": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g=="
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"dev": true
},
"prettier-linter-helpers": {
"version": "1.0.0",
@@ -17513,8 +17514,9 @@
}
},
"semver": {
"version": "7.3.7",
"dev": true,
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
"requires": {
"lru-cache": "^6.0.0"
}
@@ -17896,7 +17898,9 @@
}
},
"terser": {
"version": "5.14.0",
"version": "5.14.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
"integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
"dev": true,
"requires": {
"@jridgewell/source-map": "^0.3.2",
@@ -18087,38 +18091,37 @@
}
},
"ts-poet": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-4.14.0.tgz",
"integrity": "sha512-hLGzWo3H3GLjVubftvaycGSconJrNpjjdpn43Lthnu61l/R2CaPIu+1JhaWZyqh2yj5NaOv+wZ5zoSJzDouAHw==",
"version": "6.1.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/ts-poet/-/ts-poet-6.1.0.tgz",
"integrity": "sha512-PFwbNJjGrb44wzHUGQicG2/nhjR+3+k7zYLDTa8D61NVUitl7K/JgIc9/P+8oMNenntKzLc8tjLDOkPrxIhm6A==",
"requires": {
"lodash": "^4.17.15",
"prettier": "^2.5.1"
"dprint-node": "^1.0.7"
}
},
"ts-proto": {
"version": "1.115.5",
"resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-1.115.5.tgz",
"integrity": "sha512-JGk6hln3JP0T0wJlqIOUGE/qHDYcI547hrJwAzeLq1E2l7IdYnYiAJRsvJEOsvotIyZXMbHNlZedIZEVIPAspQ==",
"version": "1.131.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/ts-proto/-/ts-proto-1.131.0.tgz",
"integrity": "sha512-oj/kWrDz3JkkPQwumQDKfwHroH9iNz2alvrbCYzNYJlb/2MAUzFNfh9T+SZUPDfoXXK6oJoW1hGCHznj67yHNg==",
"requires": {
"@types/object-hash": "^1.3.0",
"dataloader": "^1.4.0",
"object-hash": "^1.3.1",
"protobufjs": "^6.11.3",
"ts-poet": "^4.11.0",
"ts-proto-descriptors": "1.6.0"
"ts-poet": "^6.1.0",
"ts-proto-descriptors": "1.7.1"
},
"dependencies": {
"object-hash": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/object-hash/-/object-hash-1.3.1.tgz",
"integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA=="
}
}
},
"ts-proto-descriptors": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-1.6.0.tgz",
"integrity": "sha512-Vrhue2Ti99us/o76mGy28nF3W/Uanl1/8detyJw2yyRwiBC5yxy+hEZqQ/ZX2PbZ1vyCpJ51A9L4PnCCnkBMTQ==",
"version": "1.7.1",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/ts-proto-descriptors/-/ts-proto-descriptors-1.7.1.tgz",
"integrity": "sha512-oIKUh3K4Xts4v29USGLfUG+2mEk32MsqpgZAOUyUlkrcIdv34yE+k2oZ2Nzngm6cV/JgFdOxRCqeyvmWHuYAyw==",
"requires": {
"long": "^4.0.0",
"protobufjs": "^6.8.8"
@@ -18292,9 +18295,7 @@
"validator": {
"version": "13.7.0",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/validator/-/validator-13.7.0.tgz",
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==",
"optional": true,
"peer": true
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw=="
},
"vary": {
"version": "1.1.2"
@@ -18454,10 +18455,11 @@
}
},
"winston": {
"version": "3.7.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/winston/-/winston-3.7.2.tgz",
"integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==",
"version": "3.8.2",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/winston/-/winston-3.8.2.tgz",
"integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==",
"requires": {
"@colors/colors": "1.5.0",
"@dabh/diagnostics": "^2.0.2",
"async": "^3.2.3",
"is-stream": "^2.0.0",
+14 -31
View File
@@ -9,13 +9,16 @@
"node": "18.3.0"
},
"scripts": {
"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",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:dev": "export INTERNAL_SWAGGER=true && nest start --watch",
"start:debug": "npm run start:dev",
"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",
"test:watch": "jest --watch",
@@ -25,6 +28,8 @@
},
"dependencies": {
"@aws-sdk/client-secrets-manager": "^3.112.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
"@dadosfera/protospack-v2": "3.29.0",
"@grpc/grpc-js": "^1.6.7",
"@grpc/proto-loader": "^0.6.13",
"@nestjs/common": "^8.4.7",
@@ -36,22 +41,23 @@
"@nestjs/platform-express": "^8.4.7",
"@nestjs/schedule": "^1.1.0",
"@nestjs/swagger": "^5.2.1",
"axios": "^0.25.0",
"axios": "^0.27.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"cron-parser": "^4.4.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.3",
"dotenv": "^14.3.2",
"elastic-apm-node": "^3.36.0",
"helmet": "^5.1.0",
"jsonwebtoken": "^8.5.1",
"jsonwebtoken": "^9.0.0",
"jwk-to-pem": "^2.0.5",
"mixpanel": "^0.17.0",
"passport": "^0.6.0",
"passport-facebook": "^3.0.0",
"passport-forcedotcom": "^0.1.5",
"passport-forcedotcom": "^0.2.0",
"passport-google-oauth20": "^2.0.0",
"passport-hubspot-oauth2": "^1.0.3",
"passport-mailchimp": "^1.1.0",
"protospack": "2.5.2",
"@dadosfera/protospack-v2": "3.11.0-beta.4",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.5.5",
@@ -89,28 +95,5 @@
"ts-node": "^10.8.1",
"tsconfig-paths": "^3.14.1",
"typescript": "^4.6.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": ".",
"modulePaths": [
"<rootDir>"
],
"moduleDirectories": [
"node_modules"
],
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
}
+31 -85
View File
@@ -1,81 +1,35 @@
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { ConfigModule } from '@nestjs/config';
import { InputsController } from './modules/inputs/inputs.controller';
import { TransformationsController } from './modules/transformations/transformations.controller';
import { OutputsController } from './modules/outputs/outputs.controllers';
import { PipelinesController } from './modules/pipelines/pipelines.controller';
import { HealthController } from './modules/health/health.controller';
import { InputsService } from './modules/inputs/inputs.service';
import { TransformationsService } from './modules/transformations/transformations.service';
import { OutputsService } from './modules/outputs/outputs.service';
import { PipelinesService } from './modules/pipelines/pipelines.service';
import { HealthService } from './modules/health/health.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { InputsClientService } from './clients/inputs/client.service';
import { TransformationsClientService } from './clients/transformations/client.service';
import { OutputsClientService } from './clients/outputs/client.service';
import { PipelinesClientService } from './clients/pipelines/client.service';
import { OutputsClientConfiguration } from './clients/outputs/client.config';
import { TransformationsClientConfiguration } from './clients/transformations/client.config';
import { InputsClientConfiguration } from './clients/inputs/client.config';
import { PipelinesClientConfiguration } from './clients/pipelines/client.config';
import { CatalogController } from './modules/catalog/catalog.controller';
import { CatalogService } from './modules/catalog/catalog.service';
import { OauthController } from './modules/oauth/oauth.controller';
import { getOauthSecrets } from './utils/OauthSecrets';
import { HubspotStrategy } from './modules/oauth/passport-strategies/hubspot-strategy';
import { FacebookStrategy } from './modules/oauth/passport-strategies/facebook-strategy';
import { AuthenticationGuard } from './authentication/authentication.guard';
import { APP_GUARD } from '@nestjs/core';
import { GoogleStrategy } from './modules/oauth/passport-strategies/google-strategy';
import { MailChimpStrategy } from './modules/oauth/passport-strategies/mailchimp-strategy';
import { SalesforceStrategy } from './modules/oauth/passport-strategies/salesforce-strategy';
import { UsersModule } from './modules/users/users.module';
import { RolesModule } from './modules/roles/roles.module';
import { ConnectorModule } from './modules/connector/connector.module';
import { AuthModule } from './modules/auth/auth.module';
import { PermissionsModule } from './modules/permissions/permissions.module';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { TermsOfUseModule } from './modules/termsOfUse/termsOfUse.module';
const inputClient = new InputsClientConfiguration();
const outputClient = new OutputsClientConfiguration();
const pipelineClient = new PipelinesClientConfiguration();
const transformationClient = new TransformationsClientConfiguration();
import { ConnectionModule } from './modules/connection/connection.module';
import { ConnectionTestModule } from './modules/connection-test/connection-test.module';
import { NetworkConfigModule } from './modules/network-config/network-config.module';
import { InputsModule } from './modules/inputs/inputs.module';
import { OauthModule } from './modules/oauth/oauth.module';
import { PipelinesModule } from './modules/pipelines/pipelines.module';
import { TransformationsModule } from './modules/transformations/transformations.module';
import { HealthModule } from './modules/health/health.module';
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: [
InputsController,
TransformationsController,
OutputsController,
PipelinesController,
HealthController,
CatalogController,
OauthController,
],
controllers: [],
providers: [
DadosferaLogger,
{ provide: 'OAUTH_SECRETS', useValue: getOauthSecrets() },
InputsService,
TransformationsService,
OutputsService,
PipelinesService,
HealthService,
InputsClientService,
TransformationsClientService,
OutputsClientService,
PipelinesClientService,
CatalogService,
HubspotStrategy,
FacebookStrategy,
GoogleStrategy,
MailChimpStrategy,
SalesforceStrategy,
{
provide: APP_GUARD,
useClass: AuthenticationGuard,
@@ -85,33 +39,25 @@ const transformationClient = new TransformationsClientConfiguration();
ConfigModule.forRoot({
isGlobal: true,
}),
ConnectorModule,
AuthModule,
ConnectionModule,
NetworkConfigModule,
InputsModule,
PipelinesV2Module,
CatalogModule,
ConnectorModule,
PermissionsModule,
TermsOfUseModule,
ClientsModule.register([
{
name: 'INPUTS_PACKAGE',
...inputClient.config(),
},
{
name: 'TRANSFORMATIONS_PACKAGE',
...transformationClient.config(),
},
{
name: 'OUTPUTS_PACKAGE',
...outputClient.config(),
},
{
name: 'PIPELINES_PACKAGE',
...pipelineClient.config(),
},
]),
ConnectionTestModule,
PipelinesModule,
TransformationsModule,
UsersModule,
RolesModule,
OauthModule,
ProductboardModule,
MixpanelModule,
//Always leave HealthModule last, so it is on the bottom of swagger
HealthModule,
],
})
export class AppModule {}
@@ -1,56 +0,0 @@
import { Request } from 'express';
import { CustomDecorator } from '@nestjs/common';
import { PermissionsObjectPermission } from './permissions.enum';
import { RequestUser } from './user.decorator';
export const AUTH_FUNCTION_KEY = '__AUTH_FUNCTION__';
export type AuthenticationFunction = (req: Request, user: any) => boolean;
// implementation copied from SetMetadata, but tweaked to get existing values
// of the metadataKey and accumulate it with the new metadataValue
function SetMultipleMetadata(metadataKey, metadataValue): CustomDecorator {
const decoratorFactory = (target, key, descriptor) => {
// .start: tweak
// descriptor?.value = function decorator; target = class decorator
const accumulatedVal =
Reflect.getMetadata(metadataKey, descriptor?.value ?? target) ?? [];
accumulatedVal.push(metadataValue);
// .end: tweak
if (descriptor) {
Reflect.defineMetadata(metadataKey, accumulatedVal, descriptor.value);
return descriptor;
}
Reflect.defineMetadata(metadataKey, accumulatedVal, target);
return target;
};
decoratorFactory.KEY = metadataKey;
return decoratorFactory as any;
}
export function RequireAllPermissions(
...permissions: PermissionsObjectPermission[]
) {
return SetMultipleMetadata(AUTH_FUNCTION_KEY, (req, user: RequestUser) =>
permissions.every(({ seqid }) => user.permissions.includes(seqid)),
);
}
export function RequireSomePermission(
...permissions: PermissionsObjectPermission[]
) {
return SetMultipleMetadata(AUTH_FUNCTION_KEY, (req, user: RequestUser) =>
permissions.some(({ seqid }) => user.permissions.includes(seqid)),
);
}
export function AuthenticateCondition(func: AuthenticationFunction) {
return SetMultipleMetadata(AUTH_FUNCTION_KEY, func);
}
export function Authenticated() {
return SetMultipleMetadata(AUTH_FUNCTION_KEY, () => true);
}
+13 -13
View File
@@ -11,9 +11,9 @@ import {
Authenticated,
RequireAllPermissions,
RequireSomePermission,
} from './authentication.decorator';
} from '../decorators/authentication.decorator';
import { AuthenticationGuard } from './authentication.guard';
import { PERMISSIONS } from './permissions.enum';
import { PERMISSIONS_GROUPS } from './permissions.enum';
import { AuthClientService } from '../modules/auth/auth.service';
import ErrorCodes from '../utils/errorCodes';
@@ -36,15 +36,15 @@ class NoClassAuthController {
}
@Get('required-permission')
@RequireSomePermission(PERMISSIONS.ZENDESK.permissions.OPEN)
@RequireSomePermission(PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN)
async requiredPermission(@Body() body) {
return { body };
}
@Get('has-all-permissions')
@RequireAllPermissions(
PERMISSIONS.ZENDESK.permissions.OPEN,
PERMISSIONS.METABASE.permissions.OPEN,
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE,
)
async hasAllPermissions(@Body() body) {
return { body };
@@ -52,8 +52,8 @@ class NoClassAuthController {
@Get('has-some-permission')
@RequireSomePermission(
PERMISSIONS.ZENDESK.permissions.OPEN,
PERMISSIONS.METABASE.permissions.OPEN,
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE,
)
async hasSomePermission(@Body() body) {
return { body };
@@ -62,7 +62,7 @@ class NoClassAuthController {
@Controller('class-auth-condition')
@AuthenticateCondition((req) => req.get('x-on-class') === 'ok')
@RequireSomePermission(PERMISSIONS.METABASE.permissions.OPEN)
@RequireSomePermission(PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE)
class ClassAuthConditionController {
@Get('body')
async getBody(@Body() body) {
@@ -77,7 +77,7 @@ class ClassAuthConditionController {
@Get('required-permission')
@AuthenticateCondition((req) => req.get('x-on-route') === 'ok')
@RequireSomePermission(PERMISSIONS.ZENDESK.permissions.OPEN)
@RequireSomePermission(PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN)
async requiredPermission(@Body() body) {
return { body };
}
@@ -440,17 +440,17 @@ describe('authentication.guard', () => {
NoClassAuthTest(null, null);
ClassAuthConditionTest(null, null);
const tokenZ = CreateToken([PERMISSIONS.ZENDESK.permissions.OPEN]);
const tokenZ = CreateToken([PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN]);
NoClassAuthTest(tokenZ, ['zendesk']);
ClassAuthConditionTest(tokenZ, ['zendesk']);
const tokenM = CreateToken([PERMISSIONS.METABASE.permissions.OPEN]);
const tokenM = CreateToken([PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE]);
NoClassAuthTest(tokenM, ['metabase']);
ClassAuthConditionTest(tokenM, ['metabase']);
const tokenZM = CreateToken([
PERMISSIONS.ZENDESK.permissions.OPEN,
PERMISSIONS.METABASE.permissions.OPEN,
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE,
]);
NoClassAuthTest(tokenZM, ['zendesk', 'metabase']);
ClassAuthConditionTest(tokenZM, ['zendesk', 'metabase']);
+2 -2
View File
@@ -13,8 +13,8 @@ import { AuthClientService } from '../modules/auth/auth.service';
import {
AuthenticationFunction,
AUTH_FUNCTION_KEY,
} from './authentication.decorator';
import { RequestUser } from './user.decorator';
} from '../decorators/authentication.decorator';
import { RequestUser } from '../decorators/user.decorator';
import ErrorBuilder from '../utils/ErrorBuilder';
import ErrorCodes from '../utils/errorCodes';
+223 -67
View File
@@ -37,11 +37,12 @@ export interface PermissionsObjectGroup {
/*export const Permissions: {
[P in keyof any]: { [Q in keyof any]: Permission };
} = {*/
export const PERMISSIONS = {
export const PERMISSIONS_GROUPS = {
AUTH: {
title: {
'pt-br': 'Autenticação',
'en-us': 'Authentication',
'es-es': 'Authentication',
},
permissions: {
CREATE: {
@@ -51,6 +52,7 @@ export const PERMISSIONS = {
name: {
'pt-br': '',
'en-us': '',
'es-es': '',
},
},
},
@@ -60,6 +62,7 @@ export const PERMISSIONS = {
title: {
'pt-br': 'Coletar | Pipelines',
'en-us': 'Collect | Pipelines',
'es-es': 'Colecta | Pipelines',
},
permissions: {
CREATE: {
@@ -69,6 +72,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Criar pipelines',
'en-us': 'Create pipelines',
'es-es': 'Crear pipelines',
},
},
GET: {
@@ -78,6 +82,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Buscar pipelines',
'en-us': 'Get pipelines',
'es-es': 'Obtener pipelines',
},
},
UPDATE: {
@@ -87,6 +92,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Atualizar pipeline',
'en-us': 'Update pipeline',
'es-es': 'Actualizar pipeline',
},
},
DELETE: {
@@ -96,69 +102,87 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Excluir pipeline',
'en-us': 'Delete pipeline',
'es-es': 'Eliminar pipeline',
},
},
},
},
INPUT: {
CONNECTION: {
title: {
'pt-br': 'Coletar | Conexões',
'en-us': 'Collect | Connections',
'pt-br': 'Coletar | Fontes de dados',
'en-us': 'Collect | Sources',
'es-es': 'Colecta | Sources',
},
permissions: {
CREATE: {
seqid: 21,
claim: 'POST /inputs',
usage: PermissionUsages.INTERNAL,
seqid: 37,
claim: 'connection:create',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Criar conexões',
'en-us': 'Create connections',
},
},
GET: {
seqid: 9,
claim: 'GET /inputs',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Buscar conexões',
'en-us': 'Get connections',
},
},
UPDATE: {
seqid: 27,
claim: 'PUT /inputs',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Atualizar conexões',
'en-us': 'Create connections',
'pt-br': 'Cadastrar Fonte',
'en-us': 'Create Source',
'es-es': 'Crear Source',
},
},
DELETE: {
seqid: 3,
claim: 'DELETE /inputs',
usage: PermissionUsages.INTERNAL,
seqid: 38,
claim: 'connection:delete',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Excluir conexões',
'en-us': 'Delete connections',
'pt-br': 'Excluir Fonte',
'en-us': 'Remove Source',
'es-es': 'Eliminar Source',
},
},
GET_ENTITIES: {
seqid: 10,
claim: 'GET /inputs/available-entities',
UPDATE: {
seqid: 39,
claim: 'connection:update',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Excluir conexões',
'en-us': 'Delete connections',
'pt-br': 'Editar Fonte',
'en-us': 'Edit Source',
'es-es': 'Editar Source',
},
},
},
},
NETWORK_CONFIG: {
title: {
'pt-br': 'Coletar | Redes',
'en-us': 'Collect | Network',
'es-es': 'Colecta | Network',
},
permissions: {
CREATE: {
seqid: 35,
claim: 'network_config:create',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Cadastrar VPN',
'en-us': 'Create VPN',
'es-es': 'Crear VPN',
},
},
// DELETE: {
// seqid: 36,
// claim: 'network_config:delete',
// usage: PermissionUsages.PUBLIC,
// name: {
// 'pt-br': 'Excluir VPN',
// 'en-us': 'Remove VPN',
// 'es-es': 'Eliminar VPN',
// },
// },
},
},
OUTPUT: {
title: {
'pt-br': 'Output',
'en-us': 'Output',
'es-es': 'Output',
},
permissions: {
CREATE: {
@@ -168,6 +192,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Criar outputs',
'en-us': 'Create outputs',
'es-es': 'Crear outputs',
},
},
GET: {
@@ -177,6 +202,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Buscar outputs',
'en-us': 'Get outputs',
'es-es': 'Obtener outputs',
},
},
UPDATE: {
@@ -186,6 +212,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Editar outputs',
'en-us': 'Edit outputs',
'es-es': 'Editar outputs',
},
},
DELETE: {
@@ -195,6 +222,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Excluir outputs',
'en-us': 'Remove outputs',
'es-es': 'Eliminar outputs',
},
},
},
@@ -204,6 +232,7 @@ export const PERMISSIONS = {
title: {
'pt-br': 'Micro-transformações',
'en-us': 'Micro-transformations',
'es-es': 'Micro-transformaciones',
},
permissions: {
CREATE: {
@@ -213,6 +242,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Criar micro-transformações',
'en-us': 'Create micro-transformations',
'es-es': 'Crear micro-transformaciones',
},
},
GET: {
@@ -222,6 +252,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Buscar micro-transformações',
'en-us': 'Get micro-transformations',
'es-es': 'Obtener micro-transformaciones',
},
},
UPDATE: {
@@ -231,6 +262,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Editar micro-transformações',
'en-us': 'Edit micro-transformations',
'es-es': 'Editar micro-transformaciones',
},
},
DELETE: {
@@ -240,6 +272,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Excluir micro-transformações',
'en-us': 'Remove micro-transformations',
'es-es': 'Eliminar micro-transformaciones',
},
},
},
@@ -249,6 +282,7 @@ export const PERMISSIONS = {
title: {
'pt-br': 'Explorar | Catálogo',
'en-us': 'Explore | Catalog',
'es-es': 'Explorar | Catalogar',
},
permissions: {
GET: {
@@ -256,35 +290,60 @@ export const PERMISSIONS = {
claim: 'GET /catalog',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Buscar data assets',
'en-us': 'Search for data assets',
'pt-br': 'Acessar lista de Ativos e visualizar atributos',
'en-us': 'Access Assets list and its attributes',
'es-es': 'Acceda a la lista de Activos y vea los atributos',
},
},
CREATE: {
seqid: 19,
claim: 'POST /catalog',
claim: 'catalog:create',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Criar atributos no catálogo',
'en-us': 'Create attributes',
'pt-br': 'Criar Ativos',
'en-us': 'Create Assets',
'es-es': 'Crear Activos',
},
},
UPDATE: {
seqid: 25,
claim: 'PUT /catalog',
usage: PermissionUsages.INTERNAL,
claim: 'catalog:edit',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Editar atributos no catálogo',
'en-us': 'Edit attributes',
'pt-br': 'Criar e editar atributos no catálogo',
'en-us': 'Create and edit attributes on the catalog',
'es-es': 'Crear y editar atributos en el catálogo',
},
},
DELETE: {
seqid: 1,
claim: 'DELETE /catalog',
claim: 'catalog:delete',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Excluir Ativos do catálogo',
'en-us': 'Remove Assets from the catalog',
'es-es': 'Eliminar Activos del catálogo',
},
},
DATA_MANAGER: {
seqid: 42,
claim: 'catalog:data_manager',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Excluir atributos no catálogo',
'en-us': 'Remove attributes',
'pt-br':
'Gerente do catálogo. Consegue vizualizar e editar todos os ativos.',
'en-us': 'Catalog manager. Able to view and edit all assets.',
'es-es': 'Gestor de catálogos. Puede ver y editar todos los activos.',
},
},
EMBED_ANALYTICS: {
seqid: 44,
claim: 'catalog:embed',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Acessar Módulo de Incorporação de Ativos',
'en-us': 'Access Embedding analytics Module',
'es-es': 'Acceder al Módulo de Incorporación de Activos',
},
},
},
@@ -294,6 +353,7 @@ export const PERMISSIONS = {
title: {
'pt-br': 'Conectores',
'en-us': 'Connectors',
'es-es': 'Conectores',
},
permissions: {
CREATE: {
@@ -303,6 +363,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Criar conectores',
'en-us': 'Create connectors',
'es-es': 'Crear conectores',
},
},
GET: {
@@ -312,6 +373,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Buscar conectores',
'en-us': 'Get connectors',
'es-es': 'Obtener conectores',
},
},
UPDATE: {
@@ -321,6 +383,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Editar conectores',
'en-us': 'Edit connectors',
'es-es': 'Editar conectores',
},
},
DELETE: {
@@ -330,6 +393,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Excluir conectores',
'en-us': 'Remove connectors',
'es-es': 'Eliminar conectores',
},
},
},
@@ -339,6 +403,7 @@ export const PERMISSIONS = {
title: {
'pt-br': 'Explorar | Consolidar',
'en-us': 'Explore | Data consolidation',
'es-es': 'Explorar | Consolidación de datos',
},
permissions: {
OPEN: {
@@ -348,6 +413,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Acessar Snowflake',
'en-us': 'Access Snowflake',
'es-es': 'Acceso Snowflake',
},
},
},
@@ -357,6 +423,7 @@ export const PERMISSIONS = {
title: {
'pt-br': 'Zendesk',
'en-us': 'Zendesk',
'es-es': 'Zendesk',
},
permissions: {
OPEN: {
@@ -366,44 +433,49 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Acessar Zendesk',
'en-us': 'Access Zendesk',
'es-es': 'Acceso Zendesk',
},
},
},
},
METABASE: {
ANALYZE: {
title: {
'pt-br': 'Analisar | Visualização',
'en-us': 'Analyze | Visualization',
'es-es': 'Analizar | Visualización',
},
permissions: {
OPEN: {
METABASE: {
seqid: 11,
claim: 'GET /metabase',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Acessar Metabase',
'en-us': 'Access Metabase',
'es-es': 'Acceso Metabase',
},
},
INTELLIGENCE: {
seqid: 31,
claim: 'intelligence:open',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Acessar Módulo de Inteligência',
'en-us': 'Access Intelligence Module',
'es-s': 'Acceder Módulo de Inteligencia',
},
},
},
},
ORCHEST: {
MODULES: {
title: {
'pt-br': 'Analisar | Inteligência',
'en-us': 'Analyze | Intelligence',
'pt-br': 'Módulos da Dadosfera',
'en-us': 'Dadosfera Modules',
'es-es': 'Módulos de la Dadosfera',
},
permissions: {
INTELLIGENCE: {
seqid: 31,
claim: 'intelligence:open',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Acessar Orchest',
'en-us': 'Access Orchest',
},
},
APP_BUILDER: {
seqid: 32,
claim: 'app-builder:open',
@@ -411,6 +483,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Acessar App Builder',
'en-us': 'Access App Builder',
'es-es': 'Acceder App Builder',
},
},
MACHINE_LEARNING: {
@@ -420,6 +493,7 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Acessar Machine Learning',
'en-us': 'Access Machine Learning',
'es-es': 'Acceder Machine Learning',
},
},
},
@@ -428,6 +502,7 @@ export const PERMISSIONS = {
title: {
'pt-br': 'Usuários',
'en-us': 'Users',
'es-es': 'Usuarios',
},
permissions: {
ADMIN: {
@@ -437,21 +512,97 @@ export const PERMISSIONS = {
name: {
'pt-br': 'Gestão de usuários e grupos',
'en-us': 'Manage user and roles',
'es-es': 'Administrar usuarios y roles',
},
},
},
},
PROCESS: {
title: {
'pt-br': 'Processar',
'en-us': 'Process',
'es-es': 'Procesar',
},
permissions: {
TRANSFORMATION: {
seqid: 43,
claim: 'process:open',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Acessar Módulo de Transformação',
'en-us': 'Access Transformation Module',
'es-es': 'Acceder Módulo de transformación',
},
},
},
},
DADOSFERA: {
title: {
'pt-br': 'Usuários',
'en-us': 'Users',
'es-es': 'Usuarios',
},
permissions: {
CONNECTIONS_MIGRATION: {
seqid: 40,
claim: 'dadosfera:connections-migration',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'connections-migration',
'en-us': 'connections-migration',
'es-es': 'connections-migration',
},
},
SYNC_USER: {
seqid: 41,
claim: 'dadosfera:sync-user',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'sync-user',
'en-us': 'sync-user',
'es-es': 'sync-user',
},
},
},
},
};
export interface DadosferaModule {
name: string;
description: string;
key: string;
permissionSeqId: number;
}
export const DADOSFERA_MODULES: Array<DadosferaModule> = [
{
name: 'Intelligence Module',
description: 'Orchest Module',
key: 'intelligence',
permissionSeqId: PERMISSIONS_GROUPS.ANALYZE.permissions.INTELLIGENCE.seqid,
},
{
name: 'Proccessing Module',
description: 'Proccessing Module',
key: 'process',
permissionSeqId:
PERMISSIONS_GROUPS.PROCESS.permissions.TRANSFORMATION.seqid,
},
{
name: 'Embedded Analytics',
description: 'Embedded Analytics Module',
key: 'embedded-analytics',
permissionSeqId:
PERMISSIONS_GROUPS.PROCESS.permissions.TRANSFORMATION.seqid,
},
];
// traverses the object searching for duplicate seqids or claims (executes at runtime)
let nextAvailableSeqid = 0;
const seqids = Object.values(PERMISSIONS).flatMap((namespace) =>
const seqids = Object.values(PERMISSIONS_GROUPS).flatMap((namespace) =>
Object.values(namespace.permissions).map(({ seqid }) => seqid),
);
const claims = Object.values(PERMISSIONS).flatMap((namespace) =>
const claims = Object.values(PERMISSIONS_GROUPS).flatMap((namespace) =>
Object.values(namespace.permissions).map(({ claim }) => claim),
);
Object.values(PERMISSIONS).map((namespace) =>
Object.values(PERMISSIONS_GROUPS).map((namespace) =>
Object.values(namespace.permissions).map(({ seqid, claim }) => {
const seqidsCount = seqids.filter((x) => x === seqid).length;
assert(seqidsCount === 1, `seqid ${seqid} count is not 1`);
@@ -462,4 +613,9 @@ Object.values(PERMISSIONS).map((namespace) =>
}),
);
DADOSFERA_MODULES.map((m) => m.key).forEach((m, i, arr) => {
if (arr.indexOf(m) !== i)
throw new Error(`DADOSFERA_MODULES[${i}] does not have a unique key`);
});
logger.log(`next available seqid ${nextAvailableSeqid + 1}`);
-26
View File
@@ -1,26 +0,0 @@
import { credentials } from '@grpc/grpc-js';
import { ClientOptions, Transport } from '@nestjs/microservices';
import { ProtoPackages, ProtoPaths } from '@dadosfera/protospack-v2/dist/lib/Duc';
export class DucClient {
config(): ClientOptions {
return {
transport: Transport.GRPC,
options: {
url: process.env.DUC_URL,
package: ProtoPackages.WritePackage,
// credentials: credentials.createSsl(),
credentials: process.env.LOCAL_ENV
? undefined
: credentials.createSsl(),
protoPath: ProtoPaths.WriteFilePath,
loader: {
keepCase: true,
enums: String,
objects: true,
arrays: true,
},
},
};
}
}
-25
View File
@@ -1,25 +0,0 @@
import { credentials } from '@grpc/grpc-js';
import { ClientOptions, Transport } from '@nestjs/microservices';
import { InputPackages, InputProtoFilePath } from 'protospack';
export class InputsClientConfiguration {
config(): ClientOptions {
return {
transport: Transport.GRPC,
options: {
url: process.env.INFACTORY_URL,
package: InputPackages,
credentials: process.env.LOCAL_ENV
? undefined
: credentials.createSsl(),
protoPath: InputProtoFilePath,
loader: {
enums: String,
objects: true,
arrays: true,
},
},
};
}
}
-310
View File
@@ -1,310 +0,0 @@
import {
OnModuleInit,
Inject,
HttpException,
HttpStatus,
NotFoundException,
InternalServerErrorException,
} from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import {
DecodeGrpcStruct,
EncodeJsonToGrpcStruct,
InputCreateS3Request,
InputNewCreateRequest,
InputService,
TestConnectionGetColumnsRequest,
TestConnectionRequest,
} from 'protospack';
import axios, { AxiosRequestConfig } from 'axios';
import { lastValueFrom } from 'rxjs';
import { InputModel } from 'src/modules/inputs/dtos/input.model';
import {
objectCamelToSnake,
objectSnakeToCamel,
} from 'src/utils/CaseConverter';
import { mustache } from 'src/utils/mustache';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { OauthSecrets } from 'src/utils/OauthSecrets';
import { URLSearchParams } from 'url';
import { IIdRequest, UpdateInputRequest } from './interfaces';
export class InputsClientService implements OnModuleInit {
private inputService: InputService;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject('INPUTS_PACKAGE') private readonly grpcClient: ClientGrpc,
@Inject('OAUTH_SECRETS') private readonly oauthSecrets: OauthSecrets,
) {
this.logger = dadosferaLogger.logger;
}
async onModuleInit() {
this.inputService =
this.grpcClient.getService<InputService>('InputService');
}
async newCreate(createInputDto: InputNewCreateRequest) {
this.logger.info('InputClientService - Create');
const input = await lastValueFrom(
this.inputService.NewCreate(objectSnakeToCamel(createInputDto)),
);
objectCamelToSnake(input);
return input;
}
async createGeneric(createInputGeneric) {
this.logger.info('InputClientService - Create');
const { info, ...data } = createInputGeneric;
// data.credentials = await this.getInputTokens(data);
if (data.options) delete data.options;
if (data.credentials.oauth_code) delete data.credentials.oauth_code;
const grpcPayload = {
input: EncodeJsonToGrpcStruct(data),
info,
};
objectSnakeToCamel(grpcPayload);
const structReturn = await lastValueFrom(
this.inputService.Create(grpcPayload),
).catch((err: { details: string }) => {
if (err.details === 'Item Not found!') {
throw new NotFoundException('Input not found');
}
throw new InternalServerErrorException(err.details);
});
objectCamelToSnake(structReturn);
const inputCreated = DecodeGrpcStruct(structReturn.input);
return { input: inputCreated };
}
async createS3Inputs(createInputDto: InputCreateS3Request) {
this.logger.info('InputClientService - Create');
objectSnakeToCamel(createInputDto);
const createInputResponse = await lastValueFrom(
this.inputService.CreateS3(createInputDto),
).catch((e) => {
throw new HttpException(e.details, 500);
});
this.logger.info('Done');
objectCamelToSnake(createInputResponse);
return createInputResponse;
}
async findOne(data: IIdRequest) {
this.logger.info('InputClientService - FindOne');
const findOneInputResponse = await lastValueFrom(
this.inputService.FindOne(objectSnakeToCamel(data)),
).catch((e) => {
this.logger.error(e.details);
throw new HttpException(e.details, HttpStatus.INTERNAL_SERVER_ERROR);
});
objectCamelToSnake(findOneInputResponse);
return findOneInputResponse;
}
async findAll(data) {
this.logger.info('InputClientService - FindAll');
const findAllInputResponse = await lastValueFrom(
this.inputService.FindAll(objectSnakeToCamel(data)),
).catch((e) => {
this.logger.error(e.details);
throw new HttpException(e.details, HttpStatus.INTERNAL_SERVER_ERROR);
});
findAllInputResponse.inputs.forEach((input) => objectCamelToSnake(input));
return findAllInputResponse;
}
async update(updateInputDTO: UpdateInputRequest) {
this.logger.info('InputClientService - Update');
const updateInputResponse = await new Promise((resolve, reject) => {
this.inputService.Update(objectSnakeToCamel(updateInputDTO)).subscribe({
next(x) {
resolve(objectCamelToSnake(x));
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return updateInputResponse;
}
async remove(idRequest: IIdRequest) {
this.logger.info('InputClientService - Remove');
const removeInputResponse = await new Promise((resolve, reject) => {
this.inputService.Remove(objectSnakeToCamel(idRequest)).subscribe({
next(x) {
resolve(objectCamelToSnake(x));
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return removeInputResponse;
}
async testConnection(data: TestConnectionRequest) {
this.logger.info('InputClientService - TestConnection');
const testConnectionResponse = await new Promise((resolve, reject) => {
this.inputService.NewTestConnection(objectSnakeToCamel(data)).subscribe({
next(x) {
resolve(objectCamelToSnake(x));
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return testConnectionResponse;
}
async getColumns(data: TestConnectionGetColumnsRequest) {
this.logger.info('InputClientService - TestConnection/Get-Columns');
const getColumnsResponse = await new Promise((resolve, reject) => {
this.inputService.GetColumns(objectSnakeToCamel(data)).subscribe({
next(x) {
resolve(objectCamelToSnake(x));
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return getColumnsResponse;
}
async getAuthSecrets(plugin) {
switch (plugin) {
case 'hubspot':
return {
client_id: this.oauthSecrets.hubspot.client_id || '',
client_secret: this.oauthSecrets.hubspot.client_secret || '',
};
}
return {};
}
async getInputTokens(input: InputModel) {
const { credentials } = input;
let credentialsTokens = {};
switch (credentials.connection_type) {
case 'oauth':
credentialsTokens = await this.getOauthTokens(input);
break;
}
return { ...credentials, ...credentialsTokens };
}
async getOauthTokens(input: InputModel) {
const tokens: Record<string, any> = {};
const { options, credentials } = input;
const { oauth } = options;
const { get_tokens_url_params, get_tokens_set_response } = oauth;
const secrets = await this.getAuthSecrets(input.plugin);
const get_tokens_url_params_string = mustache(get_tokens_url_params, {
...secrets,
...credentials,
});
let get_tokens_url_params_obj: Record<string, any> = {};
try {
get_tokens_url_params_obj = JSON.parse(get_tokens_url_params_string);
} catch (error) {
throw new HttpException('Erro transformando get_tokens_url_params', 400);
}
let params = {};
switch (oauth.content_type) {
case 'application/x-www-form-urlencoded':
params = new URLSearchParams(get_tokens_url_params_obj);
params = params.toString();
break;
default:
break;
}
const axiosRequestConfig: AxiosRequestConfig = {
url: oauth.get_tokens_url,
method: oauth.get_tokens_method || 'POST',
data: params,
headers: { 'content-type': oauth.content_type || 'application/json' },
};
const { data } = await axios(axiosRequestConfig).catch((err) => {
this.logger.error(err.response.data);
throw new HttpException(err.response.data.message, err.response.status);
});
for (const key in get_tokens_set_response) {
const responseKey = get_tokens_set_response[key];
tokens[key] = data[responseKey];
}
const { redirect_uri } = get_tokens_url_params_obj;
tokens.redirect_uri = redirect_uri;
return tokens;
}
async getAvailableEntities(data) {
this.logger.info('InputClientService - GetAvailableEntities');
objectSnakeToCamel(data);
const response = await lastValueFrom(
this.inputService.GetAvailableEntities(data),
).catch((err) => {
this.logger.info(err.details);
if (err.details && err.details.includes('400'))
throw new HttpException('Plugin inválido', HttpStatus.BAD_REQUEST);
throw new HttpException(err.details, HttpStatus.INTERNAL_SERVER_ERROR);
});
objectCamelToSnake(response);
return response;
}
}
-24
View File
@@ -1,24 +0,0 @@
import { OutputPackages, OutputProtoFilePath } from 'protospack';
import { ClientOptions, Transport } from '@nestjs/microservices';
import { credentials } from '@grpc/grpc-js';
export class OutputsClientConfiguration {
config(): ClientOptions {
return {
transport: Transport.GRPC,
options: {
url: process.env.OTFACTORY_URL,
package: OutputPackages,
credentials: process.env.LOCAL_ENV
? undefined
: credentials.createSsl(),
protoPath: OutputProtoFilePath,
loader: {
enums: String,
objects: true,
arrays: true,
},
},
};
}
}
-178
View File
@@ -1,178 +0,0 @@
import { Controller, Inject, OnModuleInit } from '@nestjs/common';
import { ClientGrpc, Payload } from '@nestjs/microservices';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { OutputServicesNames, OutputsServiceInterface } from 'protospack';
import {
objectCamelToSnake,
objectSnakeToCamel,
} from 'src/utils/CaseConverter';
import {
ICreateOutputRequest,
IIdRequest,
IOutputUpdateRequest,
} from './interfaces';
@Controller('output')
export class OutputsClientService implements OnModuleInit {
private outputService: OutputsServiceInterface;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject('OUTPUTS_PACKAGE') private readonly grpcClient: ClientGrpc,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.outputService = this.grpcClient.getService<OutputsServiceInterface>(
OutputServicesNames.OutputService,
);
}
async create(@Payload() createOutputDto: ICreateOutputRequest) {
this.logger.info('OutputClientService - Create');
const createOutputResponse = await new Promise((resolve, reject) => {
this.outputService.Create(objectSnakeToCamel(createOutputDto)).subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
const convertedItem = objectCamelToSnake(createOutputResponse['output']);
createOutputResponse['output'] = convertedItem;
return createOutputResponse;
}
async findAll(data) {
this.logger.info('OutputClientService - FindAll');
const findAllOutputResponse = await new Promise((resolve, reject) => {
this.outputService.FindAll(objectSnakeToCamel(data)).subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
const convertedOutputs = findAllOutputResponse['outputs'].map((ot) => {
return objectCamelToSnake(ot);
});
return { outputs: convertedOutputs };
}
async findOne(data: IIdRequest) {
this.logger.info('OutputClientService - FindOne');
const findOneOutputResponse = await new Promise((resolve, reject) => {
this.outputService.FindOne(objectSnakeToCamel(data)).subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return objectCamelToSnake(findOneOutputResponse);
}
async update(updateOutPutDTO: IOutputUpdateRequest) {
this.logger.info('OutputClientService - Update');
const updateOutputResponse = await new Promise((resolve, reject) => {
this.outputService.Update(objectSnakeToCamel(updateOutPutDTO)).subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return objectCamelToSnake(updateOutputResponse);
}
async remove(data: IIdRequest) {
this.logger.info('OutputClientService - Remove');
const removeOutputResponse = await new Promise((resolve, reject) => {
this.outputService.Remove(objectSnakeToCamel(data)).subscribe({
next(x) {
resolve(objectCamelToSnake(x));
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return removeOutputResponse;
}
}
-29
View File
@@ -1,29 +0,0 @@
export interface ICreateOutputRequest {
operation: string;
name: string;
plugin: string;
values: Values;
}
export interface Values {
prefix: string;
}
interface Info {
user_id: string;
customer_id: string;
}
export interface IIdRequest {
id: string;
info: Info;
}
export interface IOutputUpdateRequest {
id: string;
operation: string;
values: Values;
plugin: string;
name: string;
info: Info;
}
-24
View File
@@ -1,24 +0,0 @@
import { ClientOptions, Transport } from '@nestjs/microservices';
import { PipelinePackages, PipelineProtoFilePath } from 'protospack';
import { credentials } from '@grpc/grpc-js';
export class PipelinesClientConfiguration {
config(): ClientOptions {
return {
transport: Transport.GRPC,
options: {
url: process.env.PIFACTORY_URL,
package: PipelinePackages,
credentials: process.env.LOCAL_ENV
? undefined
: credentials.createSsl(),
protoPath: PipelineProtoFilePath,
loader: {
enums: String,
objects: true,
arrays: true,
},
},
};
}
}
-273
View File
@@ -1,273 +0,0 @@
import {
BadRequestException,
Inject,
InternalServerErrorException,
OnModuleInit,
} from '@nestjs/common';
import { ClientGrpc, Payload } from '@nestjs/microservices';
import { PipelineServicesNames, PipelinesServiceInterface } from 'protospack';
import { lastValueFrom } from 'rxjs';
import {
objectCamelToSnake,
objectSnakeToCamel,
} from 'src/utils/CaseConverter';
import {
ICreatePipelineDto,
IGetPipelineLogsRequest,
IIdRequest,
IUpdatePipelineRequest,
} from './interfaces';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
export class PipelinesClientService implements OnModuleInit {
private pipelineService: PipelinesServiceInterface;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject('PIPELINES_PACKAGE') private readonly grpcClient: ClientGrpc,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.pipelineService =
this.grpcClient.getService<PipelinesServiceInterface>(
PipelineServicesNames.PipelineService,
);
}
async create(@Payload() createPipelineDto: ICreatePipelineDto) {
this.logger.info('PipelinesClientService - Create');
const createPipelineResponse = await lastValueFrom(
this.pipelineService.Create(objectSnakeToCamel(createPipelineDto)),
)
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((error: { details: string }) => {
if (error.details.includes('INVALID_REQUEST')) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [errorType, message] = error.details.split('|');
throw new BadRequestException(message);
}
throw new InternalServerErrorException(error.details);
});
return createPipelineResponse;
}
async findAll(data: IIdRequest) {
this.logger.info('PipelinesClientService - FindAll');
const findAllPipelineResponse = await new Promise((resolve, reject) => {
this.pipelineService.FindAll(objectSnakeToCamel(data)).subscribe({
next(x) {
resolve(x.pipelines);
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return findAllPipelineResponse;
}
async findOne(data: IIdRequest) {
this.logger.info('PipelinesClientService - FindOne');
const findOnePipelineResponse = await new Promise((resolve, reject) => {
this.pipelineService.FindOne(objectSnakeToCamel(data)).subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return findOnePipelineResponse;
}
async update(UpdatePipelineRequest: IUpdatePipelineRequest) {
this.logger.info('PipelinesClientService - Update');
const updatePipelineResponse = await new Promise((resolve, reject) => {
this.pipelineService
.Update(objectSnakeToCamel(UpdatePipelineRequest))
.subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
//console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return updatePipelineResponse;
}
async remove(data: IIdRequest) {
this.logger.info('PipelinesClientService - Remove');
const removePipelineResponse = await new Promise((resolve, reject) => {
this.pipelineService.remove(objectSnakeToCamel(data)).subscribe({
next(x) {
resolve(objectCamelToSnake(x));
},
error(err) {
reject(err);
},
complete() {
// this.logger.info('Done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return removePipelineResponse;
}
async getPipelineLogsMessages(data: IGetPipelineLogsRequest) {
this.logger.info('PipelinesClientService - GetPipelineLogsMessages');
const logsPipelineResponse = await new Promise((resolve, reject) => {
this.pipelineService.getPipelineLogs(data).subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
// this.logger.info('Done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return logsPipelineResponse;
}
async getPipelineStatus(data) {
this.logger.info('PipelinesClientService - GetPipelineStatus');
const statusPipelineResponse = await new Promise((resolve, reject) => {
this.pipelineService
.getPipelineStatus(objectSnakeToCamel(data))
.subscribe({
next(x) {
const statusArray = x.status.sort((a, b) => {
if (a.id < b.id) {
return 1;
} else {
return -1;
}
});
resolve({ status: statusArray });
},
error(err) {
reject(err);
},
complete() {
// this.logger.info('Done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return statusPipelineResponse;
}
async runPipeline({ id, info }: IIdRequest) {
this.logger.info('PipelinesClientService - RunPipeline');
const statusPipelineResponse = await new Promise((resolve, reject) => {
this.pipelineService
.triggerPipeline(objectSnakeToCamel({ id, info }))
.subscribe({
next(x) {
if (x.status == false) {
reject(
'This pipeline is not ready yet to execute, Try again later!',
);
}
resolve(x);
},
error(err) {
this.logger.error('Observable Error');
reject(err);
},
complete() {
//this.logger.info('Done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return statusPipelineResponse;
}
}
@@ -1,25 +0,0 @@
import { ClientOptions, Transport } from '@nestjs/microservices';
import {
TransformationPackages,
TransformationProtoFilePath,
} from 'protospack';
import { credentials } from '@grpc/grpc-js';
export class TransformationsClientConfiguration {
config(): ClientOptions {
return {
transport: Transport.GRPC,
options: {
url: process.env.INFACTORY_URL,
package: TransformationPackages,
credentials: credentials.createSsl(),
protoPath: TransformationProtoFilePath,
loader: {
enums: String,
objects: true,
arrays: true,
},
},
};
}
}
@@ -1,194 +0,0 @@
import { Controller, Inject, OnModuleInit, Post } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { TransformationsServiceInterface } from 'protospack';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
objectCamelToSnake,
objectSnakeToCamel,
} from 'src/utils/CaseConverter';
import { ICreateTransformationsRequest, IIdRequest } from './interfaces';
@Controller('transformation')
export class TransformationsClientService implements OnModuleInit {
private transformationService: TransformationsServiceInterface;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject('TRANSFORMATIONS_PACKAGE') private readonly grpcClient: ClientGrpc,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.transformationService =
this.grpcClient.getService<TransformationsServiceInterface>(
'TransformationService',
);
}
@Post()
async create(createTransformationsDto: ICreateTransformationsRequest) {
this.logger.info('TransformationClientService - Create');
const createTransformationResponse = await new Promise(
(resolve, reject) => {
this.transformationService
.Create(objectSnakeToCamel(createTransformationsDto))
.subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
},
)
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
const convertedTransforms = createTransformationResponse[
'transformations'
].map((tr) => {
return objectCamelToSnake(tr);
});
return { transformations: convertedTransforms };
}
async findAll(data) {
this.logger.info('TransformationClientService - FindAll');
const findAllTransformationResponse = await new Promise(
(resolve, reject) => {
this.transformationService.FindAll(objectSnakeToCamel(data)).subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
},
)
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
const convertedTransforms = findAllTransformationResponse[
'transformations'
].map((tr) => {
return objectCamelToSnake(tr);
});
return { transformations: convertedTransforms };
}
async findOne(data: IIdRequest) {
this.logger.info('TransformationClientService - FindOne');
const findOneTransformationResponse = await new Promise(
(resolve, reject) => {
this.transformationService.FindOne(objectSnakeToCamel(data)).subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
},
)
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return objectCamelToSnake(findOneTransformationResponse);
}
async update(updateTransformationDTO) {
this.logger.info('TransformationClientService - Update');
const updateTransformationResponse = await new Promise(
(resolve, reject) => {
this.transformationService
.Update(objectSnakeToCamel(updateTransformationDTO))
.subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
},
)
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return objectCamelToSnake(updateTransformationResponse);
}
async remove(data: IIdRequest) {
this.logger.info('TransformationClientService - Remove');
const removeTransformationResponse = await new Promise(
(resolve, reject) => {
this.transformationService.Remove(objectSnakeToCamel(data)).subscribe({
next(x) {
resolve(x);
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
},
)
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return objectCamelToSnake(removeTransformationResponse);
}
}
@@ -0,0 +1,41 @@
import { applyDecorators } from '@nestjs/common';
import { ApiSecurity } from '@nestjs/swagger';
import { type Request } from 'express';
import { type PermissionsObjectPermission } from '../authentication/permissions.enum';
import { type RequestUser } from './user.decorator';
import { SetMultipleMetadata } from './shared';
export const AUTH_FUNCTION_KEY = '__AUTH_FUNCTION__';
export type AuthenticationFunction = (req: Request, user: any) => boolean;
export function RequireAllPermissions(
...permissions: PermissionsObjectPermission[]
) {
return createAuthenticatedDecorator((req, user: RequestUser) =>
permissions.every(({ seqid }) => user.permissions.includes(seqid)),
);
}
export function RequireSomePermission(
...permissions: PermissionsObjectPermission[]
) {
return createAuthenticatedDecorator((req, user: RequestUser) =>
permissions.some(({ seqid }) => user.permissions.includes(seqid)),
);
}
export function AuthenticateCondition(func: AuthenticationFunction) {
return createAuthenticatedDecorator(func);
}
export function Authenticated() {
return createAuthenticatedDecorator(() => true);
}
function createAuthenticatedDecorator(metadataValue: AuthenticationFunction) {
return applyDecorators(
ApiSecurity('access-token'),
SetMultipleMetadata(AUTH_FUNCTION_KEY, metadataValue),
);
}
+25
View File
@@ -0,0 +1,25 @@
import { createParamDecorator, type ExecutionContext } from '@nestjs/common';
import { LanguageEnum } from 'src/utils/languages.enum';
export const Language: () => ParameterDecorator = createParamDecorator(
(options: any, ctx: ExecutionContext): LanguageEnum => {
const headers = ctx.switchToHttp()?.getRequest()?.headers;
if (!headers) return LanguageEnum.ptbr;
let language: LanguageEnum =
headers['dadosfera-lang'] ?? headers['accept-language'];
language = language?.toLowerCase().trim() as LanguageEnum;
if (!language) return LanguageEnum.ptbr;
Object.values(LanguageEnum).some((l) => {
if (language === l || l.includes(language)) {
language = l;
return true;
}
language = LanguageEnum.enus;
});
return language;
},
);
+28
View File
@@ -0,0 +1,28 @@
import { type CustomDecorator } from '@nestjs/common';
// implementation copied from SetMetadata, but tweaked to get existing values
// of the metadataKey and accumulate it with the new metadataValue
export function SetMultipleMetadata(
metadataKey,
metadataValue,
): CustomDecorator {
const decoratorFactory: CustomDecorator = (target, key?, descriptor?) => {
// .start: tweak
// descriptor?.value = function decorator; target = class decorator
const accumulatedVal =
Reflect.getMetadata(metadataKey, descriptor?.value ?? target) ?? [];
accumulatedVal.push(metadataValue);
// .end: tweak
if (descriptor) {
Reflect.defineMetadata(metadataKey, accumulatedVal, descriptor.value);
return descriptor;
}
Reflect.defineMetadata(metadataKey, accumulatedVal, target);
return target;
};
decoratorFactory.KEY = metadataKey;
return decoratorFactory;
}
+13
View File
@@ -0,0 +1,13 @@
import { applyDecorators } from '@nestjs/common';
import { ApiExcludeController, ApiExcludeEndpoint } from '@nestjs/swagger';
export function ApiInternalOnlyEndpoint() {
if (process.env.INTERNAL_SWAGGER !== 'true')
return applyDecorators(ApiExcludeEndpoint());
return () => null;
}
export function ApiInternalOnlyController() {
if (process.env.INTERNAL_SWAGGER !== 'true')
return applyDecorators(ApiExcludeController());
return () => null;
}
@@ -1,17 +1,14 @@
import request from 'supertest';
import { Test } from '@nestjs/testing';
import { HttpStatus, INestApplication } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import jwt from 'jsonwebtoken';
import { Controller, Get } from '@nestjs/common';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { AuthenticationGuard } from './authentication.guard';
import { Controller, Get, HttpStatus, INestApplication } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { Test } from '@nestjs/testing';
import jwt from 'jsonwebtoken';
import request from 'supertest';
import { AuthenticationGuard } from '../authentication/authentication.guard';
import { PERMISSIONS_GROUPS } from '../authentication/permissions.enum';
import { AuthClientService } from '../modules/auth/auth.service';
import ErrorCodes from '../utils/errorCodes';
import { User } from './user.decorator';
import { PERMISSIONS } from './permissions.enum';
const logger = {
info: (...args) => args,
@@ -49,7 +46,7 @@ describe('user.decorator', () => {
const fakeUserPayload = {
user_id: 'd50d33c7-6c2b-463c-861f-e21667e7c125',
username: 'super.admin',
permissions: [PERMISSIONS.METABASE.permissions.OPEN].map(
permissions: [PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE].map(
({ seqid }) => seqid,
),
customer_id: '9d18e8ae-24b9-41a3-9e8f-a25ce57555b11',
@@ -1,4 +1,4 @@
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { createParamDecorator, type ExecutionContext } from '@nestjs/common';
import ErrorBuilder from '../utils/ErrorBuilder';
import ErrorCodes from '../utils/errorCodes';
@@ -20,6 +20,5 @@ export const User: (options?: { required?: boolean }) => ParameterDecorator =
if (!request.user && options?.required) {
throw new ErrorBuilder(ErrorCodes.AUTH.UNAUTHORIZED);
}
return request.user;
});
+18 -3
View File
@@ -1,16 +1,31 @@
import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
} from '@nestjs/common';
import { Response } from 'express';
import ErrorBuilder from '../utils/ErrorBuilder';
/**
* ExceptionFilter to handle RpcException thrown by our microservices
*
* If it detects that the exception is already an **HttpException** or it is generated by our **ErrorBuilder**,
* it will return the exception without any treatment.
*/
@Catch(Error)
export class GrpcToHttpExceptionFilter implements ExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
// return this exception directly if is already of type ErrorBuilder,
// else transform it using the ErrorBuilder
if (exception instanceof HttpException) {
return response
.status(exception.getStatus())
.json(exception.getResponse());
}
const err =
exception.constructor.name === ErrorBuilder.name
? exception
+47 -16
View File
@@ -4,9 +4,10 @@ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import helmet from 'helmet';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import documentEmpty from '../swagger_empty.json';
import { AppModule } from './app.module';
import { writeFileSync } from 'fs';
import { execSync } from 'child_process';
import { INestApplication } from '@nestjs/common';
async function bootstrap() {
DadosferaLogger.setupLogger({
serviceName: 'maestro',
@@ -23,29 +24,59 @@ async function bootstrap() {
optionsSuccessStatus: 204,
},
});
app.use(helmet());
configureSwagger(app);
await app.listen(3333);
if (process.env.KILL_AFTER_START) await app.close();
}
function configureSwagger(app: INestApplication) {
let branchName = '';
try {
branchName = execSync('git rev-parse --abbrev-ref HEAD')
.toString()
.replace('\n', '')
.trim();
} catch (error) {
console.warn("Can't get branch name", error);
}
const swaggerTitle = branchName ? `Maestro - ${branchName}` : 'Maestro';
const config = new DocumentBuilder()
.setTitle('Maestro')
.setTitle(swaggerTitle)
.setDescription('Documentation for Maestro gateway')
.setVersion('1.0')
.addBearerAuth()
.addServer(
process.env.ENV === 'local'
? 'http://127.0.0.1:3333'
: 'https://maestro.dev.dadosfera.ai',
)
.addSecurity('access-token', {
type: 'apiKey',
name: 'Authorization',
in: 'header',
})
.setDescription('This is the Maestro API')
.addServer('https://maestro.stg.dadosfera.ai', 'Ambiente STG')
.addServer('https://maestro-2.stg.dadosfera.ai', 'Ambiente STG2')
.addServer('https://maestro.dadosfera.ai', 'Ambiente PRD')
.build();
const document = SwaggerModule.createDocument(app, config);
if (process.env.ENV != 'stg' && process.env.ENV != 'prd')
SwaggerModule.setup('api', app, document);
const swaggerJsonFile =
process.env.INTERNAL_SWAGGER === 'true'
? 'docsfera.json'
: 'docsfera.external.json';
const swaggerDoc = process.env.ENV === 'dev' ? document : documentEmpty;
SwaggerModule.setup('api', app, swaggerDoc);
if (process.env.ENV === 'local') SwaggerModule.setup('api', app, document);
await app.listen(3333);
writeFileSync(
swaggerJsonFile,
JSON.stringify(
{
service: '${DOCS_API_TOKEN}',
pageId: '${DOCS_PAGE_ID}',
blockId: '${DOCS_BLOCK_ID}',
schema: document,
},
null,
2,
),
);
}
bootstrap();
+38
View File
@@ -0,0 +1,38 @@
# Auth
## SSO/oAuth
### Strategy
We are using [PassportJs](https://www.passportjs.org/) to handle oAuth authentications.
When the client (front end) makes a `GET /auth/oauth/{strategy}` Passport automatically redirects the user to the `strategy` login page. To do that we must configure and use a **Passport Strategy**. We must also have a callback route, conventionally `GET /auth/oauth/{strategy}/callback`, so the oAuth app can report the status of the user's login.
- If the oAuth is successfull we call DUC's `AuthOauthSignIn` request that gets the tokens from Cognito and saves them on cache temporarily under a key we call `session`. Duc returns that `session` to maestro which then redirects the user to our app login page with that `session` as a query param.
- If the oAuth login is not successfull for some reason or the user **does not** exist on DUC's database we redirect the user to our login page with an `error` and `error_description` as query params.
### Routes
So in order to have an SSO login, besides configuring the Strategy, we must have two routes for each Strategy, like in the example below:
```ts
@Get('oauth/google')
@UseGuards(AuthGuard('google-login'))
googleOauth() {
this.logger.info('/oauth/google');
return true;
}
@Get('oauth/google/callback')
@UseGuards(AuthGuard('google-login'))
@Redirect()
async googleOauthCallback(@Req() req) {
const { url, email, token, language = 'pt-br' } = await this.callback(req);
if (url.searchParams.get('error')) {
this.logger.error('/oauth/google - ERROR');
return { url: url.href };
}
// ... Rest of the logic
return { url: url.href };
}
```
+191 -28
View File
@@ -7,12 +7,19 @@ import {
HttpStatus,
Inject,
UseFilters,
Query,
Get,
UseGuards,
Redirect,
Req,
Param,
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import {
AuthSignInRequest,
AuthRefreshAccessTokenRequest,
ApiHeaders,
ApiOkResponse,
ApiSecurity,
ApiTags,
} from '@nestjs/swagger';
import {
AuthChangePasswordRequest,
AuthResetPasswordRequest,
AuthVerifyResetPasswordCodeRequest,
@@ -22,21 +29,37 @@ import {
AuthVerifyTotpMfaRequest,
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { PERMISSIONS } from 'src/authentication/permissions.enum';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import {
Authenticated,
RequireAllPermissions,
} from 'src/authentication/authentication.decorator';
} from 'src/decorators/authentication.decorator';
import { AuthClientService } from './auth.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { GrpcToHttpExceptionFilter } from '../../error/grpc-to-http-exception.filter';
import { RequestUser, User } from 'src/authentication/user.decorator';
import { RequestUser, User } from 'src/decorators/user.decorator';
import {
AuthRefreshAccessTokenReq,
AuthRefreshAccessTokenRes,
AuthSignInReq,
AuthSignInRes,
} from './dtos/login';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { AuthGuard } from '@nestjs/passport';
import { Request } from 'express';
import ErrorCodes, { OauthErrors } from 'src/utils/errorCodes';
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';
@ApiTags('Auth')
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
@UseFilters(new GrpcToHttpExceptionFilter())
@Controller('auth')
export class AuthController {
logger: DadosferaLogger;
redirectUrl: string;
constructor(
@Inject(DadosferaLogger)
@@ -44,10 +67,51 @@ 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`;
}
}
@Post('sign-in')
@HttpCode(HttpStatus.OK)
async signIn(
@Body() { username, password, totp }: AuthSignInReq,
@Language() language: LanguageEnum,
): Promise<AuthSignInRes> {
this.logger.info('/auth - SignIn');
const metadata = PackTheMetadata({ language });
return this.authClient.signIn({ username, password, totp }, metadata);
}
@Post('refresh-access-token')
@HttpCode(HttpStatus.OK)
@ApiOkResponse({ type: AuthRefreshAccessTokenRes })
async refreshAccessToken(
@Body() body: AuthRefreshAccessTokenReq,
@Language() language: LanguageEnum,
) {
this.logger.info('/auth - RefreshAccessToken');
const { refreshToken, customerName: customer_name } = body;
const metadata = PackTheMetadata({
customer_name,
language,
});
return this.authClient.refreshAccessToken({ refreshToken }, metadata);
}
@ApiInternalOnlyEndpoint()
@Post('/sso/snowflake')
@RequireAllPermissions(PERMISSIONS.SNOWFLAKE.permissions.OPEN)
@RequireAllPermissions(PERMISSIONS_GROUPS.SNOWFLAKE.permissions.OPEN)
@HttpCode(HttpStatus.OK)
async snowflakeSignIn(
@User() user: RequestUser,
@@ -61,24 +125,7 @@ export class AuthController {
});
}
@Post('sign-in')
@HttpCode(HttpStatus.OK)
async signIn(@Body() { username, password, totp }: AuthSignInRequest) {
this.logger.info('/auth - SignIn');
return this.authClient.signIn({ username, password, totp });
}
@Post('refresh-access-token')
@HttpCode(HttpStatus.OK)
async refreshAccessToken(
@Body() { refreshToken }: AuthRefreshAccessTokenRequest,
) {
this.logger.info('/auth - RefreshAccessToken');
return this.authClient.refreshAccessToken({ refreshToken });
}
@ApiInternalOnlyEndpoint()
@Post('change-password')
@HttpCode(HttpStatus.OK)
async changePassword(
@@ -97,16 +144,24 @@ export class AuthController {
});
}
@ApiInternalOnlyEndpoint()
@Post('reset-password')
@HttpCode(HttpStatus.OK)
async resetPassword(@Body() body: AuthResetPasswordRequest) {
async resetPassword(
@Body() body: AuthResetPasswordRequest,
@Language() language: LanguageEnum,
) {
this.logger.info('/auth - reset-password');
const metadata = PackTheMetadata({
language: language,
});
const { username } = body;
return this.authClient.resetPassword({ username });
return this.authClient.resetPassword({ username }, metadata);
}
@ApiInternalOnlyEndpoint()
@Post('verify-reset-password-code')
@HttpCode(HttpStatus.OK)
async verifyResetPasswordCode(
@@ -119,6 +174,7 @@ export class AuthController {
return this.authClient.verifyResetPasswordCode({ username, code });
}
@ApiInternalOnlyEndpoint()
@Post('confirm-reset-password')
@HttpCode(HttpStatus.OK)
async confirmResetPassword(@Body() body: AuthConfirmResetPasswordRequest) {
@@ -133,6 +189,7 @@ export class AuthController {
});
}
@ApiInternalOnlyEndpoint()
@Post('enable-totp')
@HttpCode(HttpStatus.OK)
async enableTotpMFA(
@@ -147,6 +204,7 @@ export class AuthController {
return this.authClient.enableTotpMFA({ accessToken, password });
}
@ApiInternalOnlyEndpoint()
@Post('disable-totp')
@HttpCode(HttpStatus.OK)
async disableTotpMFA(
@@ -161,6 +219,7 @@ export class AuthController {
return this.authClient.disableTotpMFA({ accessToken, password });
}
@ApiInternalOnlyEndpoint()
@Post('dismiss-totp')
@HttpCode(HttpStatus.OK)
async dismissTotpMFA(@Headers() headers) {
@@ -171,6 +230,7 @@ export class AuthController {
return this.authClient.dismissTotpMFA({ accessToken });
}
@ApiInternalOnlyEndpoint()
@Post('verify-totp')
@HttpCode(HttpStatus.OK)
async verifyTotp(@Body() body: AuthVerifyTotpMfaRequest, @Headers() headers) {
@@ -181,4 +241,107 @@ export class AuthController {
return this.authClient.verifyTotp({ accessToken, totp });
}
@ApiInternalOnlyEndpoint()
@Authenticated()
@ApiSecurity('access-token')
@Get('verify-access-token')
verifyAccessToken() {
return { access_token_status: 'valid' };
}
@ApiInternalOnlyEndpoint()
@Get('session/:session')
getSession(@Param('session') session: string) {
return this.authClient.getSession(session);
}
@ApiInternalOnlyEndpoint()
@Get('oauth/google')
@UseGuards(AuthGuard('google-login'))
googleOauth() {
this.logger.info('/oauth/google');
return true;
}
@ApiInternalOnlyEndpoint()
@Get('oauth/google/callback')
@UseGuards(AuthGuard('google-login'))
@Redirect()
async googleOauthCallback(@Req() req) {
const { url, email, token, language = 'pt-br' } = await this.callback(req);
if (url.searchParams.get('error')) {
this.logger.error('/oauth/google - ERROR');
return { url: url.href };
}
await this.authClient
.oauthSignIn({
username: email,
token,
})
.then(({ session }) => {
this.logger.info('/oauth/google - SUCESS');
url.searchParams.set('session', session);
})
.catch((err) => {
this.logger.error('/oauth/google - 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) {
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;
if (error || !authInfo) {
this.logger.error(error);
if (!authInfo) this.logger.error('No authInfo', { request: req });
error_title = OauthErrors.INVALID_CREDENTIALS[language].error;
error_description =
OauthErrors.INVALID_CREDENTIALS[language].error_description;
if (error) error_description += ` - [${error}]`;
} else {
const { accessToken } = authInfo as any;
const { _json: userInfo } = req.user as any;
email = userInfo.email;
token = accessToken;
}
if (error_title) {
url.searchParams.set('error', error_title);
url.searchParams.set('error_description', error_description);
}
return { token, email, url, language };
}
}
+11 -11
View File
@@ -5,20 +5,20 @@ import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { AuthController } from './auth.controller';
import { AuthClientService } from './auth.service';
import { DucClient } from '../../clients/duc/client.config';
const ducClient = new DucClient();
import { DucClient } from '../duc/client.config';
import { GoogleLoginStrategy } from './passport-strategies/google-strategy';
import { getOauthSecrets } from 'src/utils/OauthSecrets';
const client = new DucClient();
@Module({
imports: [
ClientsModule.register([
{
name: 'DUC_PACKAGE',
...ducClient.config(),
},
]),
],
imports: [ClientsModule.register([client.providerOptions])],
controllers: [AuthController],
providers: [AuthClientService, DadosferaLogger],
providers: [
AuthClientService,
DadosferaLogger,
GoogleLoginStrategy,
{ provide: 'OAUTH_SECRETS', useValue: getOauthSecrets() },
],
exports: [AuthClientService],
})
export class AuthModule {}
+29 -7
View File
@@ -18,6 +18,8 @@ import {
AuthVerifyResetPasswordCodeRequest,
AuthConfirmResetPasswordRequest,
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { DucClient } from '../duc/client.config';
import { Metadata } from '@grpc/grpc-js';
@Injectable()
export class AuthClientService implements OnModuleInit {
@@ -27,7 +29,7 @@ export class AuthClientService implements OnModuleInit {
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject('DUC_PACKAGE') private readonly grpcClient: ClientGrpc,
@Inject(DucClient.name) private readonly grpcClient: ClientGrpc,
) {
this.logger = dadosferaLogger.logger;
}
@@ -50,19 +52,25 @@ export class AuthClientService implements OnModuleInit {
return lastValueFrom(this.authService.AuthSnowflakeSignIn(input));
}
async signIn({ username, password, totp }: AuthSignInRequest) {
async signIn(
{ username, password, totp }: AuthSignInRequest,
metadata: Metadata,
) {
this.logger.info('SignIn');
return lastValueFrom(
this.authService.AuthSignIn({ username, password, totp }),
this.authService.AuthSignIn({ username, password, totp }, metadata),
);
}
async refreshAccessToken({ refreshToken }: AuthRefreshAccessTokenRequest) {
async refreshAccessToken(
{ refreshToken }: AuthRefreshAccessTokenRequest,
metadata: Metadata,
) {
this.logger.info('RefreshAccessToken');
return lastValueFrom(
this.authService.AuthRefreshAccessToken({ refreshToken }),
this.authService.AuthRefreshAccessToken({ refreshToken }, metadata),
);
}
@@ -82,10 +90,15 @@ export class AuthClientService implements OnModuleInit {
);
}
async resetPassword({ username }: AuthResetPasswordRequest) {
async resetPassword(
{ username }: AuthResetPasswordRequest,
metadata: Metadata,
) {
this.logger.info('resetPassword');
return lastValueFrom(this.authService.AuthResetPassword({ username }));
return lastValueFrom(
this.authService.AuthResetPassword({ username }, metadata),
);
}
async verifyResetPasswordCode({
@@ -144,4 +157,13 @@ export class AuthClientService implements OnModuleInit {
this.authService.AuthVerifyTotpMfa({ accessToken, totp }),
);
}
async getSession(session: string) {
return lastValueFrom(this.authService.AuthGetSession({ session }));
}
async oauthSignIn(data: { username: string; token: string }) {
const { username, token } = data;
return lastValueFrom(this.authService.AuthOauthSignIn({ token, username }));
}
}
+106 -1
View File
@@ -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,104 @@ 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;
@ApiProperty()
scheduleLimit: string;
}
export class AuthSignInReq implements AuthSignInRequest {
@ApiProperty()
username: string;
@ApiProperty()
password: string;
@ApiPropertyOptional({
description:
'TOTP code used to login when Multi-Factor Authentication is enabled',
})
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;
}
export class AuthRefreshAccessTokenReq {
@ApiProperty()
refreshToken: string;
@ApiProperty()
customerName: string;
}
export class AuthRefreshAccessTokenRes {
@ApiProperty()
permissions: string[];
@ApiProperty()
accessToken: string;
}
@@ -0,0 +1,46 @@
import {
AuthenticateOptionsGoogle,
Profile,
Strategy,
StrategyOptions,
} from 'passport-google-oauth20';
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 GoogleLoginStrategy extends PassportStrategy(
Strategy,
'google-login',
) {
redirect_uri: string;
constructor(
@Inject('OAUTH_SECRETS')
private readonly oauthSecrets: OauthSecrets,
) {
const options: StrategyOptions = {
clientID: oauthSecrets['google-login'].client_id,
clientSecret: oauthSecrets['google-login'].client_secret,
callbackURL: oauthSecrets['google-login'].redirect_uri,
scope: ['email', 'profile', 'openid'],
};
const verify = (
accessToken: string,
refreshToken: string,
profile: Profile,
done,
) => {
return done(null, profile, { accessToken, refreshToken });
};
super(options, verify);
}
authenticate(req: Request, options: AuthenticateOptionsGoogle) {
const language = req.headers['dadosfera-lang'] || req.query.language;
options.state = jwt.sign({ language }, process.env.JWT_PRIVATE_KEY);
super.authenticate(req, options);
}
}
+37
View File
@@ -0,0 +1,37 @@
import {
ClientsProviderAsyncOptions,
GrpcOptions,
Transport,
} from '@nestjs/microservices';
import { credentials } from '@grpc/grpc-js';
import { Catalog } from '@dadosfera/protospack-v2';
const isLocalConnection = !!process.env.PIFACTORY_URL?.includes('0.0.0.0');
export class CatalogClientConfiguration {
public name = 'CatalogClientConfiguration';
private config: GrpcOptions = {
transport: Transport.GRPC,
options: {
url: process.env.PIFACTORY_URL,
package: [
Catalog.ProtoPackages.ReadPackage,
Catalog.ProtoPackages.WritePackage,
],
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [
Catalog.ProtoPaths.ReadFilePath,
Catalog.ProtoPaths.WriteFilePath,
],
loader: {
keepCase: true,
enums: String,
defaults: false,
},
},
};
providerOptions: ClientsProviderAsyncOptions = {
name: this.name,
...this.config,
};
}
+505 -345
View File
@@ -1,41 +1,51 @@
import {
BadRequestException,
Body,
Controller,
Delete,
ForbiddenException,
Get,
Headers,
Inject,
NotFoundException,
Param,
Post,
Put,
Query,
UseFilters,
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { AuthenticateCondition } from '../../authentication/authentication.decorator';
import { PERMISSIONS } from '../../authentication/permissions.enum';
import { ApiHeaders, ApiTags } from '@nestjs/swagger';
import {
Authenticated,
RequireSomePermission,
} from '../../decorators/authentication.decorator';
import { PERMISSIONS_GROUPS } from '../../authentication/permissions.enum';
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 {
ICatalogAllRequest,
ICatalogAllResponse,
IColumnsMetadataResponse,
ICreateDataAsset,
IDeleteComment,
IDocsResponse,
IMakeAComment,
IOneDataAsset,
IPreviewResponse,
IUpdateDataRequest,
} from './dtos';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { Language } from 'src/decorators/language.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
@ApiTags('Catalog')
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
@Controller('catalog')
@AuthenticateCondition((req, user) => {
let action;
switch (req.method) {
case 'POST':
action = 'CREATE';
break;
case 'PUT':
action = 'UPDATE';
break;
default:
action = req.method;
}
return user.permissions.includes(
PERMISSIONS.CATALOG.permissions[action].seqid,
);
})
@UseFilters(new GrpcToHttpExceptionFilter())
@Authenticated()
export class CatalogController {
logger: DadosferaLogger;
constructor(
@@ -46,377 +56,527 @@ export class CatalogController {
this.logger = dadosferaLogger.logger;
}
@Get('all')
async catalogAll(@Body() body) {
this.logger.info(`/catalog - ON CATALOG ALL ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
@Get()
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.GET,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async searchCatalog(
@User() user: RequestUser,
@Query() query: ICatalogAllRequest,
): Promise<ICatalogAllResponse> {
const { user_id, customer_name, customer_id, username, permissions } = user;
this.logger.info(`/catalog - searchCatalog`, {
user_id,
customer_name,
});
const res = await this.catalogService.catalogAll(body);
const is_data_manager = permissions.includes(
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER.seqid,
);
const roles = await this.catalogService.getUserRolesIds(user_id);
const metadata = PackTheMetadata({
user_id,
customer_id,
customer_name,
username,
roles,
is_data_manager,
});
const res = await this.catalogService.searchDataAssets(
query,
metadata,
customer_id,
);
return res;
}
@Get('data_apps')
async dataAppsAll(@Body() body) {
this.logger.info(`/catalog - ON FIND ALL DATA APPS ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
@ApiInternalOnlyEndpoint()
@Get('data-asset')
async findByPipelineAndObject(@User() user: RequestUser, @Query() query) {
const { username, user_id, customer_id, customer_name, permissions } = user;
const { pipeline, object } = query;
this.logger.info(`/catalog - ON GET DATA ASSET BY PIPELINE AND OBJECT`, {
username,
customer_name,
});
const res = await this.catalogService.dataAppsAll(body);
return res;
}
@Delete('data_apps/:id')
async dataAppsOne(@Param() params, @Body() body) {
this.logger.info(`/catalog - ON FIND ONE DATA APP ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const { id } = params;
const res = await this.catalogService.dataAppsOne(id, body);
return res;
}
@Get('dashboard-metabase')
async getAllDashboardMetabase(@Body() body) {
this.logger.info(`/catalog - ON GET ALL DASHBOARDS METABASE ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.getAllDashboardMetabase(body);
return res;
}
@Get('dashboard-metabase/:id')
async getOneDashboardMetabase(@Body() body, @Param() params) {
const { id } = params;
this.logger.info(`/catalog - ON GET ONE DASHBOARD METABASE ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.getOneDashboardMetabase(id, body);
return res;
}
@Get('table-metadata')
async getAllTableMetadata(@Body() body, @Query() query) {
this.logger.info(`/catalog - ON GET ALL TABLES METADATA ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
if (!query) {
const res = await this.catalogService.getAllTableMetadata(body);
return res;
} else {
const res = await this.catalogService.getOneTableMetadata(body, query);
return res;
if (!pipeline || !object) {
throw new BadRequestException('Query params not provided');
}
}
@Delete('table-metadata/:id')
async deleteOneTableMetadata(@Param() params, @Body() body) {
const { id } = params;
this.logger.info(`/catalog - ON DELETE ONE TABLE METADATA ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
const is_data_manager = permissions.includes(
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER.seqid,
);
let has_permission = false;
const metadata = PackTheMetadata({
username,
user_id: undefined,
customer_id,
customer_name,
});
const res = await this.catalogService.deleteOneTableMetadata(id, body);
const user_roles = await this.catalogService.getUserRolesIds(user_id);
return res;
}
@Get('column-metadata')
async getOneColumnMetadata(@Body() body, @Query() params) {
this.logger.info(`/catalog - ON GET ONE COLUMN METADATA ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.getOneColumnMetadata(body, params);
return res;
}
@Delete('column-metadata/:id')
async deleteOneColumnMetadata(@Param() params, @Body() body) {
this.logger.info(`/catalog - ON DELETE ONE COLUMN METADATA ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const { id } = params;
const res = await this.catalogService.deleteOneColumnMetadata(id, body);
return res;
}
@Get('data-preview')
async getOneDataPreview(@Body() body, @Query() params) {
this.logger.info(`/catalog - ON GET ONE DATAPREVIEW ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.getOneDataPreview(body, params);
return res;
}
@Get('data-status')
async getDataStatus(@Body() body) {
this.logger.info(`/catalog - ON GET DATA STATUS ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.getDataStatus(body);
return res;
}
@Get('data-status')
async createDataStatus(@Body() body) {
this.logger.info(`/catalog - ON CREATE DATA STATUS ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.createDataStatus(body);
return res;
}
@Get('data-description')
async getDataDescription(@Body() body) {
this.logger.info(`/catalog - ON GET DATA DESCRIPTION ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.getDataDescription(body);
return res;
}
@Post('data-description')
async createDataDescription(@Body() body) {
this.logger.info(`/catalog - ON CREATE DATA DESCRIPTION ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.createDataDescription(body);
return res;
}
@Get('data-docs')
async getDataDocs(@Body() body) {
this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.getDataDocs(body);
return res;
}
@Post('data-docs')
async createDataDocs(@Body() body) {
this.logger.info(`/catalog - ON CREATE DATA DOCS ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.createDataDocs(body);
return res;
}
@Get('data-rating')
async getDataRating(@Body() body) {
this.logger.info(`/catalog - ON GET DATA RATING ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.getDataRating(body);
return res;
}
@Post('data-rating')
async createDataRating(@Body() body) {
this.logger.info(`/catalog - ON GET DATA RATING ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.createDataRating(body);
return res;
}
@Get('summary-rating/:id')
async getSummaryRating() {
this.logger.info(`/catalog - ON GET SUMMARY RATING ROUTE`);
//const { id } = params;
//
//const res = await this.catalogService.getSummaryRating(id, body);
return {
avg_rating: 0,
rating_count: 0,
};
}
@Get('data-comment')
async getDataComment(@Body() body) {
this.logger.info(`/catalog - ON GET DATA COMMENT ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.getDataComment(body);
return res;
}
@Post('data-comment')
async createDataComment(@Body() body) {
this.logger.info(`/catalog - ON GET DATA COMMENT ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.createDataComment(body);
return res;
}
@Get('data-review')
async getDataReview(@Body() body, @Query() params) {
this.logger.info(`/catalog - ON GET DATA REVIEW ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.getDataReview(body, params);
return res;
}
@Post('tags')
async createTags(@Body() body) {
this.logger.info(`/catalog - ON CREATE TAG ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.createTags(body);
return res;
const { data_asset } =
await this.catalogService.getOneDataAssetByPipelineAndObject({
pipeline: query.pipeline,
object: query.object,
customer_id,
metadata,
});
if (data_asset?.owner === username) has_permission = true;
for (const role of user_roles) {
if (data_asset.p_roles.includes(role)) has_permission = true;
}
if (data_asset.p_users.includes(user_id)) has_permission = true;
if (is_data_manager || has_permission) {
delete data_asset.p_roles;
delete data_asset.p_users;
return { data_asset };
}
throw new ForbiddenException(
'You do not have permission to access this data asset.',
);
}
@ApiInternalOnlyEndpoint()
@Get('tags')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.GET,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async findAllTags(@Body() body) {
this.logger.info(`/catalog - ON FIND ALL TAGS ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const res = await this.catalogService.findAllTags(body);
const { user_id, customer, customer_id } = body.info;
const metadata = PackTheMetadata({
user_id,
customer_id,
customer_name: customer,
});
const res = await this.catalogService.findAllTags(body, metadata);
return res;
}
@Delete('tags/:id')
async deleteTags(@Param() params, @Body() body) {
this.logger.info(`/catalog - ON DELETE TAG ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
});
const { id } = params;
@Get('data-asset/:id')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.GET,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async getDataAsset(
@User() user: RequestUser,
@Param('id') id,
@Query('shared') shared?: 'true',
) {
const { username, user_id, customer_id, customer_name, permissions } = user;
const res = await this.catalogService.deleteTags(id, body);
this.logger.info(`GET /data-asset/${id}`, {
username,
customer_name,
});
const is_data_manager = permissions.includes(
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER.seqid,
);
let has_permission = false;
const metadata = PackTheMetadata({
username,
user_id: undefined,
customer_id,
customer_name,
});
const user_roles = await this.catalogService.getUserRolesIds(user_id);
const { data_asset } = await this.catalogService.getOneDataAsset({
customer_id,
id,
metadata,
});
has_permission =
is_data_manager ||
data_asset?.owner === username ||
(user_roles as Array<any>).some((r) => data_asset.p_roles.includes(r)) ||
data_asset.p_users.includes(user_id);
if (
shared === 'true' &&
(data_asset.share_type === undefined || data_asset.share_type === 'none')
)
throw new NotFoundException();
if (!has_permission)
throw new ForbiddenException(
'You do not have permission to access this data asset.',
);
delete data_asset.p_roles;
delete data_asset.p_users;
return { data_asset };
}
@ApiInternalOnlyEndpoint()
@Get('data-asset/rls/:id')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.GET,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async getDataAssetRls(
@User() user: RequestUser,
@Headers() headers,
@Param('id') id,
) {
const { username, user_id, customer_id, customer_name, permissions } = user;
this.logger.info(`/catalog - ON GET ONE DASHBOARD METABASE ROUTE`, {
username,
customer_name,
});
const is_data_manager = permissions.includes(
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER.seqid,
);
let has_permission = false;
const metadata = PackTheMetadata({
username,
user_id,
customer_id,
customer_name,
});
const user_roles = await this.catalogService.getUserRolesIds(user_id);
const { data_asset } = await this.catalogService.getOneDataAsset({
customer_id,
id,
metadata,
});
if (data_asset?.owner === username) has_permission = true;
for (const role of user_roles) {
if (data_asset.p_roles.includes(role)) has_permission = true;
}
if (data_asset.p_users.includes(user_id)) has_permission = true;
if (is_data_manager || has_permission) {
delete data_asset.p_roles;
delete data_asset.p_users;
return { data_asset };
}
throw new ForbiddenException(
'You do not have permission to access this data asset.',
);
}
@Get('data-asset/:id/columns-metadata')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.GET,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async getDataAssetColumnsMetadata(
@User() user: RequestUser,
@Language() language: LanguageEnum,
@Param('id') id,
): Promise<IColumnsMetadataResponse> {
const { customer_name, customer_id, user_id, username } = user;
this.logger.info(`/catalog - columns-metadata`, {
user_id,
customer_name,
});
const metadata = PackTheMetadata({
customer_name,
customer_id,
user_id,
username,
language,
});
const columns_metadata =
await this.catalogService.getDatasetColumnsMetadata(id, metadata);
return { columns_metadata };
}
@Get('data-asset/:id/preview')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.GET,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async getDataAssetPreview(
@User() user: RequestUser,
@Language() language: LanguageEnum,
@Param('id') id,
): Promise<IPreviewResponse> {
const { customer_name, customer_id, user_id, username } = user;
this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, {
user_id,
customer_name,
});
const metadata = PackTheMetadata({
customer_name,
customer_id,
user_id,
username,
language,
});
const preview = await this.catalogService.getDatasetPreview(id, metadata);
return { preview };
}
@Get('data-asset/:id/docs')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.GET,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async getDataAssetDocs(
@User() user: RequestUser,
@Language() language: LanguageEnum,
@Param('id') id,
): Promise<IDocsResponse> {
const { customer_name, customer_id, user_id, username } = user;
this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, {
user_id,
customer_name,
});
const metadata = PackTheMetadata({
customer_name,
customer_id,
user_id,
username,
language,
});
const docs = await this.catalogService.getDataDocs(id, metadata);
return { docs };
}
@Put('data-asset/:id')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.UPDATE,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async updateDataAsset(
@User() user: RequestUser,
@Language() language: LanguageEnum,
@Param('id') data_asset_id,
@Body() body: IUpdateDataRequest,
): Promise<IOneDataAsset> {
const { customer_id, customer_name, user_id, username } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
language,
});
const result = await this.catalogService.updateOneDataAsset({
body,
data_asset_id,
customer_id,
metadata,
});
delete result.data_asset.p_roles;
delete result.data_asset.p_users;
return result;
}
@Post('data-asset/:id/docs')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.UPDATE,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async manageDataAssetDocs(
@User() user: RequestUser,
@Headers() headers,
@Param('id') table_id: string,
@Body('docs') docs: string,
) {
const { user_id, customer_name } = user;
this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, {
user_id,
customer_name,
});
const res = await this.catalogService.createDataDocs({
table_id,
docs,
info: {
customer: customer_name,
},
});
return res;
}
@Post('table-tags')
async createTableTags(@Body() body) {
this.logger.info(`/catalog - ON CREATE TABLE TAG ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
@ApiInternalOnlyEndpoint()
@Put('data-asset/:id/manage-permissions')
async manageDataAssetPermissions(
@Param('id') id: string,
@User() user: RequestUser,
@Body() body,
) {
const { customer_id, customer_name, user_id, username } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
});
const response = await this.catalogService.managePermissions(
{ ...body, id },
metadata,
);
const res = await this.catalogService.createTableTags(body);
return res;
return { data_asset: JSON.parse(response.data_asset) };
}
@Get('table-tags')
async getAllTableTags(@Body() body) {
this.logger.info(`/catalog - ON GET ALL TABLE TAGS ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
@ApiInternalOnlyEndpoint()
@Put('data-asset/:id/revoke-permissions')
async revokeDataAssetPermissions(
@Param('id') id: string,
@User() user: RequestUser,
@Body() body,
) {
const { customer_id, customer_name, user_id, username } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
});
const response = await this.catalogService.revokePermissions(
{ ...body, id },
metadata,
);
const res = await this.catalogService.findAllTableTags(body);
return res;
return { message: response.message };
}
@Delete('table-tags/:id')
async deleteTableTags(@Param() params, @Body() body) {
this.logger.info(`/catalog - ON CREATE TABLE TAGS ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
@Post()
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.CREATE,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async createDataAsset(
@User() user: RequestUser,
@Body() body: ICreateDataAsset,
) {
const { customer_id, customer_name, user_id, username } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
});
const { id } = params;
const { data_asset } = await this.catalogService.createDataAsset(
body,
metadata,
);
const res = await this.catalogService.deleteTableTags(id, body);
return res;
return { data_asset: JSON.parse(data_asset) };
}
@Get('table-rules')
async getTableRules(@Body() body) {
this.logger.info(`/catalog - ON GET TABLE RULES ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
@Post('data-asset/:id/comment')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.UPDATE,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async commentOnDataAsset(
@Param('id') id: string,
@User() user: RequestUser,
@Body() body: IMakeAComment,
) {
const { message } = body;
const { customer_id, customer_name, user_id, username } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
});
const response = await this.catalogService.commentOnDataAsset(
{ data_asset_id: id, username, message },
metadata,
);
const res = await this.catalogService.getTableRules(body);
return res;
return response;
}
@Delete('table-rules/:id')
async deleteTableRules(@Param() params, @Body() body) {
this.logger.info(`/catalog - ON DELETE TABLE RULES ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
@Delete('data-asset/:id')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.DELETE,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async deleteDataAsset(@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 { id } = params;
const response = await this.catalogService.deleteDataAsset(
{ id, type: undefined },
metadata,
);
const res = await this.catalogService.deleteTableRules(id, body);
return response;
}
return res;
@Delete('data-asset/:id/comment')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.UPDATE,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async deleteComment(
@Param('id') id: string,
@User() user: RequestUser,
@Body() body: IDeleteComment,
) {
const { customer_id, customer_name, user_id, username } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
});
const response = await this.catalogService.deleteComment(
{
data_asset_id: id,
comment: { ...body.comment, created_at: undefined, message: undefined },
},
metadata,
);
return response;
}
}
+26
View File
@@ -0,0 +1,26 @@
import { Module } from '@nestjs/common';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { CatalogController } from './catalog.controller';
import { CatalogService } from './catalog.service';
import { CatalogClientConfiguration } from './catalog-client';
import { ClientsModule } from '@nestjs/microservices';
import { PipelinesModule as OldPipelineModule } from 'src/modules/pipelines/pipelines.module';
import { UsersModule } from '../users/users.module';
import { RolesModule } from '../roles/roles.module';
const client = new CatalogClientConfiguration();
@Module({
imports: [
ClientsModule.register([client.providerOptions]),
OldPipelineModule,
UsersModule,
RolesModule,
],
controllers: [CatalogController],
providers: [CatalogService, DadosferaLogger],
exports: [CatalogService],
})
export class CatalogModule {}
+290 -214
View File
@@ -1,6 +1,51 @@
import DadosferaLogger from '@dadosfera/dadosfera-logs/dist';
import {
WriteService,
ReadService,
ProtoServices,
Messages,
} from '@dadosfera/protospack-v2/dist/lib/Catalog';
import {
HttpException,
HttpStatus,
Inject,
OnModuleInit,
} from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import axios from 'axios';
import { lastValueFrom } from 'rxjs';
import { CatalogClientConfiguration } from './catalog-client';
import { UsersService } from '../users/users.service';
import { RolesService } from '../roles/roles.service';
import { Metadata } from '@grpc/grpc-js';
import { IUpdateDataRequest } from './dtos';
class CatalogService implements OnModuleInit {
catalogReadService: ReadService.CatalogReadServices;
catalogWriteService: WriteService.CatalogWriteServices;
logger: any;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject(CatalogClientConfiguration.name)
private readonly grpcClient: ClientGrpc,
private readonly userService: UsersService,
private readonly roleService: RolesService,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.catalogReadService =
this.grpcClient.getService<ReadService.CatalogReadServices>(
ProtoServices.CatalogReadServices,
);
this.catalogWriteService =
this.grpcClient.getService<WriteService.CatalogWriteServices>(
ProtoServices.CatalogWriteServices,
);
}
class CatalogService {
_getNimbusUrl(body) {
const customer = body.info.customer.toLowerCase();
@@ -8,138 +53,228 @@ class CatalogService {
return `https://nimbus-${customer}.dadosfera.ai`;
}
return `https://nimbus-${customer}.${process.env.ENV}.dadosfera.ai`;
return `https://nimbus-${customer}.${process.env.ENV.replace(
'local',
'stg',
)}.dadosfera.ai`;
}
async catalogAll(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(`${nimbusUrl}/api/catalog/all/`);
async createDataAsset(data: Messages.CreateDataAssetRequest, metadata) {
this.logger.info('CatalogService - Manage Data assets permissions');
if (!data.embed) data.embed = undefined;
return data;
}
async dataAppsAll(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(`${nimbusUrl}/api/catalog/data_apps/`);
return data;
}
async dataAppsOne(id, body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(
`${nimbusUrl}/api/catalog/data_apps/${id}/`,
return lastValueFrom(
this.catalogWriteService.CreateDataAsset(data, metadata),
);
return data;
}
async getAllDashboardMetabase(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(
`${nimbusUrl}/api/catalog/dashboard-metabase/`,
);
return data;
}
async managePermissions(data: Messages.ManagePermissionRequest, metadata) {
this.logger.info('CatalogService - Manage Data assets permissions');
async getOneDashboardMetabase(id, body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(
`${nimbusUrl}/api/catalog/dashboard-metabase/${id}`,
);
// const [, path] = data.iframe_url.split('.dadosfera');
// const host = `httpss://metabase-${body.info.customer.toLowerCase()}.dadosfera.ai`;
// data.iframe_url = host + path;
return data;
}
async getAllTableMetadata(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(
`${nimbusUrl}/api/catalog/table-metadata/`,
);
return data;
}
async getOneTableMetadata(body, params) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(
`${nimbusUrl}/api/catalog/table-metadata/`,
{ params: params },
);
return data;
}
async deleteOneTableMetadata(id, body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.delete(
`${nimbusUrl}/api/catalog/table-metadata/${id}`,
);
return data;
}
async getOneColumnMetadata(body, params) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(
`${nimbusUrl}/api/catalog/column-metadata/`,
{ params: params },
);
return data;
}
async deleteOneColumnMetadata(id, body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.delete(
`${nimbusUrl}/api/catalog/column-metadata/${id}/`,
);
return data;
}
async getOneDataPreview(body, params) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(`${nimbusUrl}/api/catalog/data-preview/`, {
params: params,
return lastValueFrom(
this.catalogWriteService.ManagePermission(data, metadata),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 404,
);
});
return data;
}
async getDataStatus(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(`${nimbusUrl}/api/catalog/data-status/`);
return data;
async revokePermissions(data: Messages.RevokePermissionRequest, metadata) {
this.logger.info('CatalogService - Manage Data assets permissions');
return lastValueFrom(
this.catalogWriteService.RevokePermission(data, metadata),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 404,
);
});
}
async createDataStatus(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.post(
`${nimbusUrl}/api/catalog/data-status/`,
body,
async commentOnDataAsset(data: Messages.MakeACommentRequest, metadata) {
this.logger.info('CatalogService - Manage Data assets permissions');
return lastValueFrom(
this.catalogWriteService.MakeAComment(data, metadata),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 404,
);
});
}
async deleteComment(data: Messages.UpdateACommentRequest, metadata) {
this.logger.info('CatalogService - Manage Data assets permissions');
return lastValueFrom(
this.catalogWriteService.UpdateAComment(data, metadata),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 404,
);
});
}
async deleteDataAsset(data: Messages.DeleteDataAssetRequest, metadata) {
this.logger.info('CatalogService - Manage Data assets permissions');
return lastValueFrom(
this.catalogWriteService.DeleteDataAsset(data, metadata),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 404,
);
});
}
async getUserRolesIds(userId: string) {
const result = await this.userService.findOneById(userId).catch(() => null);
const roles_ids = result.user.roles.map((role) => role.id);
return roles_ids;
}
async searchDataAssets(
query: Record<string, any>,
metadata: Metadata,
customer_id: string,
) {
this.logger.info('CatalogService - searchDataAssets');
const { search, page, size, sort_by, order, ...filters } = query;
const { data_assets, total } = await lastValueFrom(
this.catalogReadService.GetAllDataAssets(
{
search,
page,
size,
sort_by,
order,
filters: JSON.stringify(filters),
},
metadata,
),
);
return data;
}
async getDataDescription(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(
`${nimbusUrl}/api/catalog/data-description/`,
const result = JSON.parse(data_assets);
const response = await this.getAssetsUsersAndRoles(
result.data_assets,
customer_id,
);
return data;
return { data_assets: response, total };
}
async createDataDescription(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.post(
`${nimbusUrl}/api/catalog/data-description/`,
body,
async getOneDataAsset(data: {
id: string;
customer_id: string;
metadata: Metadata;
}) {
const { customer_id, id, metadata } = data;
const { data_asset } = await lastValueFrom(
this.catalogReadService.GetOneDataAsset(
{ id, type: undefined },
metadata,
),
);
return data;
let asset = JSON.parse(data_asset);
asset = {
...asset,
p_roles: asset.roles,
p_users: asset.users,
};
asset = await this.getAssetsUsersAndRoles([asset], customer_id);
return { data_asset: asset[0] };
}
async getDataDocs(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(`${nimbusUrl}/api/catalog/data-docs/`);
return data;
async getOneDataAssetByPipelineAndObject(data: {
customer_id: string;
pipeline: string;
object: string;
metadata: Metadata;
}) {
const { customer_id, metadata, pipeline, object } = data;
const { data_asset } = await lastValueFrom(
this.catalogReadService.GetOneDataAssetByPipelineAndObject(
{ pipeline, object },
metadata,
),
);
let asset = JSON.parse(data_asset);
asset = {
...asset,
p_roles: asset.roles,
p_users: asset.users,
};
asset = await this.getAssetsUsersAndRoles([asset], customer_id);
return { data_asset: asset[0] };
}
async updateOneDataAsset(data: {
data_asset_id: string;
customer_id: string;
body: IUpdateDataRequest;
metadata: Metadata;
}) {
const { body, customer_id, data_asset_id, metadata } = data;
const { data_asset } = await lastValueFrom(
this.catalogWriteService.UpdateDataAsset(
{ id: data_asset_id, changes: JSON.stringify(body) },
metadata,
),
);
let asset = JSON.parse(data_asset);
asset = {
...asset,
p_roles: asset.roles,
p_users: asset.users,
};
asset = await this.getAssetsUsersAndRoles([asset], customer_id);
return { data_asset: asset[0] };
}
async getDataDocs(id: string, metadata: Metadata) {
const { documentation } = await lastValueFrom(
this.catalogReadService.GetDatasetDoc({ id, type: undefined }, metadata),
);
console.log(documentation);
const docs = JSON.parse(documentation);
return docs;
}
async getDatasetPreview(id: string, metadata: Metadata) {
const { preview } = await lastValueFrom(
this.catalogReadService.GetDatasetPreview(
{ id, type: undefined },
metadata,
),
);
const result = JSON.parse(preview);
return result;
}
async getDatasetColumnsMetadata(id: string, metadata: Metadata) {
const { columns_metadata } = await lastValueFrom(
this.catalogReadService.GetDatasetColumnsMetadata(
{ id, type: undefined },
metadata,
),
);
const result = JSON.parse(columns_metadata);
return result;
}
async createDataDocs(body) {
@@ -151,111 +286,52 @@ class CatalogService {
return data;
}
async getDataRating(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(`${nimbusUrl}/api/catalog/data-rating/`);
return data;
}
async findAllTags(data, metadata) {
this.logger.info('CatalogService - findAllCustomerTags');
async createDataRating(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.post(
`${nimbusUrl}/api/catalog/data-rating/`,
body,
const response = await lastValueFrom(
this.catalogReadService.GetCustomerTags(data, metadata),
)
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return response;
}
async getAssetsUsersAndRoles(data_assets: Array<any>, customer_id: string) {
const { users: customer_users } =
await this.userService.findAllUsersByCustomerId(customer_id);
const { roles: customer_roles } = await this.roleService.roleSearch(
{},
{ customer_id },
);
return data;
}
return data_assets.map((data_asset) => {
const owner = customer_users.find(
(u) => u.id === data_asset.owner,
)?.username;
async getSummaryRating(id, body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(
`${nimbusUrl}/api/catalog/summary-rating/${id}`,
);
return data;
}
async getDataComment(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(`${nimbusUrl}/api/catalog/data-comment/`);
return data;
}
async createDataComment(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.post(
`${nimbusUrl}/api/catalog/data-comment/`,
body,
);
return data;
}
async getDataReview(body, params) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(`${nimbusUrl}/api/catalog/data-review/`, {
params: params,
const roles = [];
const users = [];
for (const role_id of data_asset.roles) {
const role = customer_roles.find((r) => r.id === role_id);
if (role) roles.push({ id: role.id, name: role.name });
}
for (const user_id of data_asset.users) {
const user = customer_users.find((r) => r.id === user_id);
if (user) users.push({ id: user.id, username: user.username });
}
return {
...data_asset,
roles,
users,
owner,
} as typeof data_asset;
});
return data;
}
async createTags(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.post(`${nimbusUrl}/api/catalog/tags/`, body);
return data;
}
async findAllTags(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(`${nimbusUrl}/api/catalog/tags/`, body);
return data;
}
async findAllTableTags(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(
`${nimbusUrl}/api/catalog/table-tags/`,
body,
);
return data;
}
async deleteTags(id, body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.delete(`${nimbusUrl}/api/catalog/tags/${id}`);
return data;
}
async createTableTags(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.post(
`${nimbusUrl}/api/catalog/table-tags/`,
body,
);
return data;
}
async deleteTableTags(id, body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.delete(
`${nimbusUrl}/api/catalog/table-tags/${id}`,
);
return data;
}
async getTableRules(body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.get(
`${nimbusUrl}/api/catalog/table-rules/`,
body,
);
return data;
}
async deleteTableRules(id, body) {
const nimbusUrl = this._getNimbusUrl(body);
const { data } = await axios.delete(
`${nimbusUrl}/api/catalog/table-rules/${id}`,
);
return data;
}
}
+273
View File
@@ -0,0 +1,273 @@
import { ApiProperty, ApiPropertyOptional, PickType } from '@nestjs/swagger';
import { CreateDataAssetRequest } from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages';
export enum DataAssetShareType {
none = 'none',
public = 'public',
private = 'private',
}
export enum OrderEnum {
asc = 'asc',
desc = 'desc',
}
export class EmbedObject {
@ApiProperty()
url: string;
}
export class IRole {
@ApiProperty()
name: string;
@ApiProperty()
id: string;
}
export class IUser {
@ApiProperty()
username: string;
@ApiProperty()
id: string;
}
export class IComment {
@ApiProperty()
id: number;
@ApiProperty()
message: string;
@ApiProperty()
created_at: string;
@ApiProperty()
username: string;
}
export class IDataAsset {
@ApiProperty()
id: number;
@ApiPropertyOptional()
database_name?: string;
@ApiPropertyOptional()
table_name?: string;
@ApiPropertyOptional()
num_columns?: number;
@ApiPropertyOptional()
location?: string;
@ApiPropertyOptional()
created_time?: string;
@ApiPropertyOptional()
table_type?: string;
@ApiPropertyOptional()
size_in_bytes?: number;
@ApiPropertyOptional()
num_files?: number;
@ApiPropertyOptional()
data_encrypted?: string;
@ApiPropertyOptional()
num_rows?: number;
@ApiProperty()
display_name: string;
@ApiProperty()
pipeline_id: string;
@ApiProperty()
user_id: string;
@ApiProperty()
description: string;
@ApiProperty()
data_asset_type: string;
@ApiProperty()
tags: string[];
@ApiProperty()
owner: string;
@ApiProperty({ type: [IRole] })
roles: IRole[];
@ApiProperty({ type: [IUser] })
users: IUser[];
@ApiPropertyOptional()
dashboard_id?: number;
@ApiPropertyOptional()
name?: string;
@ApiPropertyOptional()
parameters?: string[];
@ApiPropertyOptional()
embedding_params?: object;
@ApiProperty()
created_at: string;
@ApiPropertyOptional()
updated_at?: string;
@ApiProperty({ type: [IComment] })
comments: IComment[];
@ApiPropertyOptional()
embed?: EmbedObject;
@ApiPropertyOptional({ enum: DataAssetShareType })
share_type?: DataAssetShareType;
}
export class IOneDataAsset {
@ApiProperty()
data_asset: IDataAsset;
}
export class IMakeAComment {
@ApiProperty()
message: string;
}
export class CommentToDelete extends PickType(IComment, ['id', 'username']) {}
export class IDeleteComment {
@ApiProperty()
comment: CommentToDelete;
}
export class ICatalogAllRequest {
@ApiPropertyOptional({
description: 'Texto usado para filtrar os ativos no catálogo.',
})
search;
@ApiPropertyOptional({
description: 'Número de registros a ser retornado',
maximum: 10000,
minimum: 1,
})
size: number;
@ApiPropertyOptional({
description: 'Usado para paginação, em conjunto com `size`',
minimum: 1,
})
page: number;
@ApiPropertyOptional({
description:
'Campo do data asset para usar na ordenação. Padrão: `created_at` ',
})
sort_by: string;
@ApiPropertyOptional({
enum: OrderEnum,
default: OrderEnum.asc,
description: 'Tipo de ordenação - `asc`: crescente; `desc`: decrescente ',
})
order?: OrderEnum;
}
export class ICatalogAllResponse {
@ApiProperty({ type: [IDataAsset] })
data_assets: IDataAsset[];
@ApiProperty()
total: number;
}
export class IData {
@ApiProperty()
var: number;
@ApiProperty()
date: string;
@ApiProperty()
last: number;
@ApiProperty()
maximum: number;
@ApiProperty()
minimum: number;
@ApiProperty()
day_opening: number;
}
export class IUpdateDataRequest {
@ApiProperty()
name: string;
@ApiProperty()
description: string;
@ApiProperty()
tags: string[];
@ApiPropertyOptional()
embed: EmbedObject;
@ApiPropertyOptional({ enum: DataAssetShareType })
share_type?: DataAssetShareType;
}
export class ICreateDataAsset implements CreateDataAssetRequest {
@ApiProperty()
display_name: string;
@ApiProperty()
description: string;
@ApiProperty()
data_asset_type: string;
@ApiProperty()
external_url: string;
@ApiProperty()
location: string;
@ApiPropertyOptional()
embed: EmbedObject;
}
export class IPreview {
@ApiProperty()
id: string;
@ApiProperty()
database_name: string;
@ApiProperty()
table_name: string;
@ApiProperty({ type: [IData] })
data: IData[];
}
export class IDocs {
@ApiProperty()
id: string;
@ApiProperty()
docs: string;
@ApiProperty()
updated_at: string;
@ApiProperty()
table_id: string;
}
export class IColumnMetadata {
@ApiProperty()
id: string;
@ApiProperty()
table_name: string;
@ApiProperty()
column_name: string;
@ApiProperty()
data_type: string;
@ApiProperty()
missing_count: string;
@ApiProperty()
min: string;
@ApiProperty()
max: string;
@ApiProperty()
count: string;
@ApiProperty()
histogram: string;
@ApiProperty()
mean: string;
@ApiProperty()
median: string;
@ApiProperty()
missing_rate: string;
@ApiProperty()
stddev: string;
@ApiProperty()
unique_count: string;
@ApiProperty()
unique_rate: string;
@ApiProperty()
analyzer_rules: string[];
@ApiProperty()
version: string;
}
export class IPreviewResponse {
@ApiProperty()
preview: IPreview;
}
export class IDocsResponse {
@ApiProperty()
docs: IDocs;
}
export class IColumnsMetadataResponse {
@ApiProperty({ type: [IColumnMetadata] })
columns_metadata: IColumnMetadata[];
}
@@ -0,0 +1,31 @@
import { credentials } from '@grpc/grpc-js';
import {
ClientProviderOptions,
GrpcOptions,
Transport,
} from '@nestjs/microservices';
import { ConnectionTest } from '@dadosfera/protospack-v2';
const isLocalConnection = !!process.env.INFACTORY_URL?.includes('0.0.0.0');
export class ConnectionTestClientConfiguration {
private config: GrpcOptions = {
transport: Transport.GRPC,
options: {
url: process.env.INFACTORY_URL,
package: [ConnectionTest.ProtoPackages.ReadPackage],
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [ConnectionTest.ProtoPaths.ReadFilePath],
loader: {
keepCase: true,
enums: String,
objects: true,
arrays: true,
},
},
};
providerOptions: ClientProviderOptions = {
name: 'ConnectionTestGrpcClient',
...this.config,
};
}
@@ -0,0 +1,124 @@
import {
Body,
Controller,
HttpCode,
HttpStatus,
Inject,
Post,
UseFilters,
ValidationPipe,
} from '@nestjs/common';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { User, RequestUser } from 'src/decorators/user.decorator';
import { ConnectionTestService } from './connection-test.service';
import {
ConnectionTestPingRes,
ConnectionTestPingReq,
ConnectionTestCredentialsReq,
ConnectionTestCredentialsRes,
ConnectionTestListSchemasReq,
ConnectionTestListSchemasRes,
ConnectionTestListTablesReq,
ConnectionTestListTablesRes,
GetTableMetadataRes,
GetTableMetadataReq,
} from './dto/connection-test';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { Authenticated } from 'src/decorators/authentication.decorator';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
@ApiInternalOnlyController()
@ApiTags('Connection Test')
@Controller('connection-test')
@UseFilters(new GrpcToHttpExceptionFilter())
@Authenticated()
export class ConnectionTestController {
logger: any;
constructor(
private connectionTestService: ConnectionTestService,
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
) {
this.logger = dadosferaLogger.logger;
}
@Post('ping')
@ApiOkResponse({ type: ConnectionTestPingRes })
@HttpCode(HttpStatus.OK)
async connectionTestPing(
@User() user: RequestUser,
@Body(new ValidationPipe()) body: ConnectionTestPingReq,
) {
this.logger.info('/connection-test/ping', {
user: user.user_id,
customer: user.customer_name,
});
return this.connectionTestService.connectionTestPing(body, user);
}
@Post('credentials')
@ApiOkResponse({ type: ConnectionTestCredentialsRes })
@HttpCode(HttpStatus.OK)
async connectionTestCredentials(
@User() user: RequestUser,
@Body() body: ConnectionTestCredentialsReq,
) {
this.logger.info('/connection-test/credentials', {
user: user.user_id,
customer: user.customer_name,
});
return this.connectionTestService.connectionTestCredentials(body, user);
}
@Post('schemas')
@ApiOkResponse({ type: ConnectionTestListSchemasRes })
@HttpCode(HttpStatus.OK)
async connectionTestListSchemas(
@User() user: RequestUser,
@Body(new ValidationPipe()) body: ConnectionTestListSchemasReq,
) {
this.logger.info('/connection-test/schemas', {
user: user.user_id,
customer: user.customer_name,
});
return this.connectionTestService.connectionTestListSchemas(
body,
user.customer_name,
);
}
@Post('tables')
@ApiOkResponse({ type: ConnectionTestListTablesRes })
@HttpCode(HttpStatus.OK)
async connectionTestListTables(
@User() user: RequestUser,
@Body(new ValidationPipe()) body: ConnectionTestListTablesReq,
) {
this.logger.info('/connection-test/tables', {
user: user.user_id,
customer: user.customer_name,
});
return this.connectionTestService.connectionTestListTables(
body,
user.customer_name,
);
}
@Post('table-metadata')
@ApiOkResponse({ type: GetTableMetadataRes })
@HttpCode(HttpStatus.OK)
async getTableMetadata(
@User() user: RequestUser,
@Body(new ValidationPipe()) body: GetTableMetadataReq,
) {
this.logger.info('/connection-test/table-metadata', {
user: user.user_id,
customer: user.customer_name,
});
return this.connectionTestService.getTableMetadata(
body,
user.customer_name,
);
}
}
@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { ConnectionTestController } from './connection-test.controller';
import { ConnectionTestService } from './connection-test.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { ClientsModule } from '@nestjs/microservices';
import { ConnectionTestClientConfiguration } from './connection-test-client.config';
import { ConnectionModule } from '../connection/connection.module';
const client = new ConnectionTestClientConfiguration();
@Module({
controllers: [ConnectionTestController],
providers: [ConnectionTestService, DadosferaLogger],
imports: [ClientsModule.register([client.providerOptions]), ConnectionModule],
})
export class ConnectionTestModule {}
@@ -0,0 +1,191 @@
import { Inject, Injectable } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { ConnectionTest } from '@dadosfera/protospack-v2';
import { lastValueFrom } from 'rxjs';
import {
ConnectionTestCredentialsReq,
ConnectionTestCredentialsRes,
ConnectionTestListSchemasReq,
ConnectionTestListSchemasRes,
ConnectionTestListTablesReq,
ConnectionTestListTablesRes,
ConnectionTestPingReq,
ConnectionTestPingRes,
GetTableMetadataReq,
GetTableMetadataRes,
} from './dto/connection-test';
import { ConnectionClientService } from '../connection/client.service';
import {
CreateConnectionDto,
DatabaseConnectionPropertiesDto,
} from '../connection/dtos/connection';
import { RequestUser } from 'src/decorators/user.decorator';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
@Injectable()
export class ConnectionTestService {
private connectionTestReadClient: ConnectionTest.ReadService.ConnectionTestReadServices;
constructor(
@Inject('ConnectionTestGrpcClient') private readonly grpcClient: ClientGrpc,
private connectionsService: ConnectionClientService,
) {
this.connectionTestReadClient =
grpcClient.getService<ConnectionTest.ReadService.ConnectionTestReadServices>(
ConnectionTest.ProtoServices.ConnectionTestReadServices,
);
}
async connectionTestPing(
body: ConnectionTestPingReq,
user: RequestUser,
): Promise<ConnectionTestPingRes> {
const {
host: endpoint,
plugin,
port,
network_config_id,
connection_id,
} = body;
const { user_id, customer_id, customer_name } = user;
const metadata = PackTheMetadata({
customer_name,
customer_id,
user_id,
});
if (connection_id) {
const con = await this.connectionsService.getConnectionDetails({
body,
metadata,
});
const conPayload = JSON.parse(con.connection.properties);
const { plugin, network_config_id } = con.connection;
const { host, port, endpoint } = conPayload;
return lastValueFrom(
this.connectionTestReadClient.ConnectionTestPing({
customer_name,
endpoint: endpoint || host,
network_config_id,
plugin,
port: port ? port.toString() : undefined,
connection_id,
}),
);
} else {
return lastValueFrom(
this.connectionTestReadClient.ConnectionTestPing({
customer_name,
endpoint,
network_config_id,
plugin,
port: port ? port.toString() : undefined,
connection_id,
}),
);
}
}
async connectionTestCredentials(
body: ConnectionTestCredentialsReq,
user: RequestUser,
): Promise<ConnectionTestCredentialsRes> {
const { plugin, ...connectionBody } = body;
let { connection_id } = body;
const { customer_id, customer_name, user_id, username } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
});
if (body.network_config_id == '') {
body.network_config_id = undefined;
}
/********* CREATE CONNECTION *********/
if (!connection_id) {
const properties: DatabaseConnectionPropertiesDto = {
...connectionBody.properties,
plugin,
credentials_type: 'basic_auth',
};
const createConnectionBody: CreateConnectionDto = {
...body,
properties,
};
const { connection } = await this.connectionsService.createConnection(
createConnectionBody,
metadata,
);
connection_id = connection.id;
}
/************************************/
const { operation_result } = await lastValueFrom(
this.connectionTestReadClient.ConnectionTestCredentials({
connection_id,
customer_name,
plugin,
}),
).catch(async (e) => {
if (!body.connection_id) {
await this.connectionsService.deleteConnection({
body: { id: connection_id },
metadata,
});
}
throw e;
});
if (!operation_result && !body.connection_id) {
await this.connectionsService.deleteConnection({
body: { id: connection_id },
metadata,
});
connection_id = null;
}
return { operation_result, connection_id };
}
async connectionTestListSchemas(
body: ConnectionTestListSchemasReq,
customer_name: string,
): Promise<ConnectionTestListSchemasRes> {
const { connection_id, plugin } = body;
return lastValueFrom(
this.connectionTestReadClient.ListSchemas({
connection_id,
customer_name,
plugin,
}),
);
}
async connectionTestListTables(
body: ConnectionTestListTablesReq,
customer_name: string,
): Promise<ConnectionTestListTablesRes> {
const { connection_id, plugin, schema } = body;
return lastValueFrom(
this.connectionTestReadClient.ListTables({
connection_id,
customer_name,
plugin,
schema,
}),
);
}
async getTableMetadata(
body: GetTableMetadataReq,
customer_name: string,
): Promise<GetTableMetadataRes> {
const { schema, plugin, table_list, connection_id } = body;
return lastValueFrom(
this.connectionTestReadClient.GetTableMetadata({
connection_id,
customer_name,
plugin,
schema,
table_list,
}),
);
}
}
@@ -0,0 +1,133 @@
import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger';
import { IsString, IsOptional } from 'class-validator';
import { DatabaseConnectionPropertiesDto } from 'src/modules/connection/dtos/connection';
import { CreateConnectionDto } from 'src/modules/connection/dtos/connection';
export class ColumnDto {
@ApiProperty()
name: string;
@ApiProperty()
type: string;
}
export class TableMetadataDto {
@ApiProperty()
table_name: string;
@ApiProperty({ type: [ColumnDto] })
columns: ColumnDto[];
@ApiProperty({ type: [ColumnDto] })
references: ColumnDto[];
}
export class ConnectionTestPingReq {
@ApiPropertyOptional()
@IsString()
@IsOptional()
plugin: string;
@ApiPropertyOptional()
@IsString()
@IsOptional()
host: string;
@ApiPropertyOptional()
@IsOptional()
port: number;
@ApiPropertyOptional()
@IsOptional()
@IsString()
network_config_id?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
connection_id: string;
}
export class ConnectionTestPingRes {
@ApiProperty()
operation_result: boolean;
}
export class ConnectionTestCredentialsProperties extends OmitType(
DatabaseConnectionPropertiesDto,
['plugin', 'credentials_type'],
) {}
export class ConnectionTestCredentialsReq extends OmitType(
CreateConnectionDto,
['properties'],
) {
@ApiProperty()
properties: ConnectionTestCredentialsProperties;
@ApiPropertyOptional()
connection_id?: string;
}
export class ConnectionTestCredentialsRes {
@ApiProperty()
operation_result: boolean;
@ApiProperty()
connection_id: string;
}
export class ConnectionTestListSchemasReq {
@ApiProperty()
@IsString()
plugin: string;
@ApiProperty()
@IsString()
connection_id: string;
}
export class ConnectionTestListSchemasRes {
@ApiProperty()
operation_result: boolean;
@ApiProperty()
schema_list: string[];
}
export class ConnectionTestListTablesReq {
@ApiProperty()
@IsString()
plugin: string;
@ApiProperty()
@IsString()
connection_id: string;
@ApiProperty()
@IsString()
schema: string;
}
export class ConnectionTestListTablesRes {
@ApiProperty()
operation_result: boolean;
@ApiProperty()
table_list: string[];
}
export class GetTableMetadataReq {
@ApiProperty()
@IsString()
connection_id: string;
@ApiProperty()
@IsString()
plugin: string;
@ApiProperty()
@IsString()
schema: string;
@ApiProperty()
@IsString({ each: true })
table_list: string[];
}
export class GetTableMetadataRes {
@ApiProperty()
operation_result: boolean;
@ApiProperty({ type: [TableMetadataDto] })
tables_metadata: TableMetadataDto[];
}
+39
View File
@@ -0,0 +1,39 @@
import { credentials } from '@grpc/grpc-js';
import {
ClientsProviderAsyncOptions,
GrpcOptions,
Transport,
} from '@nestjs/microservices';
import { ConnectionManager } from '@dadosfera/protospack-v2';
const isLocalConnection = !!process.env.INFACTORY_URL?.includes('0.0.0.0');
export class ConnectionClientConfiguration {
public name = 'ConnectionClientConfiguration';
private config: GrpcOptions = {
transport: Transport.GRPC,
options: {
url: process.env.INFACTORY_URL,
package: [
ConnectionManager.ProtoPackages.WritePackage,
ConnectionManager.ProtoPackages.ReadPackage,
],
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [
ConnectionManager.ProtoPaths.WriteFilePath,
ConnectionManager.ProtoPaths.ReadFilePath,
],
loader: {
keepCase: true,
enums: String,
objects: true,
arrays: true,
},
},
};
providerOptions: ClientsProviderAsyncOptions = {
name: this.name,
...this.config,
};
}
+340
View File
@@ -0,0 +1,340 @@
import { OnModuleInit, Inject } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { ConnectionManager } from '@dadosfera/protospack-v2';
import { lastValueFrom } from 'rxjs';
import {
ConnectionApiConnection,
ConnectionRes,
ConnectionsRes,
ConnectionToCatalogDto,
CreateConnectionDto,
UpdateConnectionDto,
} from './dtos/connection';
import { ConnectionClientConfiguration } from './client.config';
import {
GetConnectionDetailsResponse,
INTERNAL_GetAllConnectionsResponse,
} from '@dadosfera/protospack-v2/dist/lib/ConnectionManager/interfaces/messages';
import { UsersService } from '../users/users.service';
import { ConnectorClientService } from '../connector/client.service';
import {
ConnectionsApiNetworkConfig,
NetworkConfigDto,
} from '../network-config/dto/network-config';
import DadosferaLogger from '@dadosfera/dadosfera-logs/dist';
export class ConnectionClientService implements OnModuleInit {
private connectionServiceRead: ConnectionManager.ReadService.ConnectionManagerReadServices;
private connectionServiceWrite: ConnectionManager.WriteService.ConnectionManagerWriteServices;
logger: DadosferaLogger;
constructor(
@Inject(ConnectionClientConfiguration.name)
private readonly grpcClient: ClientGrpc,
private usersService: UsersService,
private connectorService: ConnectorClientService,
private dadosferaLogger: DadosferaLogger,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.connectionServiceWrite =
this.grpcClient.getService<ConnectionManager.WriteService.ConnectionManagerWriteServices>(
ConnectionManager.ProtoServices.ConnectionManagerWriteServices,
);
this.connectionServiceRead =
this.grpcClient.getService<ConnectionManager.ReadService.ConnectionManagerReadServices>(
ConnectionManager.ProtoServices.ConnectionManagerReadServices,
);
}
async createConnection(
body: CreateConnectionDto,
metadata,
): Promise<ConnectionRes> {
const {
connector_id,
description,
image_url,
name,
network_config_id,
properties,
type,
connector_name,
plugin,
} = body;
return lastValueFrom(
this.connectionServiceWrite.CreateConnection(
{
connector_id,
connector_name,
description,
image_url,
name,
network_config_id,
properties: JSON.stringify(properties),
type,
plugin,
},
metadata,
),
);
}
async updateConnection(
id: string,
body: UpdateConnectionDto,
metadata,
): Promise<ConnectionRes> {
const { description, name, network_config_id, properties } = body;
return lastValueFrom(
this.connectionServiceWrite.UpdateConnection(
{
id,
description,
name,
network_config_id,
properties: JSON.stringify(properties),
},
metadata,
),
);
}
async deleteConnection({ body, metadata }): Promise<ConnectionRes> {
const { id } = body;
return lastValueFrom(
this.connectionServiceWrite.DeleteConnection(
{
id,
},
metadata,
),
);
}
async getAllConnections({ body, metadata }): Promise<ConnectionsRes> {
const { search, filters, size, page } = body;
return lastValueFrom(
this.connectionServiceRead.GetAllConnection(
{ search, filters: JSON.stringify(filters), size, page },
metadata,
),
);
}
async getConnectionDetails({
body,
metadata,
}): Promise<GetConnectionDetailsResponse> {
const id = body.connection_id || body.id;
return lastValueFrom(
this.connectionServiceRead.GetConnectionDetails({ id }, metadata),
);
}
async INTERNAL_getAllConnections({
customer_name,
}: {
customer_name: string;
}): Promise<INTERNAL_GetAllConnectionsResponse> {
return lastValueFrom(
this.connectionServiceRead.INTERNAL_GetAllConnections({ customer_name }),
);
}
async catalogExistingConnection(data: {
connection?: ConnectionToCatalogDto;
networkConfig?: NetworkConfigDto;
}) {
return lastValueFrom(
this.connectionServiceRead.INTERNAL_CatalogExistingConnection({
data_str: JSON.stringify(data),
}),
);
}
async synchronizeConnections(apply: boolean) {
const connectors = await this.connectorService
.getAllConnectors({
search: undefined,
filters: undefined,
language: 'pt-br',
page: 1,
size: 500,
})
.then(({ connectors: connectorsStr }) =>
JSON.parse(connectorsStr).map((c) => c),
);
const { customers } = await this.usersService.findAllCustomers();
const customer_connections: {
customer_name: string;
customer_id: string;
productConnections: Record<string, any>[];
platformConnections: ConnectionApiConnection[];
productNetworkConfigs: Record<string, any>[];
platformNetworkConfigs: ConnectionsApiNetworkConfig[];
}[] = [];
for (const customer of customers) {
const { name: customer_name, id: customer_id } = customer;
const customer_connection = await this.INTERNAL_getAllConnections({
customer_name,
}).then((res) => ({
customer_name,
customer_id,
platformConnections: res.platformConnections
? JSON.parse(res.platformConnections)
: [],
productConnections: res.productConnections
? JSON.parse(res.productConnections)
: [],
productNetworkConfigs: res.productNetworkConfigs
? JSON.parse(res.productNetworkConfigs)
: [],
platformNetworkConfigs: res.platformNetworkConfigs
? JSON.parse(res.platformNetworkConfigs)
: [],
}));
customer_connections.push(customer_connection);
}
this.logger.info(
'DEBUG_ConnectionsMigrations - ' + JSON.stringify(customer_connections),
);
const migrations: {
customer_name: string;
status: any[];
productConnectionsMissing: ConnectionApiConnection[];
productNetworkConfigsMissing: ConnectionsApiNetworkConfig[];
}[] = [];
for (const customer_connection of customer_connections) {
const productConnectionsMissing: ConnectionApiConnection[] = [];
const productNetworkConfigsMissing: ConnectionsApiNetworkConfig[] = [];
const status: any[] = [];
let updatePromises: Promise<any>[] = [];
this.logger.info(
'DEBUG_ConnectionsMigrations - customer_connections ' +
JSON.stringify(customer_connection),
);
const {
customer_name,
productConnections,
platformConnections,
productNetworkConfigs,
platformNetworkConfigs,
customer_id,
} = customer_connection;
platformConnections?.forEach((plc) => {
const productConnection = productConnections.find(
(prc) => prc.id === plc.config_id,
);
if (!productConnection) productConnectionsMissing.push(plc);
});
platformNetworkConfigs?.forEach((platNetConfig) => {
const productNetworkConfig = productNetworkConfigs.find(
(prodNetConfig) => prodNetConfig.id === platNetConfig.config_id,
);
if (!productNetworkConfig)
productNetworkConfigsMissing.push(platNetConfig);
});
if (apply) {
for (const network_config of productNetworkConfigsMissing) {
const networkConfigToCatalog = {
id: network_config.config_id,
created_at: network_config.created_at,
updated_at: network_config.updated_at,
customer_name: customer_name,
customer_id: customer_id,
user_id: '',
network_type_id: '',
network_type_name: network_config.type,
type: network_config.type,
plugin: network_config.plugin,
name: network_config.name,
description: network_config.description,
properties: undefined,
keywords: undefined,
tags: undefined,
};
updatePromises.push(
this.catalogExistingConnection({
networkConfig: networkConfigToCatalog,
}),
);
}
this.logger.info(
'DEBUG_ConnectionsMigrations - Creating missing network configs for customer' +
customer_name +
' - ' +
JSON.stringify(customer_connection),
);
const updateStatuses = await Promise.all(updatePromises);
status.push(...updateStatuses);
updatePromises = [];
for (const connection of productConnectionsMissing) {
const connector = connectors.find(
(c) => c._source.plugin === connection.plugin,
);
const connectionToCatalog = {
id: connection.config_id,
created_at: connection.created_at,
updated_at: new Date().toISOString(),
customer_id: customer_id,
user_id: '',
plugin: connection.plugin,
name: connection.name,
description: connection.description,
image_url: connector._source.image,
network_config_id: connection.network_config_id,
type: connection.type,
connector_id: connector._id,
connector_name: connector._source.name,
in_use: 1,
username: '',
customer_name,
};
updatePromises.push(
this.catalogExistingConnection({ connection: connectionToCatalog }),
);
}
this.logger.info(
'DEBUG_ConnectionsMigrations - Creating missing connections for customer' +
' - ' +
customer_name +
JSON.stringify(customer_connection),
);
const updateStatuses_2 = await Promise.all(updatePromises);
status.push(...updateStatuses_2);
}
this.logger.info(
'DEBUG_ConnectionsMigrations - migrations for customer ' +
customer_name +
' - ' +
JSON.stringify(customer_connection),
);
migrations.push({
customer_name,
status,
productConnectionsMissing,
productNetworkConfigsMissing,
});
}
return { migrations };
}
}
@@ -0,0 +1,212 @@
import {
Body,
Controller,
Post,
Inject,
Put,
Param,
Delete,
Get,
Query,
UseFilters,
} from '@nestjs/common';
import { ApiExcludeEndpoint, ApiTags } from '@nestjs/swagger';
import { ConnectionClientService } from './client.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
Authenticated,
RequireAllPermissions,
} from 'src/decorators/authentication.decorator';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import { RequestUser, User } from 'src/decorators/user.decorator';
import { ValidationPipe } from '../../pipes/object-validation.pipe';
import {
ConnectionDetailsRes,
ConnectionRes,
ConnectionsRes,
UpdateConnectionDto,
} from './dtos/connection';
import { CreateConnectionDto } from './dtos/connection';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { Language } from 'src/decorators/language.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
const connectionPermissions = PERMISSIONS_GROUPS.CONNECTION.permissions;
@UseFilters(new GrpcToHttpExceptionFilter())
@ApiTags('connections')
@Authenticated()
@Controller('connections')
export class ConnectionController {
logger: any;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private clientService: ConnectionClientService,
) {
this.logger = dadosferaLogger.logger;
}
@ApiInternalOnlyEndpoint()
@Get('connections-migration')
@ApiExcludeEndpoint()
@RequireAllPermissions(
PERMISSIONS_GROUPS.DADOSFERA.permissions.CONNECTIONS_MIGRATION,
)
async synchronizeConnections(@Query('apply') apply: string) {
return this.clientService.synchronizeConnections(apply === 'true');
}
@Post()
@RequireAllPermissions(connectionPermissions.CREATE)
async createConnection(
@User() user: RequestUser,
@Body(new ValidationPipe()) body: CreateConnectionDto,
): Promise<ConnectionRes> {
this.logger.info('/connections - Create Connection');
const { user_id, customer_id, customer_name, username } = user;
const metadata = PackTheMetadata({
user_id,
customer_id,
customer_name,
username,
});
const response: any = await this.clientService.createConnection(
body,
metadata,
);
return response;
}
@Put('/:id')
@RequireAllPermissions(connectionPermissions.CREATE)
async updateConnection(
@User() user: RequestUser,
@Language() language: LanguageEnum,
@Param('id') id: string,
@Body(new ValidationPipe()) body: UpdateConnectionDto,
): Promise<ConnectionRes> {
this.logger.info('/connections - Update Connection');
const { user_id, customer_id, customer_name, username } = user;
const metadata = PackTheMetadata({
user_id,
customer_id,
customer_name,
language,
username,
});
const response: any = await this.clientService.updateConnection(
id,
body,
metadata,
);
return response;
}
@Delete('/:id')
@RequireAllPermissions(connectionPermissions.DELETE)
async deleteConnection(
@User() user: RequestUser,
@Param('id') id: string,
): Promise<ConnectionRes> {
this.logger.info('/connections - Delete Connection');
const { user_id, customer_id, customer_name, username } = user;
const metadata = PackTheMetadata({
user_id,
customer_id,
customer_name,
username,
});
const response: any = await this.clientService.deleteConnection({
body: { id },
metadata,
});
return response;
}
@Get()
async getAllConnections(
@User() user: RequestUser,
@Language() language: LanguageEnum,
@Query() queries,
): Promise<ConnectionsRes> {
this.logger.info('/connections - Get All Connection');
const { user_id, customer_id, customer_name, username } = user;
const { search, size, page, ...filters } = queries;
const metadata = PackTheMetadata({
user_id,
customer_id,
customer_name,
language,
username,
});
const response = await this.clientService.getAllConnections({
body: { search, size, page, filters },
metadata,
});
return response;
}
@Get('/:id')
async getConnectionDetails(
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Param('id') id,
@Query() queries,
): Promise<ConnectionDetailsRes> {
this.logger.info('/connections - Get Connection Details');
console.log(queries);
const { details, sensitive } = queries;
const { user_id, customer_id, customer_name, username } = user;
const metadata = PackTheMetadata({
user_id,
customer_id,
customer_name,
username,
details,
language,
sensitive,
});
const response: any = await this.clientService.getConnectionDetails({
body: { id },
metadata,
});
if (details === 'true') {
const properties = JSON.parse(response.connection.properties);
Object.assign(response.connection, {
pipelines: JSON.parse(response.connection.pipelines),
connection_controls: properties.connection_controls,
config_controls: properties.config_controls,
connection_steps: properties.connection_steps,
});
delete response.connection.properties;
} else {
Object.assign(response.connection, {
properties: JSON.parse(response.connection.properties),
pipelines: JSON.parse(response.connection.pipelines),
});
}
return response;
}
}
@@ -0,0 +1,22 @@
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { ConnectionClientService } from './client.service';
import { ConnectionClientConfiguration } from './client.config';
import { ConnectionController } from './connection.controller';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { UsersModule } from '../users/users.module';
import { ConnectorModule } from '../connector/connector.module';
const client = new ConnectionClientConfiguration();
@Module({
imports: [
ClientsModule.register([client.providerOptions]),
UsersModule,
ConnectorModule,
],
controllers: [ConnectionController],
providers: [ConnectionClientService, DadosferaLogger],
exports: [ConnectionClientService],
})
export class ConnectionModule {}
+189
View File
@@ -0,0 +1,189 @@
import {
ApiProperty,
ApiPropertyOptional,
OmitType,
PickType,
} from '@nestjs/swagger';
import {
Connection,
ConnectionToCatalog,
} from '@dadosfera/protospack-v2/dist/lib/ConnectionManager/interfaces/entities';
import {
GetAllConnectionResponse,
GetConnectionDetailsResponse,
GetConnectionResponse,
} from '@dadosfera/protospack-v2/dist/lib/ConnectionManager/interfaces/messages';
export type ConnectionTypes = 'database' | 'file' | 'application';
export type ConnectionCredentialsType =
| 'basic_auth'
| 'iam_user'
| 'role_name'
| 'oauth'
| 'api_key'
| 'service_account'
| 'headers_authF';
export interface ConnectionApiConnection {
config_id: string;
plugin: string;
secret_id: string;
created_at: string;
network_config_id: null;
type: ConnectionTypes;
credentials_type: ConnectionCredentialsType;
credentials: Record<string, any>;
sensitive_fields: string[];
name: string;
customer_name: string;
description: string;
updated_at: string;
}
export class ConnectionDto implements Connection {
// ---Automatically generated information will not be sent by the frontend--- //
@ApiProperty()
id: string;
@ApiProperty()
customer_id: string;
@ApiProperty()
customer_name: string;
@ApiProperty()
user_id: string;
@ApiProperty()
updated_at: string;
@ApiProperty()
created_at: string;
@ApiProperty()
pipelines: string;
// ---Information sent by the frontend--- //
@ApiProperty()
name: string;
@ApiProperty()
description: string;
@ApiProperty()
type: string;
@ApiPropertyOptional()
network_config_id: string | null;
@ApiProperty()
properties: DatabaseConnectionPropertiesDto | any;
// ---Information sent by the frontend (Heirs of Connector)--- //
@ApiProperty()
connector_id: string;
@ApiProperty()
connector_name: string;
@ApiProperty()
plugin: string;
@ApiProperty()
image_url: string;
}
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;
// ---Information extracted from token--- //
user_id: string;
username: string;
customer_name: string;
// ---Information sent by the frontend--- //
@ApiProperty()
name: string;
@ApiProperty()
description: string;
@ApiProperty()
type: string;
@ApiPropertyOptional()
network_config_id: string | null;
// ---Information sent by the frontend (Heirs of Connector)--- //
@ApiProperty()
connector_id: string;
@ApiProperty()
connector_name: string;
@ApiProperty()
plugin: string;
@ApiProperty()
image_url: string;
}
export class CreateConnectionDto extends OmitType(ConnectionDto, [
'id',
'customer_id',
'customer_name',
'user_id',
'created_at',
'updated_at',
'pipelines',
]) {}
export class UpdateConnectionDto extends PickType(ConnectionDto, [
'name',
'description',
'network_config_id',
'properties',
]) {}
export class ConnectionsRes implements GetAllConnectionResponse {
@ApiProperty({ type: [ConnectionToCatalogDto] })
connections: ConnectionToCatalogDto[];
@ApiProperty()
message: string;
}
export class ConnectionRes implements GetConnectionResponse {
@ApiProperty()
connection?: ConnectionToCatalogDto;
@ApiProperty()
message: string;
}
export class ConnectionDetailsRes implements GetConnectionDetailsResponse {
@ApiProperty()
connection?: ConnectionDto;
@ApiProperty()
message: string;
}
export class DatabaseConnectionPropertiesDto {
@ApiProperty()
credentials_type: 'basic_auth';
@ApiProperty()
plugin: string;
@ApiProperty()
host: string;
@ApiProperty()
port: string;
@ApiProperty()
username: string;
@ApiProperty()
password: string;
@ApiProperty()
database: string;
}
+32 -25
View File
@@ -1,31 +1,38 @@
import { credentials } from '@grpc/grpc-js';
import { ClientOptions, Transport } from '@nestjs/microservices';
import {
ClientsProviderAsyncOptions,
GrpcOptions,
Transport,
} from '@nestjs/microservices';
import { ConnectorManager } from '@dadosfera/protospack-v2';
const isLocalConnection = !!process.env.INFACTORY_URL?.includes('0.0.0.0');
export class ConnectorClientConfiguration {
config(): ClientOptions {
return {
transport: Transport.GRPC,
options: {
url: process.env.INFACTORY_URL,
package: [
ConnectorManager.ProtoPackages.WritePackage,
ConnectorManager.ProtoPackages.ReadPackage,
],
// credentials: undefined,
credentials: process.env.LOCAL_ENV
? undefined
: credentials.createSsl(),
protoPath: [
ConnectorManager.ProtoPaths.WriteFilePath,
ConnectorManager.ProtoPaths.ReadFilePath,
],
loader: {
enums: String,
objects: true,
arrays: true,
},
public name = 'ConnectorClientConfiguration';
private config: GrpcOptions = {
transport: Transport.GRPC,
options: {
url: process.env.INFACTORY_URL,
package: [
ConnectorManager.ProtoPackages.WritePackage,
ConnectorManager.ProtoPackages.ReadPackage,
],
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [
ConnectorManager.ProtoPaths.WriteFilePath,
ConnectorManager.ProtoPaths.ReadFilePath,
],
loader: {
keepCase: true,
enums: String,
objects: true,
arrays: true,
},
};
}
},
};
providerOptions: ClientsProviderAsyncOptions = {
name: this.name,
...this.config,
};
}
+37 -26
View File
@@ -8,12 +8,14 @@ import {
import { ClientGrpc } from '@nestjs/microservices';
import { ConnectorManager } from '@dadosfera/protospack-v2';
import { lastValueFrom } from 'rxjs';
import { ConnectorClientConfiguration } from './client.config';
export class ConnectorClientService implements OnModuleInit {
private connectorServiceRead: ConnectorManager.ReadService.ConnectorManagerReadServices;
private connectorServiceWrite: ConnectorManager.WriteService.ConnectorManagerWriteServices;
constructor(
@Inject('CONNECTOR_PACKAGE') private readonly grpcClient: ClientGrpc,
@Inject(ConnectorClientConfiguration.name)
private readonly grpcClient: ClientGrpc,
) {}
onModuleInit() {
@@ -43,7 +45,6 @@ export class ConnectorClientService implements OnModuleInit {
return lastValueFrom(
this.connectorServiceWrite.RegisterConnector(serviceBody),
).catch((err) => {
console.log(err.details);
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
@@ -62,7 +63,6 @@ export class ConnectorClientService implements OnModuleInit {
return lastValueFrom(this.connectorServiceWrite.UploadFile(body)).catch(
(err) => {
console.log(err.details);
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
@@ -71,27 +71,43 @@ export class ConnectorClientService implements OnModuleInit {
);
}
async uploadConnectorsFile(uploadFile) {
const connectors = JSON.parse(uploadFile.file.buffer.toString('utf8'));
const body = connectors.map((connector) => {
return JSON.stringify(connector);
});
const request = {
connectors: body,
};
return lastValueFrom(
this.connectorServiceWrite.RegisterMultipleConnectorsWithoutImage(
request,
),
async uploadConnectorsFile(uploadFile: Express.Multer.File) {
const res = await lastValueFrom(
this.connectorServiceWrite.RegisterMultipleConnectorsWithoutImage({
connectors: uploadFile.buffer,
}),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
const responseParsed = JSON.parse(res.message);
const connectorsResponse = responseParsed.items.filter(
(i) => i.update._index === process.env.CONNECTORS_INDEX,
);
const connectorErrors = connectorsResponse
.filter((i) => i.update.error)
.map((i) => ({
id: i.update?._id,
reason: i.update?.error,
status: i.error?.status,
}));
const connectorCreated = connectorsResponse
.filter((i) => i.update.result === 'created')
.map((i) => i.update._id);
const connectorUpdated = connectorsResponse
.filter((i) => i.update.result === 'updated')
.map((i) => i.update._id);
const connectorNoop = connectorsResponse
.filter((i) => i.update.result === 'noop')
.map((i) => i.update._id);
return {
errors: connectorErrors,
created: connectorCreated,
updated: connectorUpdated,
noop: connectorNoop,
};
}
async getAllConnectors(body) {
@@ -116,13 +132,13 @@ export class ConnectorClientService implements OnModuleInit {
});
}
async getConnectorDetails(plugin: string, language: string) {
async getConnectorDetails(connectorId: string, language: string) {
const meta = new Metadata();
meta.add('Language', language);
return lastValueFrom(
this.connectorServiceRead.GetConnectorDetails(
{
plugin,
plugin: connectorId,
},
meta,
),
@@ -169,12 +185,7 @@ export class ConnectorClientService implements OnModuleInit {
version,
plugin,
}),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
);
}
async updateConnector({ plugin, changes }) {
+53 -33
View File
@@ -11,16 +11,13 @@ import {
UploadedFile,
UseInterceptors,
Inject,
Headers,
UploadedFiles,
} from '@nestjs/common';
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
import { ApiBearerAuth, ApiConsumes, ApiTags } from '@nestjs/swagger';
import { ApiConsumes, ApiTags } from '@nestjs/swagger';
import { ConnectorClientService } from './client.service';
import { AddTagDto } from './dtos/add-tag';
import { CreateConnectorDto } from './dtos/create-connector';
import { UploadConnectorsFileDto } from './dtos/create-multiple-connector';
import { DeleteConnectorDto } from './dtos/delete-connector';
import { GetAllDto } from './dtos/get-all';
import { RemoveTagDto } from './dtos/remove-tag';
import { UpdateDto } from './dtos/update';
@@ -28,11 +25,15 @@ import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
Authenticated,
RequireAllPermissions,
} from 'src/authentication/authentication.decorator';
import { PERMISSIONS } from 'src/authentication/permissions.enum';
RequireSomePermission,
} from 'src/decorators/authentication.decorator';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import { Language } from 'src/decorators/language.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
@ApiInternalOnlyController()
@ApiTags('connectors')
@ApiBearerAuth()
@Authenticated()
@Controller('connectors')
export class ConnectorController {
@@ -46,7 +47,7 @@ export class ConnectorController {
}
@Post()
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.CREATE)
@RequireAllPermissions(PERMISSIONS_GROUPS.CONNECTORS.permissions.CREATE)
@ApiConsumes('multipart/form-data')
@UseInterceptors(FileInterceptor('file'))
async uploadConnector(
@@ -64,7 +65,7 @@ export class ConnectorController {
}
@Post('/uploads')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.CREATE)
@RequireAllPermissions(PERMISSIONS_GROUPS.CONNECTORS.permissions.CREATE)
@ApiConsumes('multipart/form-data')
@UseInterceptors(FilesInterceptor('files'))
async uploadFile(@UploadedFiles() files) {
@@ -81,26 +82,28 @@ export class ConnectorController {
}
@Post('/multiple-connectors-upload')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.CREATE)
@RequireAllPermissions(PERMISSIONS_GROUPS.CONNECTORS.permissions.CREATE)
@ApiConsumes('multipart/form-data')
@UseInterceptors(FileInterceptor('file'))
async uploadConnectorsFile(@UploadedFile() file: UploadConnectorsFileDto) {
console.log(`/upload`, 'Upload Connector Route');
async uploadConnectorsFile(@UploadedFile() file: Express.Multer.File) {
this.logger.info(`/upload`, 'Upload Connector Route');
const response = await this.connectorClientService.uploadConnectorsFile({
file,
});
return response;
return await this.connectorClientService.uploadConnectorsFile(file);
}
@Get()
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.GET)
@RequireSomePermission(
PERMISSIONS_GROUPS.CONNECTORS.permissions.GET,
PERMISSIONS_GROUPS.PIPELINE.permissions.CREATE,
PERMISSIONS_GROUPS.PIPELINE.permissions.GET,
PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE,
PERMISSIONS_GROUPS.PIPELINE.permissions.DELETE,
)
async getAllConnectors(
@Headers('dadosfera-lang') language,
@Language() language: LanguageEnum,
@Query() queries: GetAllDto,
) {
this.logger.info('/upload - Upload Connector Route');
this.logger.info('/GET - getAllConnectors Route');
const { search, size, page, ...filters } = queries;
@@ -112,7 +115,7 @@ export class ConnectorController {
page: page || 1,
});
const connectors = JSON.parse(response.connectors).connectors;
const connectors = JSON.parse(response.connectors);
return {
message: response.message,
@@ -121,19 +124,31 @@ export class ConnectorController {
}
@Get('/tags')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.GET)
@RequireSomePermission(
PERMISSIONS_GROUPS.CONNECTORS.permissions.GET,
PERMISSIONS_GROUPS.PIPELINE.permissions.CREATE,
PERMISSIONS_GROUPS.PIPELINE.permissions.GET,
PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE,
PERMISSIONS_GROUPS.PIPELINE.permissions.DELETE,
)
async getConnectorsTags() {
return await this.connectorClientService.getConnectorsTags();
}
@Get('/:plugin')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.GET)
@RequireSomePermission(
PERMISSIONS_GROUPS.CONNECTORS.permissions.GET,
PERMISSIONS_GROUPS.PIPELINE.permissions.CREATE,
PERMISSIONS_GROUPS.PIPELINE.permissions.GET,
PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE,
PERMISSIONS_GROUPS.PIPELINE.permissions.DELETE,
)
async getConnector(
@Headers('dadosfera-lang') language,
@Language() language: LanguageEnum,
@Param('plugin') plugin: string,
@Query('version') version: string,
) {
this.logger.info('/upload - Upload Connector Route');
this.logger.info('/GET/:plugin - getConnector Route');
const pluginId = `${plugin}-${version}`;
@@ -149,14 +164,19 @@ export class ConnectorController {
}
@Get('/:plugin/details')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.GET)
@RequireSomePermission(
PERMISSIONS_GROUPS.CONNECTORS.permissions.GET,
PERMISSIONS_GROUPS.PIPELINE.permissions.CREATE,
PERMISSIONS_GROUPS.PIPELINE.permissions.GET,
PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE,
PERMISSIONS_GROUPS.PIPELINE.permissions.DELETE,
)
async getConnectorDetails(
@Headers('dadosfera-lang') language,
@Language() language: LanguageEnum,
@Param('plugin') plugin: string,
@Query('version') version: string,
) {
this.logger.info('/upload - Upload Connector Route');
const pluginId = `${plugin}-${version}`;
const response: any = await this.connectorClientService.getConnectorDetails(
@@ -171,7 +191,7 @@ export class ConnectorController {
}
@Put('/:plugin')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.UPDATE)
@RequireAllPermissions(PERMISSIONS_GROUPS.CONNECTORS.permissions.UPDATE)
@ApiConsumes('multipart/form-data')
async updateConnector(
@Param('plugin') plugin: string,
@@ -193,7 +213,7 @@ export class ConnectorController {
}
@Put('/:plugin/add-tag')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.UPDATE)
@RequireAllPermissions(PERMISSIONS_GROUPS.CONNECTORS.permissions.UPDATE)
async addTagOnConnector(
@Param('plugin') plugin: string,
@Body() body: AddTagDto,
@@ -220,7 +240,7 @@ export class ConnectorController {
}
@Put('/:plugin/remove-tag')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.UPDATE)
@RequireAllPermissions(PERMISSIONS_GROUPS.CONNECTORS.permissions.UPDATE)
async removeTagOnConnector(
@Param('plugin') plugin: string,
@Body() body: RemoveTagDto,
@@ -248,10 +268,10 @@ export class ConnectorController {
}
@Delete('/:plugin')
@RequireAllPermissions(PERMISSIONS.CONNECTORS.permissions.DELETE)
@RequireAllPermissions(PERMISSIONS_GROUPS.CONNECTORS.permissions.DELETE)
async deleteConnector(
@Param('plugin') plugin: string,
@Body() { version }: DeleteConnectorDto,
@Query('version') version: string,
) {
this.logger.info('/upload - Upload Connector Route');
+3 -9
View File
@@ -5,18 +5,12 @@ import { ConnectorClientConfiguration } from './client.config';
import { ConnectorController } from './connector.controller';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
const connectorClient = new ConnectorClientConfiguration();
const client = new ConnectorClientConfiguration();
@Module({
controllers: [ConnectorController],
providers: [ConnectorClientService, DadosferaLogger],
imports: [
ClientsModule.register([
{
name: 'CONNECTOR_PACKAGE',
...connectorClient.config(),
},
]),
],
imports: [ClientsModule.register([client.providerOptions])],
exports: [ConnectorClientService],
})
export class ConnectorModule {}
+37
View File
@@ -0,0 +1,37 @@
import { credentials } from '@grpc/grpc-js';
import {
ClientsProviderAsyncOptions,
GrpcOptions,
Transport,
} from '@nestjs/microservices';
import {
ProtoPackages,
ProtoPaths,
} from '@dadosfera/protospack-v2/dist/lib/Duc';
const isLocalConnection = !!process.env.DUC_URL?.includes('0.0.0.0');
export class DucClient {
public name = 'DucClient';
private config: GrpcOptions = {
transport: Transport.GRPC,
options: {
url: process.env.DUC_URL,
package: [ProtoPackages.WritePackage, ProtoPackages.ReadPackage],
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [ProtoPaths.WriteFilePath, ProtoPaths.ReadFilePath],
loader: {
keepCase: true,
enums: String,
objects: true,
arrays: true,
},
},
};
providerOptions: ClientsProviderAsyncOptions = {
name: this.name,
...this.config,
};
}
+12
View File
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { HealthController } from './health.controller';
import { HealthService } from './health.service';
@Module({
controllers: [HealthController],
providers: [HealthService, DadosferaLogger],
})
export class HealthModule {}
+38 -195
View File
@@ -1,135 +1,45 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Info } from 'src/clients/inputs/interfaces';
import { Method } from 'axios';
import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger';
export class InputModel {
category: string;
plugin: string;
name: string;
cron?: string;
credentials?: {
connection_type: string;
oauth: string;
oauth_code: string;
start_date: string;
};
options?: {
oauth?: {
get_tokens_url: string;
get_tokens_method?: Method;
get_tokens_url_params: string;
get_tokens_set_response: Record<string, string>;
content_type: string;
};
};
}
class GoogleAnalyticsClientSecrets {
@ApiProperty()
type: string;
@ApiProperty()
project_id: string;
@ApiProperty()
private_key_id: string;
@ApiProperty()
private_key: string;
@ApiProperty()
client_email: string;
@ApiProperty()
client_id: string;
@ApiProperty()
auth_uri: string;
@ApiProperty()
token_uri: string;
@ApiProperty()
auth_provider_x509_cert_url: string;
@ApiProperty()
client_x509_cert_url: string;
}
class OauthObject {
@ApiPropertyOptional()
get_tokens_url: string;
@ApiPropertyOptional()
get_tokens_url_params: string;
@ApiPropertyOptional()
get_tokens_set_response: Record<string, any>;
@ApiPropertyOptional()
content_type: string;
}
class InputOptions {
@ApiPropertyOptional()
oauth: OauthObject;
@ApiPropertyOptional()
skip_select_columns: true;
@ApiPropertyOptional()
skip_select_entities: false;
@ApiPropertyOptional()
skip_transformation: true;
}
class CredentialsJdbc {
@ApiPropertyOptional()
jdbc_user: string;
@ApiPropertyOptional()
jdbc_password: string;
@ApiPropertyOptional()
database: string;
@ApiPropertyOptional()
endpoint: string;
@ApiPropertyOptional()
port: string;
@ApiPropertyOptional()
engine: string;
@ApiPropertyOptional()
schema: string;
}
class FileFormatParams {
@ApiPropertyOptional()
file_format: string;
@ApiPropertyOptional()
encoding: string;
}
class Credentials extends CredentialsJdbc {
@ApiProperty()
connection_type: string;
@ApiPropertyOptional()
client_aws_access_key_id: string;
@ApiPropertyOptional()
client_aws_secret_access_key: string;
@ApiPropertyOptional()
client_bucket: string;
@ApiPropertyOptional()
file_to_extract: string;
@ApiPropertyOptional()
file_format_params: FileFormatParams;
@ApiPropertyOptional()
view_id: string;
@ApiPropertyOptional()
client_secrets: GoogleAnalyticsClientSecrets;
@ApiPropertyOptional()
start_date: string;
@ApiPropertyOptional()
end_date: string;
@ApiPropertyOptional()
oauth_code: string;
}
class Column {
export class Column {
@ApiProperty()
name: string;
@ApiProperty()
type: string;
}
class TableColumns {
export class TableColumns {
@ApiProperty()
name: string;
@ApiProperty()
columns: Column[];
columns: string[];
@ApiProperty()
references: Column[];
@ApiProperty()
type: string;
}
export class AvailableEntity {
@ApiProperty()
name: string;
@ApiProperty()
replication_methods: string[];
}
export class Input {
@ApiProperty()
id: string;
@ApiProperty({ type: [TableColumns] })
tables: TableColumns[];
@ApiProperty()
name: string;
@ApiProperty()
description: string;
@ApiProperty()
plugin: string;
@ApiPropertyOptional()
type?: string;
@ApiProperty()
created_at: string;
@ApiProperty()
updated_at: string;
}
export class GetAvailableEntitiesReq {
@ApiProperty()
@@ -137,79 +47,12 @@ export class GetAvailableEntitiesReq {
}
export class GetAvailableEntitiesRes {
@ApiProperty()
entities: string[];
@ApiProperty({ type: [AvailableEntity] })
entities: AvailableEntity[];
}
export class TestConnectionGetColumnsReq {
@ApiProperty()
plugin: string;
@ApiProperty()
tables: string[];
@ApiPropertyOptional()
credentials: CredentialsJdbc;
@ApiPropertyOptional()
id: string;
@ApiPropertyOptional()
info: Info;
}
export class TestConnectionGetColumnsRes {
@ApiProperty()
tables: TableColumns[];
}
export class TestConnectionReq {
@ApiProperty()
plugin: string;
@ApiProperty()
credentials: CredentialsJdbc;
@ApiPropertyOptional()
info: Info;
}
export class TestConnectionRes {
@ApiProperty()
connection_state: boolean;
@ApiProperty()
total_entities: number;
@ApiProperty()
database_tables: string[];
}
export class CreateInputReq {
@ApiPropertyOptional()
id: string;
@ApiProperty()
plugin: string;
@ApiProperty()
category: string;
@ApiProperty()
credentials: Credentials;
@ApiProperty()
name: string;
@ApiPropertyOptional()
options: InputOptions;
@ApiPropertyOptional()
info: Info;
}
export class Input {
@ApiProperty()
id: string;
@ApiProperty()
category: string;
@ApiProperty()
plugin: string;
@ApiProperty()
name: string;
@ApiProperty()
cron: string;
@ApiProperty()
credentials: Credentials;
@ApiProperty()
client_id: string;
@ApiProperty()
created_at: string;
@ApiProperty()
updated_at: string;
@ApiPropertyOptional()
info: Info;
}
export class CreateInputReq extends OmitType(Input, [
'id',
'created_at',
'updated_at',
]) {}
@@ -1,3 +1,5 @@
import { Info } from 'protospack/dist/lib/interfaces';
interface Values {
jdbc_user: string;
jdbc_password: string;
@@ -9,32 +11,6 @@ interface Values {
schema: string;
}
interface Cron {
hours: string[];
hour_interval: boolean;
hour_resourse: boolean;
hour_resourse_value: number;
month_day: string[];
month_day_interval: boolean;
month_day_resourse: boolean;
month_day_resourse_value: number;
month: string[];
month_interval: boolean;
month_resourse: boolean;
month_resourse_value: number;
week_days: string[];
week_days_interval: boolean;
week_days_resourse: boolean;
week_days_resourse_value: number;
}
export interface Info {
customer_id: string;
user_id: string;
customer: string;
customer_tier: string;
}
export interface ICreateInputRequest {
cron: string;
name: string;
@@ -56,6 +32,7 @@ export interface UpdateInputRequest {
values: Values;
operation: string;
info: Info;
tables: any[];
}
export interface ITestConnectionRequest {
@@ -0,0 +1,36 @@
import { Input } from '@dadosfera/protospack-v2';
import { credentials } from '@grpc/grpc-js';
import {
ClientProviderOptions,
Transport,
type GrpcOptions,
} from '@nestjs/microservices';
const isLocalConnection = !!process.env.INFACTORY_URL?.includes('0.0.0.0');
export class InputsGrpcClient {
public readonly name = 'InputsGrpcClient';
private config: GrpcOptions = {
transport: Transport.GRPC,
options: {
url: process.env.INFACTORY_URL,
package: [
Input.ProtoPackages.WritePackage,
Input.ProtoPackages.ReadPackage,
],
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [
Input.ProtoPaths.WriteFilePath,
Input.ProtoPaths.ReadFilePath,
],
loader: {
keepCase: true,
enums: String,
defaults: false,
},
},
};
providerOptions: ClientProviderOptions = {
name: this.name,
...this.config,
};
}
+62 -83
View File
@@ -7,140 +7,117 @@ import {
Param,
Patch,
Post,
UseFilters,
} from '@nestjs/common';
import { InputsService } from './inputs.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { InputsClientService } from 'src/clients/inputs/client.service';
import { UpdateInputRequest } from 'src/clients/inputs/interfaces';
import { PERMISSIONS } from '../../authentication/permissions.enum';
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
import { PERMISSIONS_GROUPS } from '../../authentication/permissions.enum';
import { AuthenticateCondition } from 'src/decorators/authentication.decorator';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import {
CreateInputReq,
GetAvailableEntitiesReq,
GetAvailableEntitiesRes,
Input,
TestConnectionGetColumnsReq,
TestConnectionGetColumnsRes,
TestConnectionReq,
TestConnectionRes,
} from './dtos/input.model';
import { UpdateInputRequest } from './dtos/old_interfaces';
import { RequestUser, User } from 'src/decorators/user.decorator';
import { Info } from '@dadosfera/protospack-v2/dist/lib/Input/interfaces/entities';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
@ApiTags('inputs')
@ApiTags('Inputs')
@Controller('inputs')
@AuthenticateCondition((req, user) => {
let action: any;
switch (req.method) {
case 'POST':
action = 'CREATE';
break;
case 'PUT':
action = 'UPDATE';
break;
case 'PATCH':
action = 'UPDATE';
break;
return (
user.permissions.includes(
PERMISSIONS_GROUPS.PIPELINE.permissions.CREATE.seqid,
) ||
user.permissions.includes(
PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE.seqid,
)
);
default:
action = req.method;
return (
user.permissions.includes(
PERMISSIONS_GROUPS.PIPELINE.permissions.CREATE.seqid,
) ||
user.permissions.includes(
PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE.seqid,
) ||
user.permissions.includes(
PERMISSIONS_GROUPS.PIPELINE.permissions.GET.seqid,
)
);
}
if (
`${req.method} ${req.route.path}` ===
'GET /inputs/available-entities/:plugin'
) {
action = 'GET_ENTITIES';
}
return user.permissions.includes(PERMISSIONS.INPUT.permissions[action].seqid);
})
@UseFilters(new GrpcToHttpExceptionFilter())
export class InputsController {
inputService: InputsService;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private inputsClientService: InputsClientService,
private inputService: InputsService,
) {
this.logger = dadosferaLogger.logger;
this.inputService = new InputsService(this.inputsClientService);
}
@ApiInternalOnlyEndpoint()
@Get('available-entities/:plugin')
@ApiOkResponse({ type: GetAvailableEntitiesRes })
async getAvailableEntities(
@Body() body,
@Param() params: GetAvailableEntitiesReq,
@User() user: RequestUser,
) {
const { info } = body;
const { plugin } = params;
return await this.inputService.getAvailableEntities({ info, plugin });
}
@Post('/test-connection')
@ApiOkResponse({ type: TestConnectionRes })
async testConnection(@Body() data: TestConnectionReq) {
this.logger.info(`/test-connection - ON TEST CONNECTION ROUTE`, {
user: data.info.user_id,
customer: data.info.customer,
});
const response = await this.inputService.testConnection(data);
return response;
}
@Post('/test-connection/get-columns')
@ApiOkResponse({ type: TestConnectionGetColumnsRes })
async getColumns(@Body() data: TestConnectionGetColumnsReq) {
this.logger.info(
`/test-connection/get-columns - ON TEST CONNECTION GET COLUMNS ROUTE`,
{
user: data.info.user_id,
customer: data.info.customer,
const availableEntities = await this.inputService.getAvailableEntities({
info: {
customer: user.customer_name,
customer_id: user.customer_id,
user_id: user.user_id,
},
);
const response = await this.inputService.getColumns(data);
return response;
plugin,
});
return availableEntities;
}
@Post()
@ApiOkResponse({ type: Input })
async create(@Body() createInputDto: CreateInputReq) {
async create(@Body() body: CreateInputReq, @User() user: RequestUser) {
const info: Info = {
user_id: user.user_id,
customer: user.customer_name,
customer_id: user.customer_id,
};
this.logger.info(`/input - ON CREATE ROUTE`, {
user: createInputDto.info.user_id,
customer: createInputDto.info.customer,
user: info.user_id,
customer: info.customer,
});
const response = await this.inputService.create(createInputDto);
return response;
}
@Post(':id')
@ApiOkResponse({ type: Input })
async reCreate(@Param('id') id: string, @Body() input: CreateInputReq) {
if (!input.id) input.id = id;
this.logger.info(`POST /${id}`, 'ON RECREATE ROUTE');
const response = await this.inputService.reCreate(input);
const response = await this.inputService.create({ body, info });
return response;
}
@ApiInternalOnlyEndpoint()
@Get()
async findAll(@Body() body) {
async findAll(@User() user: RequestUser) {
const { user_id, customer_id, customer_name: customer } = user;
this.logger.info(`/input - ON FIND ALL ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
user: user.user_id,
customer,
});
const response = await this.inputService.findAll(body);
const info: Info = {
customer,
customer_id,
user_id,
};
const response = await this.inputService.findAll(info);
return response;
}
@@ -158,6 +135,7 @@ export class InputsController {
return response;
}
@ApiInternalOnlyEndpoint()
@Patch(':id')
async update(@Body() updateInputDto: UpdateInputRequest, @Param() params) {
const { id } = params;
@@ -173,6 +151,7 @@ export class InputsController {
return response;
}
@ApiInternalOnlyEndpoint()
@Delete(':id')
async delete(@Body() data, @Param() params) {
const { id } = params;
+16
View File
@@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { InputsGrpcClient } from './inputs-client.config';
import { InputsController } from './inputs.controller';
import { InputsService } from './inputs.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
const client = new InputsGrpcClient();
@Module({
controllers: [InputsController],
providers: [InputsService, DadosferaLogger],
imports: [ClientsModule.register([client.providerOptions])],
exports: [InputsService],
})
export class InputsModule {}
+161 -93
View File
@@ -1,20 +1,132 @@
import { Body, HttpException, HttpStatus, Injectable } from '@nestjs/common';
import {
ForbiddenException,
HttpException,
HttpStatus,
Inject,
Injectable,
} from '@nestjs/common';
import { Timeout } from '@nestjs/schedule';
import { DecodeGrpcStruct } from 'protospack';
import CronParser, { CronExpression } from 'cron-parser';
import { InputsClientService } from 'src/clients/inputs/client.service';
import { IIdRequest, Info } from 'src/clients/inputs/interfaces';
import { ClientGrpc } from '@nestjs/microservices';
import DadosferaLogger from '@dadosfera/dadosfera-logs/dist';
import { lastValueFrom } from 'rxjs';
import { objectCamelToSnake } from 'src/utils/CaseConverter';
import { IIdRequest, UpdateInputRequest } from './dtos/old_interfaces';
import { Input } from '@dadosfera/protospack-v2';
import {
GetAvailableEntitiesRequest,
InputCreateGenericRequest,
InputCreateS3Request,
InputNewCreateRequest,
TestConnectionRequest,
} from '@dadosfera/protospack-v2/dist/lib/Input/interfaces/messages';
import { Info } from '@dadosfera/protospack-v2/dist/lib/Input/interfaces/entities';
import { CreateInputReq } from './dtos/input.model';
@Injectable()
//TODO refactor this class to remove OLD_inputClient
export class InputsService {
constructor(private inputClient: InputsClientService) {}
logger: DadosferaLogger;
inputWriteService: Input.WriteService.InputWriteService;
inputReadService: Input.ReadService.InputReadService;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject('InputsGrpcClient') private readonly grpcClient: ClientGrpc,
) {
this.logger = dadosferaLogger.logger;
}
async onModuleInit() {
this.inputWriteService =
this.grpcClient.getService<Input.WriteService.InputWriteService>(
Input.ProtoServices.InputWriteService,
);
this.inputReadService =
this.grpcClient.getService<Input.ReadService.InputReadService>(
Input.ProtoServices.InputReadService,
);
}
OLD_inputClient = {
newCreate: async (createInputDto: InputNewCreateRequest) => {
this.logger.info('InputClientService - Create');
const input = await lastValueFrom(
this.inputWriteService.InputNewCreate(createInputDto),
);
objectCamelToSnake(input);
return input;
},
createS3Inputs: async (createInputDto: InputCreateS3Request) => {
this.logger.info('InputClientService - Create');
createInputDto;
const createInputResponse = await lastValueFrom(
this.inputWriteService.InputCreateS3(createInputDto),
).catch((e) => {
throw new HttpException(e.details, 500);
});
this.logger.info('Done');
objectCamelToSnake(createInputResponse);
return createInputResponse;
},
update: async (updateInputDTO: UpdateInputRequest) => {
this.logger.info('InputClientService - Update');
const updateInputResponse = await lastValueFrom(
this.inputWriteService.InputUpdate(updateInputDTO),
);
return updateInputResponse;
},
testConnection: async (data: TestConnectionRequest) => {
this.logger.info('InputClientService - TestConnection');
const testConnectionResponse = await lastValueFrom(
this.inputReadService.NewTestConnection(data),
);
return testConnectionResponse;
},
getAvailableEntities: async (data) => {
this.logger.info('InputClientService - GetAvailableEntities');
data;
const response = await lastValueFrom(
this.inputReadService.GetAvailableEntities(data),
).catch((err) => {
this.logger.info(err.details);
if (err.details && err.details.includes('400'))
throw new HttpException('Plugin inválido', HttpStatus.BAD_REQUEST);
throw new HttpException(err.details, HttpStatus.INTERNAL_SERVER_ERROR);
});
objectCamelToSnake(response);
return response;
},
};
secondsInADay = 60 * 60 * 24;
secondsInAnHour = 60 * 60;
adjustInputPayload(payload) {
if (payload?.input_generic) return DecodeGrpcStruct(payload.input_generic);
if (!payload.input_s3 && !payload.input_jdbc) return payload;
return payload?.input_s3 || payload?.input_jdbc;
if (payload) {
if (payload.input_generic) {
const { credentials } = payload.input_generic;
return {
...payload.input_generic,
credentials: credentials && JSON.parse(credentials),
};
}
if (payload.input_s3) return payload.input_s3;
if (payload.input_jdbc) return payload.input_jdbc;
const { credentials } = payload;
return {
...payload,
credentials: credentials && JSON.parse(credentials),
};
}
return payload;
}
getDifferenceInSeconds(date1: Date, date2: Date) {
const diffInMs = Math.abs(date2.getTime() - date1.getTime());
@@ -37,87 +149,61 @@ export class InputsService {
const afterNextDate = interval.next().toDate();
const secondsApart = this.getDifferenceInSeconds(nextDate, afterNextDate);
if (customer_tier === 'BASIC' && secondsApart < this.secondsInADay) {
throw new HttpException(
throw new ForbiddenException(
'Intervalo de tempo não pode ser inferior a um dia.',
HttpStatus.FORBIDDEN,
);
} else if (secondsApart < this.secondsInAnHour) {
throw new HttpException(
throw new ForbiddenException(
'Intervalo de tempo não pode ser inferior a uma hora.',
HttpStatus.FORBIDDEN,
);
}
}
async create(@Body() data) {
this.validateCron(data);
let response;
switch (data.plugin.toLowerCase()) {
case 'csv':
case 'json':
case 'parquet': {
const { info, ...input } = data;
const inputPayload = this.generateInputS3Payload(input);
response = await this.inputClient.createS3Inputs({
input: inputPayload,
info,
});
break;
}
case 'oracle':
case 'mysql':
case 'postgresql':
case 'sqlserver':
response = await this.inputClient.newCreate(data);
break;
default:
response = await this.inputClient.createGeneric(data);
break;
}
const adjustedInput = this.adjustInputPayload(response.input);
return { ...response, input: adjustedInput };
async create(data: { body: CreateInputReq; info: Info }) {
// this.validateCron(data);
const { body, info } = data;
const inputCreateGenericRequest: InputCreateGenericRequest = {
input: {
...body,
},
info,
};
const { input } = await lastValueFrom(
this.inputWriteService.InputCreate(inputCreateGenericRequest),
);
const adjustedInput = this.adjustInputPayload(input);
return { input: adjustedInput };
}
async reCreate(data) {
const { info, cron } = data;
if (cron) this.validateCron({ info, cron });
const response = await this.inputClient.createGeneric(data);
const adjustedInput = this.adjustInputPayload(response.input);
return { ...response, input: adjustedInput };
async getAvailableEntities(data: GetAvailableEntitiesRequest) {
return lastValueFrom(this.inputReadService.GetAvailableEntities(data));
}
async getAvailableEntities(data): Promise<{ entities: string[] }> {
return await this.inputClient.getAvailableEntities(data);
}
async findAll(body) {
try {
const findAllInputResponse: any = await this.inputClient.findAll(body);
if (findAllInputResponse?.inputs?.length) {
findAllInputResponse.inputs = findAllInputResponse.inputs.map((input) =>
this.adjustInputPayload(input),
);
}
return findAllInputResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
async findAll(info: Info) {
const { inputs } = await lastValueFrom(
this.inputReadService.InputFindAll({ info }),
);
if (!inputs) return { inputs: [] };
const inputsReturn = inputs.map((i) => this.adjustInputPayload(i));
return { inputs: inputsReturn };
}
async findOne(idRequest: IIdRequest) {
try {
const findOneInputResponse: any = await this.inputClient.findOne(
idRequest,
);
findOneInputResponse.input = this.adjustInputPayload(
findOneInputResponse.input,
);
return findOneInputResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
const findOneInputResponse: any = await lastValueFrom(
this.inputReadService.InputFindOne(idRequest),
);
findOneInputResponse.input = this.adjustInputPayload(
findOneInputResponse.input,
);
return findOneInputResponse;
}
async update(id: string, data, info: Info) {
this.validateCron({ ...data, info });
try {
const updateInputResponse: any = await this.inputClient.update({
const updateInputResponse: any = await this.OLD_inputClient.update({
id,
info,
...data,
@@ -133,21 +219,14 @@ export class InputsService {
}
async remove(idRequest: IIdRequest) {
try {
const removeInputResponse = await this.inputClient.remove(idRequest);
return removeInputResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
return lastValueFrom(this.inputWriteService.InputRemove(idRequest));
}
@Timeout(60000 * 10) // Timeout set for 10 minutes
async testConnection(data) {
try {
const testConnectionInputResponse = await this.inputClient.testConnection(
data,
);
const testConnectionInputResponse =
await this.OLD_inputClient.testConnection(data);
return testConnectionInputResponse;
} catch (err) {
@@ -155,17 +234,6 @@ export class InputsService {
}
}
async getColumns(data) {
try {
const testConnectionGetColumnsResponse =
await this.inputClient.getColumns(data);
return testConnectionGetColumnsResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
generateInputS3Payload(payload) {
const { credentials, plugin, cron } = payload;
if (!credentials) return payload;
@@ -0,0 +1,93 @@
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';
import { RequestUser, User } from 'src/decorators/user.decorator';
@ApiInternalOnlyController()
@Authenticated()
@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);
const separator = user.username.includes('-') ? '-' : '.';
const removeValues = [
'.dadosferatech.dadosfera',
'.demo.dadosfera',
'.dadosferademo',
'.dadosferarh.dadosfera',
'.dadosferatech.dadosfera2',
'.dadosferatech.dadosfera',
'.dadosfera.fin',
'.dadosferafin.dadosfera',
'.praxio.dadosfera',
'.dadosfera.tech',
'.treinamentos@dadosfera.ai',
'.dadosfera2',
'.treinamentosfera',
'.dadosfera',
];
let username = user.username;
removeValues.forEach((value) => {
username = username.replace(value, '');
});
username = username.split('@')?.[0];
username = username.split('+')?.[0];
let firstName = username
.substring(0, username.indexOf(separator))
.replace('dadosfera', '');
let lastName = username
.substring(username.lastIndexOf(separator) + 1)
.replace('dadosfera', '');
if (!firstName) {
firstName = lastName;
lastName = '';
}
firstName = this.capitalize(firstName);
lastName = this.capitalize(lastName);
await mixpanel.people.set(user.username, {
$first_name: firstName,
$last_name: lastName,
$name: this.getFullName(firstName, lastName),
$email: user.username.includes('@')
? user.username
: user.username + '@dadosfera.ai',
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 };
}
capitalize(sentence: string): string {
if (!sentence) {
return '';
}
return sentence[0].toUpperCase() + sentence.substring(1);
}
getFullName(firstName: string, lastName: string) {
return `${firstName}${lastName ? ' ' + lastName : ''}`;
}
}
+16
View File
@@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { getSecretFromSecretsManager } from 'src/utils/SecretManager';
import { MixpanelController } from './mixpanel.controller';
@Module({
controllers: [MixpanelController],
providers: [
{
provide: 'MIXPANEL_TOKEN',
useValue: getSecretFromSecretsManager(
`${process.env.ENV}/root/mixpanel_token`,
),
},
],
})
export class MixpanelModule {}
@@ -0,0 +1,172 @@
import {
NetworkConfigEntity,
NetworkConfigToCatalog,
} from '@dadosfera/protospack-v2/dist/lib/NetworkConfig/interfaces/entities';
import {
ApiProperty,
ApiPropertyOptional,
OmitType,
PickType,
} from '@nestjs/swagger';
export enum NetworkConfigTypesEnum {
vpn = 'vpn',
}
export enum NetworkConfigPluginsEnum {
openvpn = 'openvpn',
}
export interface ConnectionsApiNetworkConfig {
config_id: string;
created_at: string;
credentials: {
CERTIFICATE_FILE: string;
OVPN_CREDENTIALS: string;
TLS_AUTH_FILE: string;
OVPN_FILE: string;
};
customer_name: string;
description: string;
name: string;
plugin: string;
secret_id: string;
sensitive_fields: string[];
type: string;
updated_at: string;
}
export class NetworkConfigDto implements NetworkConfigEntity {
@ApiProperty()
id: string;
@ApiProperty()
created_at: string;
@ApiProperty()
updated_at: string;
@ApiProperty()
customer_name: string;
@ApiProperty()
customer_id: string;
@ApiProperty()
properties: any;
@ApiProperty()
user_id: string;
@ApiProperty()
keywords: string;
@ApiProperty()
tags: string;
@ApiProperty()
network_type_id: string;
@ApiProperty()
network_type_name: string;
@ApiProperty({ enum: NetworkConfigTypesEnum })
type: string;
@ApiProperty({ enum: NetworkConfigPluginsEnum })
plugin: string;
@ApiProperty()
name: string;
@ApiProperty()
description: string;
}
export class CreateNetworkConfigReq extends PickType(NetworkConfigDto, [
'network_type_id',
'network_type_name',
'name',
'description',
]) {
@ApiProperty({ format: 'binary' })
ovpn_file: string;
}
export class NetworkConfigOpenVpnProperties {
@ApiProperty({ enum: NetworkConfigPluginsEnum })
plugin: NetworkConfigPluginsEnum;
@ApiProperty()
OVPN_FILE: string;
@ApiPropertyOptional()
CERTIFICATE_FILE?: string;
@ApiPropertyOptional()
OVPN_CREDENTIALS?: string;
@ApiPropertyOptional()
TLS_AUTH_FILE?: string;
}
export class NetworkConfigToCatalogDto implements NetworkConfigToCatalog {
@ApiProperty()
id: string;
@ApiProperty()
customer_id: string;
@ApiProperty()
plugin: string;
@ApiProperty()
type: string;
@ApiProperty()
name: string;
@ApiProperty()
description: string;
@ApiProperty()
created_at: string;
@ApiProperty()
updated_at: string;
@ApiProperty()
network_type_id: string;
@ApiProperty()
network_type_name: string;
}
export class FindAllNetworkConfigsRes {
@ApiProperty({ type: [NetworkConfigToCatalogDto] })
network_configs: NetworkConfigToCatalogDto[];
@ApiProperty()
message: string;
}
export class FindNetworkConfigByidRes {
@ApiProperty()
network_config: NetworkConfigToCatalogDto;
}
export class FindNetworkConfigByidDetails extends PickType(NetworkConfigDto, [
'id',
'name',
'description',
'type',
'network_type_id',
'customer_name',
]) {
@ApiProperty()
properties: NetworkConfigOpenVpnProperties | string;
}
export class FindNetworkConfigByidDetailsRes {
@ApiProperty()
network_config: FindNetworkConfigByidDetails;
}
export class CreateNetworkConfigRes {
@ApiProperty()
network_config: NetworkConfigToCatalogDto;
}
export class UpdateNetworkConfigReq {
@ApiProperty()
name: string;
@ApiProperty()
description: string;
}
export class UpdateNetworkConfigRes {
@ApiProperty()
network_config: NetworkConfigToCatalogDto;
}
export class DeleteNetworkConfigRes {
@ApiProperty()
message: string;
}
@@ -0,0 +1,39 @@
import { credentials } from '@grpc/grpc-js';
import {
ClientProviderOptions,
GrpcOptions,
Transport,
} from '@nestjs/microservices';
import { NetworkConfig } from '@dadosfera/protospack-v2';
const isLocalConnection = !!process.env.INFACTORY_URL?.includes('0.0.0.0');
export class NetworkConfigGrpcClient {
public name = 'NetworkConfigGrpcClient';
private config: GrpcOptions = {
transport: Transport.GRPC,
options: {
url: process.env.INFACTORY_URL,
package: [
NetworkConfig.ProtoPackages.ReadPackage,
NetworkConfig.ProtoPackages.WritePackage,
],
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [
NetworkConfig.ProtoPaths.ReadFilePath,
NetworkConfig.ProtoPaths.WriteFilePath,
],
loader: {
keepCase: true,
enums: String,
objects: true,
arrays: true,
},
},
};
providerOptions: ClientProviderOptions = {
name: this.name,
...this.config,
};
}
@@ -0,0 +1,151 @@
import {
Body,
Controller,
Get,
Inject,
Param,
Post,
Query,
UploadedFile,
UseInterceptors,
} from '@nestjs/common';
import { NetworkConfigService } from './network-config.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { ApiConsumes, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { RequestUser, User } from 'src/decorators/user.decorator';
import {
CreateNetworkConfigReq,
CreateNetworkConfigRes,
FindAllNetworkConfigsRes,
} from './dto/network-config';
import { FileInterceptor } from '@nestjs/platform-express';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import {
Authenticated,
RequireAllPermissions,
} from 'src/decorators/authentication.decorator';
import { Language } from 'src/decorators/language.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
const networkConfigPermissions = PERMISSIONS_GROUPS.NETWORK_CONFIG.permissions;
@ApiTags('Network Config')
@Authenticated()
@Controller('network-config')
export class NetworkConfigController {
logger: any;
constructor(
private networkConfigService: NetworkConfigService,
@Inject(DadosferaLogger) dadosferaLogger: DadosferaLogger,
) {
this.logger = dadosferaLogger.logger;
}
@Get()
@ApiOkResponse({ type: FindAllNetworkConfigsRes })
async findAllNetworkConfigs(
@User() user: RequestUser,
@Language() language: LanguageEnum,
@Query() queries,
) {
this.logger.info('GET /network-config', {
user: user.user_id,
customer: user.customer_name,
});
const { user_id, customer_id, customer_name } = user;
const { search, size, page, ...filters } = queries;
return this.networkConfigService.findAllNetworkConfigs({
body: { search, size, page, filters },
info: { user_id, customer_id, customer_name, language },
});
}
@Get(':id')
@ApiOkResponse({ type: FindAllNetworkConfigsRes })
async findNetworkConfigById(
@User() user: RequestUser,
@Param('id') id: string,
) {
this.logger.info('GET /network-config/:id', {
user: user.user_id,
customer: user.customer_name,
});
return this.networkConfigService.findNetworkConfigById(id);
}
@Get(':id/details')
@ApiOkResponse({ type: FindAllNetworkConfigsRes })
async findNetworkConfigByIdDetails(
@User() user: RequestUser,
@Language() language: LanguageEnum,
@Param('id') id: string,
) {
this.logger.info('GET /network-config/:id', {
user: user.user_id,
customer: user.customer_name,
});
return this.networkConfigService.findNetworkConfigByIdDetails(id, {
customer_name: user.customer_name,
customer_id: user.customer_id,
language,
});
}
@Post()
@ApiOkResponse({ type: CreateNetworkConfigRes })
@RequireAllPermissions(networkConfigPermissions.CREATE)
@ApiConsumes('multipart/form-data')
@UseInterceptors(FileInterceptor('ovpn_file'))
async createNetworkConfigs(
@User() user: RequestUser,
@UploadedFile() file: Express.Multer.File,
@Body() body: CreateNetworkConfigReq,
) {
this.logger.info('POST /network-config', {
user: user.user_id,
customer: user.customer_name,
});
const { customer_id, customer_name, user_id } = user;
return this.networkConfigService.createNetworkConfig(body, file, {
customer_name,
customer_id,
user_id,
});
}
// @Patch(':id')
// @ApiOkResponse({ type: UpdateNetworkConfigRes })
// @RequireAllPermissions(networkConfigPermissions.CREATE)
// async updateNetworkConfig(
// @User() user: RequestUser,
// @Param('id') network_config_id: string,
// @Body() body: UpdateNetworkConfigReq,
// ) {
// this.logger.info('PATCH /network-config/:id', {
// user: user.user_id,
// customer: user.customer_name,
// });
// return this.networkConfigService.updateNetworkConfig(
// body,
// network_config_id,
// );
// }
// @Delete(':id')
// @ApiOkResponse({ type: DeleteNetworkConfigRes })
// async deleteNetworkConfig(
// @User() user: RequestUser,
// @Param('id') network_config_id: string,
// ) {
// this.logger.info('DELETE /network-config', {
// user: user.user_id,
// customer: user.customer_name,
// });
// return this.networkConfigService.deleteNetworkConfig(network_config_id, {
// customer_name: user.customer_name,
// customer_id: user.customer_id,
// });
// }
}
@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { NetworkConfigController } from './network-config.controller';
import { NetworkConfigService } from './network-config.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { ClientsModule } from '@nestjs/microservices';
import { NetworkConfigGrpcClient } from './network-config-client.config';
const client = new NetworkConfigGrpcClient();
@Module({
controllers: [NetworkConfigController],
providers: [NetworkConfigService, DadosferaLogger],
imports: [ClientsModule.register([client.providerOptions])],
})
export class NetworkConfigModule {}
@@ -0,0 +1,158 @@
import { Metadata } from '@grpc/grpc-js';
import { Inject, Injectable } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { NetworkConfig } from '@dadosfera/protospack-v2';
import { lastValueFrom } from 'rxjs';
import {
CreateNetworkConfigReq,
CreateNetworkConfigRes,
DeleteNetworkConfigRes,
FindAllNetworkConfigsRes,
FindNetworkConfigByidDetailsRes,
FindNetworkConfigByidRes,
UpdateNetworkConfigReq,
UpdateNetworkConfigRes,
} from './dto/network-config';
import { NetworkConfigGrpcClient } from './network-config-client.config';
@Injectable()
export class NetworkConfigService {
private networkConfigReadClient: NetworkConfig.ReadService.NetworkConfigReadService;
private networkConfigWriteClient: NetworkConfig.WriteService.NetworkConfigWriteService;
constructor(
@Inject(NetworkConfigGrpcClient.name)
private readonly grpcClient: ClientGrpc,
) {
this.networkConfigReadClient =
grpcClient.getService<NetworkConfig.ReadService.NetworkConfigReadService>(
NetworkConfig.ProtoServices.NetworkConfigReadService,
);
this.networkConfigWriteClient =
grpcClient.getService<NetworkConfig.WriteService.NetworkConfigWriteService>(
NetworkConfig.ProtoServices.NetworkConfigWriteService,
);
}
async findAllNetworkConfigs({
body,
info,
}): Promise<FindAllNetworkConfigsRes> {
const { search, page, size, filters } = body;
const { language, customer_id, customer_name, user_id } = info;
const meta = new Metadata();
meta.add('language', language);
meta.add('customer_id', customer_id);
meta.add('customer_name', customer_name);
meta.add('user_id', user_id);
return lastValueFrom(
this.networkConfigReadClient.NetworkConfigFindAll(
{ search, filters: JSON.stringify(filters), page, size },
meta,
),
);
}
async findNetworkConfigById(id: string): Promise<FindNetworkConfigByidRes> {
const { network_config } = await lastValueFrom(
this.networkConfigReadClient.NetworkConfigFindOneById({
id,
}),
);
return { network_config };
}
async findNetworkConfigByIdDetails(
id: string,
info?,
): Promise<FindNetworkConfigByidDetailsRes> {
const { customer_name, customer_id, language } = info;
const meta = new Metadata();
meta.add('customer_name', customer_name);
meta.add('customer_id', customer_id);
meta.add('language', language);
const { network_config } = await lastValueFrom(
this.networkConfigReadClient.NetworkConfigFindOneDetailsById(
{ id },
meta,
),
);
Object.assign(network_config, {
properties: JSON.parse(network_config.properties),
});
return { network_config };
}
async createNetworkConfig(
body: CreateNetworkConfigReq,
file: Express.Multer.File,
{ customer_name, customer_id, user_id },
): Promise<CreateNetworkConfigRes> {
const { description, name, network_type_id, network_type_name } = body;
const type = 'vpn';
const plugin = 'openvpn';
const OVPN_FILE_STRING = Buffer.from(file.buffer).toString();
const meta = new Metadata();
meta.add('customer_id', customer_id);
meta.add('customer_name', customer_name);
meta.add('user_id', user_id);
const { network_config } = await lastValueFrom(
this.networkConfigWriteClient.NetworkConfigCreate(
{
description,
name,
properties: JSON.stringify({
OVPN_FILE: OVPN_FILE_STRING,
plugin,
}),
type,
network_type_id,
network_type_name,
plugin,
},
meta,
),
);
return { network_config };
}
async updateNetworkConfig(
body: UpdateNetworkConfigReq,
network_config_id: string,
): Promise<UpdateNetworkConfigRes> {
const { description, name } = body;
const { network_config } = await lastValueFrom(
this.networkConfigWriteClient.NetworkConfigUpdate({
id: network_config_id,
description,
name,
}),
);
return { network_config };
}
async deleteNetworkConfig(
network_config_id: string,
{ customer_name, customer_id },
): Promise<DeleteNetworkConfigRes> {
const meta = new Metadata();
meta.add('customer_id', customer_id);
meta.add('customer_name', customer_name);
return lastValueFrom(
this.networkConfigWriteClient.NetworkConfigDelete(
{ id: network_config_id },
meta,
),
);
}
}
+85 -36
View File
@@ -1,27 +1,29 @@
import { Controller, Get, Redirect, Req, UseGuards } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { AuthGuard } from '@nestjs/passport';
import { InputsClientService } from 'src/clients/inputs/client.service';
import { InputsService } from '../inputs/inputs.service';
import { Request } from 'express';
import { ConnectionClientService } from '../connection/client.service';
import jwt from 'jsonwebtoken';
import DadosferaLogger from '@dadosfera/dadosfera-logs/dist';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
@ApiTags('oauth')
@Controller('oauth')
export class OauthController {
inputService: InputsService;
frontendRedirectUri = '';
constructor(private inputsClientService: InputsClientService) {
this.inputService = new InputsService(this.inputsClientService);
redirectUrl: string;
constructor(
private connectionService: ConnectionClientService,
private logger: DadosferaLogger,
) {
switch (process.env.ENV) {
case 'dev':
case 'stg':
this.frontendRedirectUri = `https://app.${process.env.ENV}.dadosfera.ai/coletix/auth-callback`;
this.redirectUrl = `https://app.${process.env.ENV}.dadosfera.ai/collect/auth-callback`;
break;
case 'prd':
this.frontendRedirectUri = `https://app.dadosfera.ai/coletix/auth-callback`;
this.redirectUrl = `https://app.dadosfera.ai/collect/auth-callback`;
break;
default:
this.frontendRedirectUri = `http://localhost:4200/coletix/auth-callback`;
this.redirectUrl = `http://localhost:4200/collect/auth-callback`;
}
}
@Get('hubspot')
@@ -30,11 +32,12 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('hubspot/callback')
@UseGuards(AuthGuard('hubspot'))
@Redirect()
async oauthHubspotCallback(@Req() req) {
return await this.callback(req, 'hubspot');
return await this.callback(req);
}
@Get('googleads')
@@ -43,11 +46,12 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('googleads/callback')
@UseGuards(AuthGuard('google'))
@Redirect()
async oauthGoogleAdsCallback(@Req() req) {
return await this.callback(req, 'googleads');
return await this.callback(req);
}
@Get('google-sheets')
@@ -56,11 +60,12 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('google-sheets/callback')
@UseGuards(AuthGuard('google'))
@Redirect()
async oauthGoogleSheetsCallback(@Req() req) {
return await this.callback(req, 'google-sheets');
return await this.callback(req);
}
@Get('facebook')
@@ -69,11 +74,12 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('facebook/callback')
@UseGuards(AuthGuard('facebook'))
@Redirect()
async oauthFacebookCallback(@Req() req) {
return await this.callback(req, 'facebook');
return await this.callback(req);
}
@Get('mailchimp')
@@ -82,11 +88,12 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('mailchimp/callback')
@UseGuards(AuthGuard('mailchimp'))
@Redirect()
async oauthMailchimpCallback(@Req() req) {
return await this.callback(req, 'mailchimp');
return await this.callback(req);
}
@Get('salesforce')
@@ -95,36 +102,78 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('salesforce/callback')
@UseGuards(AuthGuard('salesforce'))
@Redirect()
async oauthSalesforceCallback(@Req() req) {
return await this.callback(req, 'salesforce');
return await this.callback(req);
}
async callback(@Req() req: Request, plugin) {
const { error, state: customer_id } = req.query;
async callback(@Req() req) {
const { error, state } = req.query;
const url = new URL(this.redirectUrl);
if (error)
return {
url: `${this.frontendRedirectUri}?error=${error}`,
};
const request: any = req;
const { authInfo } = request;
if (error) {
this.logger.error(error);
url.searchParams.set('error', 'Erro ao autorizar');
url.searchParams.set(
'error_description',
`Credenciais inválidas. Erro: [${error}]`,
);
return { url: url.href };
}
if (!authInfo)
return {
url: `${this.frontendRedirectUri}?error=invalid_credentials`,
};
const { authInfo } = req;
if (!authInfo) {
this.logger.error('No authInfo', req);
url.searchParams.set('error', 'Erro ao autorizar');
url.searchParams.set('error_description', `Credenciais inválidas.`);
return { url: url.href };
}
const { accessToken: access_token, refreshToken: refresh_token } = authInfo;
const response = await this.inputService.create({
info: { customer_id, customer: 'customer', user_id: 'empty' },
plugin,
credentials: { access_token, refresh_token },
});
let connectionInfo: any;
try {
connectionInfo = jwt.verify(state as string, process.env.JWT_PRIVATE_KEY);
} catch (error) {
this.logger.error('Invalid state JWT', { state, req, error });
url.searchParams.set('error', 'Erro ao autorizar');
url.searchParams.set(
'error_description',
'Ocorreu um erro ao autorizá-lo. Por favor tente novamente',
);
return { url: url.href };
}
return { url: `${this.frontendRedirectUri}?input_id=${response.input.id}` };
connectionInfo.properties.access_token = access_token;
connectionInfo.properties.refresh_token = refresh_token;
const { customer_id, customer_name, user_id } = connectionInfo;
try {
const response = connectionInfo.id
? await this.connectionService.updateConnection(
connectionInfo.id,
connectionInfo,
PackTheMetadata({ customer_id, customer_name, user_id }),
)
: await this.connectionService.createConnection(
connectionInfo,
PackTheMetadata({ customer_id, customer_name, user_id }),
);
url.searchParams.set('connection_id', response.connection.id);
return { url: url.href };
} catch (error) {
this.logger.error(error);
url.searchParams.set('error', 'Erro ao autorizar');
url.searchParams.set(
'error_description',
'Ocorreu um erro interno ao autorizá-lo. Por favor tente novamente mais tarde',
);
return { url: url.href };
}
}
}
+29
View File
@@ -0,0 +1,29 @@
import { Module } from '@nestjs/common';
import { OauthController } from './oauth.controller';
import { getOauthSecrets } from '../../utils/OauthSecrets';
import { HubspotStrategy } from './passport-strategies/hubspot-strategy';
import { FacebookStrategy } from './passport-strategies/facebook-strategy';
import { GoogleStrategy } from './passport-strategies/google-strategy';
import { MailChimpStrategy } from './passport-strategies/mailchimp-strategy';
import { SalesforceStrategy } from './passport-strategies/salesforce-strategy';
import { ConnectionModule } from '../connection/connection.module';
import { OauthService } from './oauth.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
@Module({
providers: [
DadosferaLogger,
{ provide: 'OAUTH_SECRETS', useValue: getOauthSecrets() },
HubspotStrategy,
FacebookStrategy,
GoogleStrategy,
MailChimpStrategy,
SalesforceStrategy,
OauthService,
],
controllers: [OauthController],
imports: [ConnectionModule],
})
export class OauthModule {}
+53
View File
@@ -0,0 +1,53 @@
import { Injectable } from '@nestjs/common';
import { ConnectionDto } from '../connection/dtos/connection';
import jwt from 'jsonwebtoken';
import { PartialType } from '@nestjs/swagger';
class PartialConnection extends PartialType(ConnectionDto) {}
@Injectable()
export class OauthService {
generateStateFromRequest(prevState, req, type): string {
if (req.route.path.includes('callback')) {
return prevState;
}
const plugin: string = req.route.path.replace('/oauth/', '');
const { query } = req;
const {
name,
description,
connector_id,
connector_name,
image_url,
user_id,
customer_id,
customer_name,
id,
} = query;
const properties = { plugin, credentials_type: 'oauth' };
for (const key in query) {
if (key.includes('properties.')) {
const propertyKey = key.split('.')[1];
properties[propertyKey] = query[key];
}
}
const newConnection: PartialConnection = {
name,
description,
connector_id,
connector_name,
image_url,
user_id,
customer_id,
customer_name,
type,
properties,
plugin,
id,
};
return jwt.sign({ ...newConnection }, process.env.JWT_PRIVATE_KEY);
}
}
@@ -10,12 +10,14 @@ import { OauthSecrets } from 'src/utils/OauthSecrets';
import { Request } from 'express';
import { ParamsDictionary } from 'express-serve-static-core';
import { ParsedQs } from 'qs';
import { OauthService } from '../oauth.service';
@Injectable()
export class FacebookStrategy extends PassportStrategy(Strategy) {
constructor(
@Inject('OAUTH_SECRETS')
private readonly oauthSecrets: OauthSecrets,
private oauthService: OauthService,
) {
const options: StrategyOption = {
clientID: oauthSecrets.facebook.client_id,
@@ -37,9 +39,13 @@ export class FacebookStrategy extends PassportStrategy(Strategy) {
req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>,
options?: any,
): void {
const { customer_id } = req.query;
options.state = customer_id;
options.scope = 'ads_read ads_management';
options.state = this.oauthService.generateStateFromRequest(
options.state,
req,
'application',
);
options.scope =
'pages_show_list ads_read pages_read_engagement ads_management';
super.authenticate(req, options);
}
}
@@ -2,6 +2,7 @@ import { Profile, Strategy, StrategyOptions } from 'passport-google-oauth20';
import { PassportStrategy } from '@nestjs/passport';
import { Inject, Injectable } from '@nestjs/common';
import { OauthSecrets } from 'src/utils/OauthSecrets';
import { OauthService } from '../oauth.service';
@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
@@ -9,6 +10,7 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor(
@Inject('OAUTH_SECRETS')
private readonly oauthSecrets: OauthSecrets,
private oauthService: OauthService,
) {
const options: StrategyOptions = {
clientID: oauthSecrets.google.client_id,
@@ -27,7 +29,6 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
super(options, verify);
switch (process.env.ENV) {
case 'dev':
case 'stg':
this.redirect_uri = `https://maestro.${process.env.ENV}.dadosfera.ai/oauth`;
break;
@@ -44,23 +45,30 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
const plugin: string = req.route.path
.replace('/oauth/', '')
.replace('/callback', '');
const { customer_id } = req.query;
options.scope = ['https://www.googleapis.com/auth/userinfo.email'];
options.callbackURL = `${this.redirect_uri}/${plugin}/callback`;
switch (plugin) {
case 'googleads':
options.state = this.oauthService.generateStateFromRequest(
options.state,
req,
'application',
);
options.scope.push('https://www.googleapis.com/auth/adwords');
break;
case 'google-sheets':
options.state = this.oauthService.generateStateFromRequest(
options.state,
req,
'file',
);
options.scope.push(
'https://www.googleapis.com/auth/drive.metadata.readonly',
'https://www.googleapis.com/auth/spreadsheets.readonly',
);
break;
}
options.state = customer_id;
options.accessType = 'offline';
options.prompt = 'consent';
super.authenticate(req, options);
@@ -2,12 +2,14 @@ import { Strategy } from 'passport-hubspot-oauth2';
import { PassportStrategy } from '@nestjs/passport';
import { Inject, Injectable } from '@nestjs/common';
import { OauthSecrets } from 'src/utils/OauthSecrets';
import { OauthService } from '../oauth.service';
@Injectable()
export class HubspotStrategy extends PassportStrategy(Strategy) {
constructor(
@Inject('OAUTH_SECRETS')
private readonly oauthSecrets: OauthSecrets,
private oauthService: OauthService,
) {
super(
{
@@ -25,8 +27,11 @@ export class HubspotStrategy extends PassportStrategy(Strategy) {
}
authenticate(req, options) {
const { customer_id } = req.query;
options.state = customer_id;
options.state = this.oauthService.generateStateFromRequest(
options.state,
req,
'application',
);
super.authenticate(req, options);
}
}
@@ -2,12 +2,14 @@ import { Strategy } from 'passport-mailchimp';
import { PassportStrategy } from '@nestjs/passport';
import { Inject, Injectable, Req } from '@nestjs/common';
import { OauthSecrets } from 'src/utils/OauthSecrets';
import { OauthService } from '../oauth.service';
@Injectable()
export class MailChimpStrategy extends PassportStrategy(Strategy, 'mailchimp') {
constructor(
@Inject('OAUTH_SECRETS')
private readonly oauthSecrets: OauthSecrets,
private oauthService: OauthService,
) {
const options = {
clientID: oauthSecrets.mailchimp.client_id,
@@ -28,8 +30,12 @@ export class MailChimpStrategy extends PassportStrategy(Strategy, 'mailchimp') {
}
authenticate(@Req() req, options) {
const { customer_id } = req.query;
options.state = customer_id;
if (!req.route.path.includes('callback'))
options.state = this.oauthService.generateStateFromRequest(
options.state,
req,
'application',
);
super.authenticate(req, options);
}
}
@@ -2,6 +2,7 @@ import { Strategy } from 'passport-forcedotcom';
import { PassportStrategy } from '@nestjs/passport';
import { Inject, Injectable } from '@nestjs/common';
import { OauthSecrets } from 'src/utils/OauthSecrets';
import { OauthService } from '../oauth.service';
@Injectable()
export class SalesforceStrategy extends PassportStrategy(
@@ -11,6 +12,7 @@ export class SalesforceStrategy extends PassportStrategy(
constructor(
@Inject('OAUTH_SECRETS')
private readonly oauthSecrets: OauthSecrets,
private oauthService: OauthService,
) {
const options = {
clientID: oauthSecrets.salesforce.client_id,
@@ -40,8 +42,11 @@ export class SalesforceStrategy extends PassportStrategy(
}
authenticate(req, options) {
const { customer_id } = req.query;
options.state = customer_id;
options.state = this.oauthService.generateStateFromRequest(
options.state,
req,
'application',
);
super.authenticate(req, options);
}
}
-123
View File
@@ -1,123 +0,0 @@
import {
Body,
Controller,
Delete,
Get,
Inject,
Param,
Post,
Put,
} from '@nestjs/common';
import { Payload } from '@nestjs/microservices';
import { ApiTags } from '@nestjs/swagger';
import { OutputsClientService } from 'src/clients/outputs/client.service';
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
import { PERMISSIONS } from '../../authentication/permissions.enum';
import { OutputsService } from './outputs.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
@ApiTags('Outputs')
@Controller('outputs')
@AuthenticateCondition((req, user) => {
let action;
switch (req.method) {
case 'POST':
action = 'CREATE';
break;
case 'PUT':
action = 'UPDATE';
break;
default:
action = req.method;
}
return user.permissions.includes(
PERMISSIONS.OUTPUT.permissions[action].seqid,
);
})
export class OutputsController {
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private outputsClientService: OutputsClientService,
) {
this.logger = dadosferaLogger.logger;
}
@Post()
async create(@Body() createOutputDto) {
this.logger.info(process.env.DEV_URL + `/output - ON CREATE ROUTE`, {
user: createOutputDto.info.user_id,
customer: createOutputDto.info.customer,
});
const outputService = new OutputsService(this.outputsClientService);
const response = await outputService.create(createOutputDto);
return response;
}
@Get()
async findAll(@Body() data) {
this.logger.info(process.env.DEV_URL + `/output - ON Find All ROUTE`, {
user: data.info.user_id,
customer: data.info.customer,
});
const outputService = new OutputsService(this.outputsClientService);
const response = await outputService.findAll(data);
return response;
}
@Get('/:id')
async findOne(@Body() data, @Param() params) {
const { id } = params;
this.logger.info(
process.env.DEV_URL + `/output/${id} - ON Find One ROUTE`,
{
user: data.info.user_id,
customer: data.info.customer,
},
);
const outputService = new OutputsService(this.outputsClientService);
const response = await outputService.findOne({ id, ...data });
return response;
}
@Put(':id')
async update(@Payload() updateOutputDto, @Param() params) {
const { id } = params;
const { info } = updateOutputDto;
this.logger.info(process.env.DEV_URL + `/output/${id} - ON UPDATE ROUTE`, {
user: updateOutputDto.info.user_id,
customer: updateOutputDto.info.customer,
});
delete updateOutputDto.info;
const outputService = new OutputsService(this.outputsClientService);
const response = await outputService.update(id, updateOutputDto, info);
return response;
}
@Delete(':id')
async delete(@Body() data, @Param() params) {
const { id } = params;
this.logger.info(process.env.DEV_URL + `/output/${id} - ON DELETE ROUTE`, {
user: data.info.user_id,
customer: data.info.customer,
});
const outputService = new OutputsService(this.outputsClientService);
const response = await outputService.remove({ id, ...data });
return response;
}
}
-63
View File
@@ -1,63 +0,0 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { IIdRequest, Info } from 'src/clients/outputs/interfaces';
import { OutputsClientService } from 'src/clients/outputs/client.service';
@Injectable()
export class OutputsService {
constructor(private outputClient: OutputsClientService) {}
async create(createOutputDto) {
try {
const createOutputResponse = await this.outputClient.create(
createOutputDto,
);
return createOutputResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
async findOne(data: IIdRequest) {
try {
const findOneOutputResponse = await this.outputClient.findOne(data);
return findOneOutputResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
async findAll(data) {
try {
const findAllOutputResponse = await this.outputClient.findAll(data);
return findAllOutputResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
async update(id: string, data, info: Info) {
try {
const updateOutputResponse = await this.outputClient.update({
id,
info,
...data,
});
return updateOutputResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
async remove(data: IIdRequest) {
try {
const removeOutputResponse = await this.outputClient.remove(data);
return removeOutputResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
}
@@ -1,28 +1,21 @@
import { ClientsModule } from '@nestjs/microservices';
import { Test, TestingModule } from '@nestjs/testing';
import { DucClient } from 'src/clients/duc/client.config';
// import { DucClient } from 'src/clients/duc/client.config';
import { PermissionsController } from './permissions.controller';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { PermissionsService } from './permissions.service';
const ducClient = new DucClient();
// const client = new DucClient();
const logger = {
info: (...args) => args,
error: (...args) => args,
};
describe('PermissionsController', () => {
describe.skip('PermissionsController', () => {
let controller: PermissionsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ClientsModule.register([
{
name: 'DUC_PACKAGE',
...ducClient.config(),
},
]),
],
// imports: [ClientsModule.register([client.providerOptions])],
controllers: [PermissionsController],
providers: [
{
@@ -1,20 +1,18 @@
import { Controller, Get, Headers, Inject } from '@nestjs/common';
import { Controller, Get, Inject } from '@nestjs/common';
import { ApiHeader, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
Authenticated,
RequireAllPermissions,
} from 'src/authentication/authentication.decorator';
import {
PERMISSIONS,
PermissionUsages,
} from 'src/authentication/permissions.enum';
import { RequestUser, User } from 'src/authentication/user.decorator';
import { Authenticated } from 'src/decorators/authentication.decorator';
import { PermissionUsages } from 'src/authentication/permissions.enum';
import { RequestUser, User } from 'src/decorators/user.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
import { GetPublicPermissionsRes } from './dto/entities';
import { PermissionsService } from './permissions.service';
import { Language } from 'src/decorators/language.decorator';
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
@ApiInternalOnlyController()
@ApiTags('Permissions')
@Authenticated()
@Controller('permissions')
@ApiHeader({
name: 'dadosfera-lang',
@@ -22,7 +20,6 @@ import { PermissionsService } from './permissions.service';
required: false,
description: 'Defaults to pt-br',
})
@Authenticated()
export class PermissionsController {
logger: DadosferaLogger;
@@ -36,12 +33,12 @@ export class PermissionsController {
@Get()
@ApiOkResponse({ type: GetPublicPermissionsRes })
@RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN)
getPublicPermissions(
@User() user: RequestUser,
@Headers('dadosfera-lang') language,
@Language() language: LanguageEnum,
) {
this.logger.info('getPublicPermissions', { user });
const permissionGroups = this.permissionsService.getPermissionsGrouped(
PermissionUsages.PUBLIC,
language,
+3 -10
View File
@@ -4,20 +4,13 @@ import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { PermissionsService } from './permissions.service';
import { DucClient } from '../../clients/duc/client.config';
import { DucClient } from '../duc/client.config';
import { PermissionsController } from './permissions.controller';
const ducClient = new DucClient();
const client = new DucClient();
@Module({
imports: [
ClientsModule.register([
{
name: 'DUC_PACKAGE',
...ducClient.config(),
},
]),
],
imports: [ClientsModule.register([client.providerOptions])],
providers: [PermissionsService, DadosferaLogger],
controllers: [PermissionsController],
exports: [PermissionsService],
+14 -10
View File
@@ -12,7 +12,7 @@ import { PermissionsProtoService as PermissionsServiceInterface } from '@dadosfe
import { InjectPermissionsRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
PERMISSIONS,
PERMISSIONS_GROUPS,
PermissionUsages,
PermissionsObjectGroup,
PermissionsObjectPermissionTranslated,
@@ -20,6 +20,7 @@ import {
import ErrorBuilder from '../../utils/ErrorBuilder';
import { PermissionGroupPermission, PermissionGroup } from './dto/entities';
import { LanguageEnum } from 'src/utils/languages.enum';
import { DucClient } from '../duc/client.config';
@Injectable()
export class PermissionsService
@@ -31,7 +32,7 @@ export class PermissionsService
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject('DUC_PACKAGE') private readonly grpcClient: ClientGrpc,
@Inject(DucClient.name) private readonly grpcClient: ClientGrpc,
) {
this.logger = dadosferaLogger.logger;
}
@@ -43,11 +44,14 @@ export class PermissionsService
);
}
// internally used to send permissions to duc on microservice startup
async onApplicationBootstrap() {
// Do not sent permissions if running local but pointing to a remote DUC url
if (process.env.ENV == 'local' && !process.env.DUC_URL.includes('0.0.0.0'))
return;
this.logger.info('sending permissions to DUC...');
const permissions = Object.values(PERMISSIONS).flatMap((namespace) =>
const permissions = Object.values(PERMISSIONS_GROUPS).flatMap((namespace) =>
Object.values(namespace.permissions),
);
@@ -56,10 +60,10 @@ export class PermissionsService
}).catch((err: ErrorBuilder) => {
if (
err.code === 'No connection established' &&
process.env.LOCAL_ENV === 'true'
process.env.ENV === 'local'
) {
return this.logger.info(
"couldn't connect to DUC. suppresing in local env",
"couldn't connect to DUC. suppressing in local env",
);
}
@@ -79,8 +83,8 @@ export class PermissionsService
language: LanguageEnum = LanguageEnum.ptbr,
): PermissionGroup[] {
const groups: PermissionGroup[] = [];
for (const key in PERMISSIONS) {
const permissionGroup: PermissionsObjectGroup = PERMISSIONS[key];
for (const key in PERMISSIONS_GROUPS) {
const permissionGroup: PermissionsObjectGroup = PERMISSIONS_GROUPS[key];
const title = permissionGroup.title[language];
const permissions: PermissionGroupPermission[] = Object.values(
permissionGroup.permissions,
@@ -99,8 +103,8 @@ export class PermissionsService
}
getAllPermissions(language: LanguageEnum = LanguageEnum.ptbr) {
let permissions: PermissionsObjectPermissionTranslated[] = [];
for (const key in PERMISSIONS) {
const permissionGroup: PermissionsObjectGroup = PERMISSIONS[key];
for (const key in PERMISSIONS_GROUPS) {
const permissionGroup: PermissionsObjectGroup = PERMISSIONS_GROUPS[key];
const permissionGroupPermissions: PermissionsObjectPermissionTranslated[] =
Object.values(permissionGroup.permissions).map((p) => ({
...p,
+74
View File
@@ -0,0 +1,74 @@
import { ConflictException, Inject, OnModuleInit } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { PipelineServicesNames, PipelinesServiceInterface } from 'protospack';
import { lastValueFrom } from 'rxjs';
import { IIdRequest } from './interfaces';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { PipelinesClientConfiguration } from './pipelines-client';
export class PipelinesClientService implements OnModuleInit {
private pipelineService: PipelinesServiceInterface;
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@Inject(PipelinesClientConfiguration.name)
private readonly grpcClient: ClientGrpc,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.pipelineService =
this.grpcClient.getService<PipelinesServiceInterface>(
PipelineServicesNames.PipelineService,
);
}
async getPipelineStatus(data) {
this.logger.info('PipelinesClientService - GetPipelineStatus');
const statusPipelineResponse = await lastValueFrom(
this.pipelineService.getPipelineStatus(data),
)
.then((res) => {
const statusArray =
res.status?.sort((a, b) => {
if (a.id < b.id) {
return 1;
} else {
return -1;
}
}) || [];
return { status: statusArray };
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
this.logger.info('Done');
return statusPipelineResponse;
}
async runPipeline({ id, info }: IIdRequest) {
this.logger.info('PipelinesClientService - RunPipeline');
const statusPipelineResponse = await lastValueFrom(
this.pipelineService.triggerPipeline({ id, info }),
).catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
if (statusPipelineResponse.status == false) {
throw new ConflictException(
'This pipeline is not ready yet to execute, Try again later!',
);
}
this.logger.info('Done');
return statusPipelineResponse;
}
}
@@ -1,3 +1,5 @@
import { Info } from 'protospack/dist/lib/interfaces';
export interface ICreatePipelineDto {
input: IdRequest;
transformations: IdRequest[];
@@ -8,11 +10,6 @@ export interface ICreatePipelineDto {
info: Info;
}
export interface Info {
customer_id: sting;
user_id: string;
}
export interface IdRequest {
id: string;
}
+31
View File
@@ -0,0 +1,31 @@
import {
ClientsProviderAsyncOptions,
GrpcOptions,
Transport,
} from '@nestjs/microservices';
import { PipelinePackages, PipelineProtoFilePath } from 'protospack';
import { credentials } from '@grpc/grpc-js';
const isLocalConnection = !!process.env.PIFACTORY_URL?.includes('0.0.0.0');
export class PipelinesClientConfiguration {
public name = 'PipelinesClientConfiguration';
private config: GrpcOptions = {
transport: Transport.GRPC,
options: {
url: process.env.PIFACTORY_URL,
package: PipelinePackages,
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: PipelineProtoFilePath,
loader: {
keepCase: true,
enums: String,
defaults: false,
},
},
};
providerOptions: ClientsProviderAsyncOptions = {
name: this.name,
...this.config,
};
}
+23 -92
View File
@@ -1,22 +1,18 @@
import { Body, Controller, Get, Inject, Param, Post } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import {
Body,
Controller,
Delete,
Get,
Inject,
Param,
Post,
Put,
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { PipelinesClientService } from 'src/clients/pipelines/client.service';
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
import { PERMISSIONS } from '../../authentication/permissions.enum';
AuthenticateCondition,
Authenticated,
} from 'src/decorators/authentication.decorator';
import { PERMISSIONS_GROUPS } from '../../authentication/permissions.enum';
import { PipelinesService } from './pipelines.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
@ApiInternalOnlyController()
@ApiTags('Pipelines')
@Controller('pipelines')
@Authenticated()
@AuthenticateCondition((req, user) => {
let action;
@@ -34,7 +30,7 @@ import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
}
return user.permissions.includes(
PERMISSIONS.PIPELINE.permissions[action].seqid,
PERMISSIONS_GROUPS.PIPELINE.permissions[action].seqid,
);
})
export class PipelinesController {
@@ -42,12 +38,17 @@ export class PipelinesController {
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private pipelinesClientService: PipelinesClientService,
private pipelineService: PipelinesService,
) {
this.logger = dadosferaLogger.logger;
}
@Post('start/:id')
@ApiOperation({
deprecated: true,
description:
'This method is deprecated. Please use route /pipelinesV2/start/:id instead',
})
async activate(@Param() params, @Body() body) {
const { id } = params;
const { info } = body;
@@ -60,13 +61,17 @@ export class PipelinesController {
},
);
const pipelineService = new PipelinesService(this.pipelinesClientService);
const response = await pipelineService.runPipeline({ id, info });
const response = await this.pipelineService.runPipeline({ id, info });
return response;
}
@Get(':id/status')
@ApiOperation({
deprecated: true,
description:
'This method is deprecated. Please use route /pipelinesV2/:id/status instead',
})
async getPipelineStatus(@Body() body, @Param() params) {
const { id } = params;
body.id = id;
@@ -79,81 +84,7 @@ export class PipelinesController {
},
);
const pipelineService = new PipelinesService(this.pipelinesClientService);
const response = await pipelineService.getPipelineStatus(body);
return response;
}
@Get(':id/:details')
async getPipelineLogs(@Param() params) {
const { id, details } = params;
this.logger.info(
process.env.DEV_URL + `/pipeline/${id} - ON GET PIPELINE LOGS ROUTE`,
{},
);
const pipelineService = new PipelinesService(this.pipelinesClientService);
const response = await pipelineService.getPipelineLogsMessages(id, details);
return response;
}
@Post()
async create(@Body() createPipelineDto) {
this.logger.info(process.env.DEV_URL + `/pipelines ON CREATE ROUTE`);
const pipelineService = new PipelinesService(this.pipelinesClientService);
const response = await pipelineService.create(createPipelineDto);
return response;
}
@Get()
async findAll(@Body() data) {
this.logger.info(process.env.DEV_URL + `/pipeline ON Find All ROUTE`, {
user: data.info.user_id,
customer: data.info.customer,
});
const pipelineService = new PipelinesService(this.pipelinesClientService);
const response = await pipelineService.findAll(data);
return response;
}
@Get('/:id')
async findOne(@Body() data, @Param() params) {
const { id } = params;
this.logger.info(process.env.DEV_URL + `/pipeline/${id} ON Find One ROUTE`);
const pipelineService = new PipelinesService(this.pipelinesClientService);
const response = await pipelineService.findOne({ id, ...data });
return response;
}
@Put(':id')
async update(@Body() updatePipelineDto, @Param() params) {
const { id } = params;
const { info } = updatePipelineDto;
delete updatePipelineDto.info;
this.logger.info(process.env.DEV_URL + `/pipeline/${id} ON UPDATE ROUTE`);
const pipelineService = new PipelinesService(this.pipelinesClientService);
const response = await pipelineService.update(id, updatePipelineDto, info);
return response;
}
@Delete(':id')
async delete(@Body() data, @Param() params) {
const { id } = params;
this.logger.info(process.env.DEV_URL + `/pipeline/${id} ON DELETE ROUTE`);
const pipelineService = new PipelinesService(this.pipelinesClientService);
const response = await pipelineService.remove({ id, ...data });
const response = await this.pipelineService.getPipelineStatus(body);
return response;
}
+19
View File
@@ -0,0 +1,19 @@
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { PipelinesController } from './pipelines.controller';
import { PipelinesService } from './pipelines.service';
import { PipelinesClientConfiguration } from './pipelines-client';
import { PipelinesClientService } from './client.service';
const client = new PipelinesClientConfiguration();
@Module({
imports: [ClientsModule.register([client.providerOptions])],
controllers: [PipelinesController],
providers: [PipelinesService, PipelinesClientService, DadosferaLogger],
exports: [PipelinesService],
})
export class PipelinesModule {}
+2 -87
View File
@@ -1,97 +1,12 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { DecodeGrpcStruct } from 'protospack';
import { PipelinesClientService } from 'src/clients/pipelines/client.service';
import { IIdRequest, Info } from 'src/clients/pipelines/interfaces';
import { PipelinesClientService } from './client.service';
import { IIdRequest } from './interfaces';
import { objectCamelToSnake } from 'src/utils/CaseConverter';
@Injectable()
export class PipelinesService {
constructor(private pipelineClient: PipelinesClientService) {}
adjustPayload(payload) {
if (payload.input?.input_generic) {
payload.input = DecodeGrpcStruct(payload.input?.input_generic);
} else {
payload.input = payload.input?.input_s3 || payload.input?.input_jdbc;
}
}
async create(createPipelineDto) {
const createPipelineResponse = await this.pipelineClient.create(
createPipelineDto,
);
const pipeline = objectCamelToSnake(createPipelineResponse);
this.adjustPayload(pipeline.pipeline);
return pipeline;
}
async findOne(data: IIdRequest) {
try {
const findOnePipelineResponse = await this.pipelineClient.findOne(data);
const pipeline = objectCamelToSnake(findOnePipelineResponse);
this.adjustPayload(pipeline.pipeline);
return pipeline;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
async findAll(data) {
try {
const findAllPipelineResponse = await this.pipelineClient.findAll(data);
const pipelines = objectCamelToSnake(findAllPipelineResponse);
pipelines.forEach((pipeline) => {
this.adjustPayload(pipeline);
});
return { pipelines };
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
async update(id: string, data, info: Info) {
try {
const updatePipelineResponse = await this.pipelineClient.update({
id,
info,
...data,
});
const pipeline = objectCamelToSnake(updatePipelineResponse);
this.adjustPayload(pipeline);
return pipeline;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
async remove(data: IIdRequest) {
try {
const removePipelineResponse = await this.pipelineClient.remove(data);
return removePipelineResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
async getPipelineLogsMessages(id: string, details: string) {
try {
const pipelineLogsResponse =
await this.pipelineClient.getPipelineLogsMessages({ id, details });
return objectCamelToSnake(pipelineLogsResponse);
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
async getPipelineStatus(data: IIdRequest) {
try {
const pipelineStatusResponse =
+107
View File
@@ -0,0 +1,107 @@
import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger';
import { Info } from 'protospack/dist/lib/interfaces';
export class IPipelineV2 {
@ApiProperty()
id: string;
@ApiProperty()
name: string;
@ApiProperty()
description: string;
@ApiProperty()
connection_id: string;
@ApiProperty()
cron: string;
@ApiPropertyOptional()
input_id?: string;
@ApiPropertyOptional()
transformations_ids?: string[];
@ApiPropertyOptional()
tags?: string[];
@ApiPropertyOptional()
properties?: any;
@ApiProperty()
connector_name: string;
@ApiProperty()
connector_plugin: string;
@ApiProperty()
connector_version: string;
@ApiProperty()
image_url: string;
@ApiProperty()
created_at: string;
@ApiProperty()
updated_at: string;
}
export class ICreatePipelineV2Req extends OmitType(IPipelineV2, [
'id',
'created_at',
'updated_at',
]) {}
export interface IdRequest {
id: string;
}
export interface IIdRequest {
id: string;
info: Info;
}
export interface IUpdatePipelineRequest {
input: IdRequest;
transformations: IdRequest[];
output: IdRequest;
tags: string[];
name: string;
description: string;
id: string;
info: Info;
}
export interface IGetPipelineLogsRequest {
id: string;
details: string;
}
export class IInitUploadCSVFile {
@ApiProperty()
file_name: string;
@ApiProperty()
parts: number;
}
export class ICompleteUploadCSVFile {
@ApiProperty()
upload_id: string;
@ApiProperty()
file_name: string;
@ApiProperty()
parts: { ETag: string; PartNumber: number }[];
}
export class ICreatePipelineCSVFile {
@ApiProperty()
name: string;
@ApiProperty()
file_name: string;
@ApiProperty()
description: string;
@ApiProperty()
encoding: string;
@ApiProperty()
sep: string;
@ApiProperty()
header: boolean;
}
@@ -0,0 +1,35 @@
import {
ClientsProviderAsyncOptions,
GrpcOptions,
Transport,
} from '@nestjs/microservices';
import {
ProtoPackages,
ProtoPaths,
} from '@dadosfera/protospack-v2/dist/lib/PipelineV2';
import { credentials } from '@grpc/grpc-js';
const isLocalConnection = !!process.env.PIFACTORY_URL?.includes('0.0.0.0');
export class PipelinesClientConfiguration {
public name = 'PipelinesClientConfiguration';
private config: GrpcOptions = {
transport: Transport.GRPC,
options: {
url: process.env.PIFACTORY_URL,
package: [ProtoPackages.ReadPackage, ProtoPackages.WritePackage],
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [ProtoPaths.ReadFilePath, ProtoPaths.WriteFilePath],
loader: {
keepCase: true,
enums: String,
objects: true,
arrays: true,
},
},
};
providerOptions: ClientsProviderAsyncOptions = {
name: this.name,
...this.config,
};
}
@@ -0,0 +1,427 @@
import {
Body,
Controller,
Delete,
Get,
Inject,
Param,
Post,
Put,
Query,
UseFilters,
HttpCode,
HttpStatus,
Patch,
HttpException,
BadRequestException,
} from '@nestjs/common';
import {
ApiCreatedResponse,
ApiHeaders,
ApiNoContentResponse,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import {
AuthenticateCondition,
RequireAllPermissions,
} from 'src/decorators/authentication.decorator';
import { PERMISSIONS_GROUPS } from '../../authentication/permissions.enum';
import { PipelinesService } from './pipelines.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { Messages } from '@dadosfera/protospack-v2/dist/lib/PipelineV2';
import { RequestUser, User } from 'src/decorators/user.decorator';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { PipelinesService as OldPipelineService } from 'src/modules/pipelines/pipelines.service';
import {
ICompleteUploadCSVFile,
ICreatePipelineCSVFile,
ICreatePipelineV2Req,
IPipelineV2,
IInitUploadCSVFile,
} from './interfaces';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { LanguageEnum } from 'src/utils/languages.enum';
import { Language } from 'src/decorators/language.decorator';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
@ApiTags('PipelinesV2')
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
@UseFilters(new GrpcToHttpExceptionFilter())
@Controller('pipelinesV2')
@AuthenticateCondition((req, user) => {
let action;
switch (req.method) {
case 'POST':
action = 'CREATE';
break;
case 'PUT':
action = 'UPDATE';
break;
case 'PATCH':
action = 'UPDATE';
break;
default:
action = req.method;
}
return user.permissions.includes(
PERMISSIONS_GROUPS.PIPELINE.permissions[action].seqid,
);
})
export class PipelinesController {
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private pipelinesClientService: PipelinesService,
private oldPipelinesService: OldPipelineService,
) {
this.logger = dadosferaLogger.logger;
}
@Post()
@ApiCreatedResponse({ type: IPipelineV2 })
async create(
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Body() createPipelineDto: ICreatePipelineV2Req,
) {
this.logger.info('PipelinesController - create', { user });
const { customer_id, customer_name, username, user_id } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
username,
user_id,
language,
});
const response = await this.pipelinesClientService.create(
createPipelineDto,
metadata,
);
return response;
}
@Get()
async findAll(
@User() user: RequestUser,
@Language() language: LanguageEnum,
@Query() data,
) {
this.logger.info('PipelinesController - findAll', { user });
const { customer_id, customer_name, user_id, username } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
language,
});
const response = await this.pipelinesClientService.findAll(data, metadata);
return {
pipelines: [...response.pipelines],
};
}
@Get('/download-logs')
async downloadLogs(
@User() user: RequestUser,
@Language() language: LanguageEnum,
@Query('pipeline_run_id') pipeline_run_id: string,
) {
this.logger.info('PipelinesController - downloadLogs', { user });
if (!pipeline_run_id)
throw new BadRequestException('Missing pipeline_run_id');
const { customer_id, customer_name, user_id, username } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
language,
});
const url = await this.pipelinesClientService.downloadLogs(
pipeline_run_id,
metadata,
);
return {
url,
};
}
@Get(':id/config')
async getPipelineproperties(
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Param('id') id,
) {
this.logger.info('PipelinesController - getPipelineproperties', { user });
const metadata = PackTheMetadata({ ...user, language: language });
return this.pipelinesClientService.findOneProperties(id, metadata);
}
@Get(':id/objects')
async getPipelineObjects(
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Param('id') id,
) {
this.logger.info('PipelinesController - getPipelineObjects', { user });
const metadata = PackTheMetadata({ ...user, language: language });
return this.pipelinesClientService.findOneObjects(id, metadata);
}
@Get(':id/status')
async getPipelineStatus(@Body() body, @Param() params) {
const { id } = params;
body.id = id;
this.logger.info(
process.env.DEV_URL + `/pipeline/${id} - ON GET PIPELINE STATUS ROUTE`,
{
user: body.info.user_id,
customer: body.info.customer,
},
);
const response = await this.oldPipelinesService.getPipelineStatus(body);
return response;
}
@Get('/:id')
async findOne(
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Param('id') id,
): Promise<Messages.PipelineV2FindOneResponse> {
this.logger.info('PipelinesController - findOne', { user });
const { customer_id, customer_name, user_id, username } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
language,
});
const result = await this.pipelinesClientService
.findOne({ id }, metadata)
.then((res) => {
//{pipeline:{tables: {tables: [], input_id: ''}}}
let tables = JSON.parse(res.pipeline.config.tables);
if (tables?.tables) tables = tables.tables;
Object.assign(res.pipeline, {
transformations: res.pipeline.transformations
? JSON.parse(res.pipeline.transformations)
: [],
config: {
cron: res.pipeline.config.cron,
tables,
},
properties: res.pipeline.properties
? JSON.parse(res.pipeline.properties)
: {},
});
return res;
});
return result;
}
@Patch('/:id')
async update(
@Language() language: LanguageEnum,
@Body() updatePipelineDto,
@Param('id') id,
@User() user: RequestUser,
) {
this.logger.info('PipelinesController - update', { user });
const { info } = updatePipelineDto;
delete updatePipelineDto.info;
const { customer_id, customer_name, user_id, username } = user;
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
language,
});
const response = await this.pipelinesClientService.update(
{
...updatePipelineDto,
info,
id,
},
metadata,
);
this.logger.info('PipelinesController - update: OK', { user });
return response;
}
@ApiInternalOnlyEndpoint()
@Put('/:id')
@ApiOperation({
deprecated: true,
description: 'This method is deprecated. Please use PATCH instead',
})
async updateDeprecated(
@Language() language: LanguageEnum,
@Body() updatePipelineDto,
@Param('id') id,
@User() user: RequestUser,
) {
this.logger.info('PipelinesController - put', { user });
const response = await this.update(language, updatePipelineDto, id, user);
this.logger.info('PipelinesController - put: OK', { user });
return response;
}
@Delete(':id')
@ApiNoContentResponse()
@HttpCode(HttpStatus.NO_CONTENT)
async delete(@Param() params, @User() user: RequestUser) {
this.logger.info('PipelinesController - delete', { user });
const { id } = params;
const metadata = PackTheMetadata({
customer_id: user.customer_id,
customer_name: user.customer_name,
user_id: user.user_id,
});
await this.pipelinesClientService.remove({ id, metadata, user });
this.logger.info('PipelinesController - delete: OK');
}
@ApiInternalOnlyEndpoint()
@Post('/init-upload')
@RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.CREATE)
async initUploadFile(
@User() user: RequestUser,
@Body() body: IInitUploadCSVFile,
) {
this.logger.info('/upload - Init Upload File Route');
const metadata = PackTheMetadata({ ...user });
const { file_name, parts } = body;
const { urls, upload_id } =
await this.pipelinesClientService.initUploadFile(
{
name: file_name,
parts: String(parts),
},
metadata,
);
if (!urls) {
this.logger.info('pipeline/upload - Failed File pipeline');
throw new HttpException(
'Upload failed, try again in a few minutes, if the problem persists, contact support.',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
return { urls: JSON.parse(urls), upload_id };
}
@ApiInternalOnlyEndpoint()
@Post('/complete-upload')
@RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.CREATE)
async completeUploadFile(
@User() user: RequestUser,
@Body() body: ICompleteUploadCSVFile,
) {
this.logger.info('/upload - Complete Upload File Route');
const metadata = PackTheMetadata({ ...user });
const { upload_id, parts, file_name } = body;
return await this.pipelinesClientService.completeUploadFile(
{ upload_id, parts, file_name },
metadata,
);
}
@ApiInternalOnlyEndpoint()
@Post('/file')
@RequireAllPermissions(PERMISSIONS_GROUPS.PIPELINE.permissions.CREATE)
async uploadedFile(
@User() user: RequestUser,
@Body() body: ICreatePipelineCSVFile,
) {
this.logger.info('/upload - Upload Connector Route');
const metadata = PackTheMetadata({ ...user });
const { name, sep, header, encoding, description, file_name } = body;
const source_prefix = `${user.customer_name}/${file_name}`;
const upload_pipeline = {
connection_id: process.env.UPLOAD_FILE_AGENT_CONNECTION,
connector_name: 'Amazon S3',
connector_plugin: 'aws_s3',
connector_version: '1.0.0',
image_url: 'https://assets.dadosfera.ai/images/connectors/csv.svg',
name,
description,
transformations_ids: [],
tags: [],
cron: '@once',
config: { cron: '@once', tables: [] },
properties: {
engine: 'csv',
source_bucket: process.env.BUCKET_CUSTOMER_CSV_ASSETS,
source_prefix,
file_format_params: { sep, encoding, header: Boolean(header) },
is_a_upload_csv: true,
},
input_id: undefined,
};
const pipeline = await this.pipelinesClientService.create(
upload_pipeline,
metadata,
);
return pipeline;
}
@ApiInternalOnlyEndpoint()
@Post('start/:id')
async activate(@Param() params, @Body() body) {
const { id } = params;
const { info } = body;
this.logger.info(
process.env.DEV_URL + `/pipeline/start/${id} - ON START PIPELINE ROUTE`,
{
user: body.info.user_id,
customer: body.info.customer,
},
);
const response = await this.oldPipelinesService.runPipeline({ id, info });
return response;
}
}
@@ -0,0 +1,29 @@
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { PipelinesController } from './pipelines.controller';
import { PipelinesService } from './pipelines.service';
import { PipelinesClientConfiguration } from './pipelines-client';
import { PipelinesModule as OldPipelineModule } from 'src/modules/pipelines/pipelines.module';
import { ConnectorModule } from '../connector/connector.module';
import { InputsModule } from '../inputs/inputs.module';
import { TransformationsModule } from '../transformations/transformations.module';
const client = new PipelinesClientConfiguration();
@Module({
imports: [
ClientsModule.register([client.providerOptions]),
OldPipelineModule,
ConnectorModule,
InputsModule,
TransformationsModule,
],
controllers: [PipelinesController],
providers: [PipelinesService, DadosferaLogger],
exports: [PipelinesService],
})
export class PipelinesV2Module {}

Some files were not shown because too many files have changed in this diff Show More