Compare commits
	
		
			408 Commits
		
	
	
		
			AUT-735
			...
			hubspot-up
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | a171623d4e | ||
|   | e4292815cd | ||
|   | ab37250d5d | ||
|   | e5be8d3ba7 | ||
|   | 96a421fa22 | ||
|   | 12f72401b1 | ||
|   | 7391a9eddc | ||
|   | 30dee27f72 | ||
|   | 51a9939034 | ||
|   | e03c6e0ca4 | ||
|   | bece5c6488 | ||
|   | d49bb4c52d | ||
|   | 73d0eec30c | ||
|   | 5c756b16ca | ||
|   | f482c2422c | ||
|   | 2e564c863f | ||
|   | d9917a81bb | ||
|   | 61dc431f92 | ||
|   | 7d2fb8d9d7 | ||
|   | 608b79b66f | ||
|   | 009754c18b | ||
|   | 5df07c289e | ||
|   | a36d10870b | ||
|   | b549ba3e39 | ||
|   | 897c96361f | ||
|   | e7693d8aa6 | ||
|   | 1fe755f836 | ||
|   | ea1a63f7dd | ||
|   | 85134722a5 | ||
|   | 5c9d3ed134 | ||
|   | 17fb935ea0 | ||
|   | 196642a1cf | ||
|   | 009cf63d8c | ||
|   | da399aacd6 | ||
|   | 3632ee77e5 | ||
|   | 2901f337cc | ||
|   | f0bd2f335b | ||
|   | acdd026448 | ||
|   | fb0a328ab0 | ||
|   | d2a7889fc9 | ||
|   | 88c50e014d | ||
|   | f0ef12f904 | ||
|   | 1827f5413f | ||
|   | 0609f30e25 | ||
|   | d4e4d95b6d | ||
|   | d74af4931e | ||
|   | bee043d10d | ||
|   | a65e48b98a | ||
|   | ee26b54d54 | ||
|   | 855ec53dc2 | ||
|   | 3e3e48110d | ||
|   | fc04a357c8 | ||
|   | c8147370de | ||
|   | 999426be89 | ||
|   | 91458f91ef | ||
|   | 4b9ed29cc0 | ||
|   | e3bcb673fb | ||
|   | bf4776ca4f | ||
|   | 9f7f30a92a | ||
|   | 5c29fff55e | ||
|   | a0160c2573 | ||
|   | 87d3ca287d | ||
|   | 526e093689 | ||
|   | 0930c9d8d6 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | ec680a713d | ||
|   | 583f90d1e9 | ||
|   | 8ba95381bc | ||
|   | ec6d634b99 | ||
|   | bc082acbe7 | ||
|   | e474ba02cb | ||
|   | ea922aaf10 | ||
|   | 766e6e20d8 | ||
|   | 8e646c244e | ||
|   | 26f31a5899 | ||
|   | 5c79e374dd | ||
|   | 7c1473ea95 | ||
|   | 1fe4cc3258 | ||
|   | 042ad4cea1 | ||
|   | e4c998dbce | ||
|   | 83c8cacdac | ||
|   | f75d5d906e | ||
|   | 85b3856564 | ||
|   | 75cb2569b5 | ||
|   | 0a4ac1cece | ||
|   | a873fd14bd | ||
|   | 85b4cd4998 | ||
|   | e9bc9b1aa8 | ||
|   | e3bf599bf6 | ||
|   | 01ae96840e | ||
|   | 186160ebf4 | ||
|   | 70f5e45c1f | ||
|   | 6dc54ecabc | ||
|   | d21888c047 | ||
|   | 33f7a90042 | ||
|   | a00d3a2c5e | ||
|   | abc64d769c | ||
|   | 88754ac569 | ||
|   | e3ee05d47d | ||
|   | 3b004e7483 | ||
|   | 9aa48c20e4 | ||
|   | 1b5d3beeca | ||
|   | 00115d313e | ||
|   | 190f1a205f | ||
|   | 8ab6f0c3fe | ||
|   | 13eea263c0 | ||
|   | b52b40962e | ||
|   | 7d1fa2e40c | ||
|   | 93b2098829 | ||
|   | a2acdc6b12 | ||
|   | 38b2c1e30f | ||
|   | e07f579f3c | ||
|   | df3297b6ca | ||
|   | fc4eeed764 | ||
|   | 3596d13be1 | ||
|   | 104d49ea1c | ||
|   | 7057317446 | ||
|   | 280575df88 | ||
|   | d2cb434b7b | ||
|   | 2ecb802a2e | ||
|   | 46e706c415 | ||
|   | 3a57349d8a | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 565db852e0 | ||
|   | 754c3269ec | ||
|   | a079842408 | ||
|   | 7664b58553 | ||
|   | de77488f7e | ||
|   | d808afd21b | ||
|   | b68aff76a1 | ||
|   | 6da7fe158f | ||
|   | 4dbc7fdc7d | ||
|   | ad1e1f7eca | ||
|   | 9c3f7a3823 | ||
|   | 86f4cb7701 | ||
|   | 359a90245d | ||
|   | d8d7d86359 | ||
|   | 7189b629c0 | ||
|   | 55c9b5566c | ||
|   | ab671ccbf7 | ||
|   | 316bda8c3f | ||
|   | 76f77e8a4c | ||
|   | 4a99d5eab7 | ||
|   | 473d287c6d | ||
|   | bddd9896e4 | ||
|   | 95eb115965 | ||
|   | 9a63b213b0 | ||
|   | 90b00d88f1 | ||
|   | ec87c7f21c | ||
|   | 452f45cac6 | ||
|   | c644b3d384 | ||
|   | 68160c20e8 | ||
|   | ad144206dd | ||
|   | f3d20ab769 | ||
|   | 9767ca7116 | ||
|   | 73a5b8553f | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 5c684cd499 | ||
|   | 479f3e3172 | ||
|   | 6a1350fd00 | ||
|   | 563784da1c | ||
|   | 347f0ed3a5 | ||
|   | 1db9f5b2c2 | ||
|   | f2a3e26188 | ||
|   | 1c0897bfb6 | ||
|   | f0793992a6 | ||
|   | 393205ba2f | ||
|   | ab49535b6c | ||
|   | 8191b48548 | ||
|   | 925dd06432 | ||
|   | 0d525e056a | ||
|   | 3aa86eebf2 | ||
|   | d7a93abec0 | ||
|   | 6448d28a18 | ||
|   | 449976483c | ||
|   | e468b762ef | ||
|   | b578e73cc4 | ||
|   | e381f95b95 | ||
|   | 5685afae63 | ||
|   | 53a473422b | ||
|   | 3f9f17f584 | ||
|   | 2c410bf318 | ||
|   | 40934a2c77 | ||
|   | ecc9379d7e | ||
|   | f8d27342dc | ||
|   | 17bd2bf2ba | ||
|   | d984a3f275 | ||
|   | 64049bd546 | ||
|   | 9218091c33 | ||
|   | 75df7d6413 | ||
|   | 29341f81e1 | ||
|   | 68c5a3dca7 | ||
|   | b1e2e370c8 | ||
|   | ba9d3afc88 | ||
|   | 9a0434be32 | ||
|   | d6923a2ff0 | ||
|   | 8f7f6dc19e | ||
|   | 70b8817643 | ||
|   | 87c25cbbfe | ||
|   | 082e905014 | ||
|   | e3e598b208 | ||
|   | 6cf92d4ea6 | ||
|   | 89ad685f3a | ||
|   | 1e868dc802 | ||
|   | 58fcfd9a34 | ||
|   | c849afbc11 | ||
|   | 2ebe71ddd0 | ||
|   | 7a8e8c1f3e | ||
|   | 5e897ad1c2 | ||
|   | ce07907f85 | ||
|   | 1e38aa7b53 | ||
|   | 3bc0c23e5a | ||
|   | f07b6d105a | ||
|   | 46491269e3 | ||
|   | 8d9c43af6a | ||
|   | c3568354aa | ||
|   | e68696ccd4 | ||
|   | 35d8b2e790 | ||
|   | 1f83573206 | ||
|   | 2887e76514 | ||
|   | 63b9943203 | ||
|   | bd5aedd83f | ||
|   | c9ff6d7bb9 | ||
|   | 5835def5d0 | ||
|   | 6a2694ce3b | ||
|   | 65ae7bce79 | ||
|   | 57ce8da0ee | ||
|   | 7484bf7403 | ||
|   | f6b2312c49 | ||
|   | 6027cb7cb0 | ||
|   | c1740aae6c | ||
|   | 22ce29e86c | ||
|   | 251d1b5b2e | ||
|   | ea64708c69 | ||
|   | 209ec27a29 | ||
|   | ceee495525 | ||
|   | 751a2347aa | ||
|   | efd96d5fdf | ||
|   | 2ee5af8bfb | ||
|   | a4c0edf493 | ||
|   | 28c8be97b6 | ||
|   | f320a44d45 | ||
|   | c0cc6cc176 | ||
|   | be62c09d06 | ||
|   | 5fe3546d2a | ||
|   | c4b2ea125c | ||
|   | 3301b038fe | ||
|   | 6a58d1e3da | ||
|   | e5670d820d | ||
|   | 1870aead73 | ||
|   | 95db6cca2c | ||
|   | 79a792ac62 | ||
|   | 6406f9eb86 | ||
|   | 8f8ec496f8 | ||
|   | 3d6847a3a2 | ||
|   | 613abaec1b | ||
|   | b51ae9ac38 | ||
|   | c4fd03542b | ||
|   | e40d6c5ef0 | ||
|   | 9f7dee3baa | ||
|   | d3da62c04a | ||
|   | aa7bb3f8c9 | ||
|   | 3a9dfe339a | ||
|   | 08830003a3 | ||
|   | efeeb6cb02 | ||
|   | ac59ce2deb | ||
|   | 3ff89a03ac | ||
|   | 25e231cd7c | ||
|   | f4d8d909b0 | ||
|   | 35ea18a117 | ||
|   | ee9433261b | ||
|   | 1cb48c7760 | ||
|   | 172bf4bd51 | ||
|   | fe1039cfbc | ||
|   | a9de79546b | ||
|   | 7afdf43872 | ||
|   | bfc7d5d0dd | ||
|   | 690832052a | ||
|   | f7c1a47d52 | ||
|   | 930843d065 | ||
|   | a9ee609502 | ||
|   | 9fd2125923 | ||
|   | ede8703f9d | ||
|   | 6d85623d9b | ||
|   | 6236ee8f6d | ||
|   | 92665d80d6 | ||
|   | 70ae0bc77e | ||
|   | e28c757352 | ||
|   | 7cdcf7ebab | ||
|   | 7c368af5ed | ||
|   | df2fbbabc6 | ||
|   | 48141eb199 | ||
|   | 343fbb282c | ||
|   | cea9ed056b | ||
|   | 73bd90c555 | ||
|   | 917de46c45 | ||
|   | b592092923 | ||
|   | 760bc1c22a | ||
|   | 1f8b81ee78 | ||
|   | 70af7d05e3 | ||
|   | 14c04ee4ac | ||
|   | 83815d3caa | ||
|   | 487c621fa5 | ||
|   | 304eab801c | ||
|   | dfe3add1cc | ||
|   | a32bf5539e | ||
|   | e944333e5f | ||
|   | dad23a52b0 | ||
|   | 53606c306d | ||
|   | 53b03f8231 | ||
|   | ac5f6dc024 | ||
|   | 2c15e0dd32 | ||
|   | 007334fef0 | ||
|   | b3ae2d2748 | ||
|   | 7149c766d0 | ||
|   | 5dca0191d2 | ||
|   | 356668a68d | ||
|   | 63c9442126 | ||
|   | 0031d7911d | ||
|   | 31c92b43b4 | ||
|   | 2667548041 | ||
|   | 54282ba7e0 | ||
|   | 7f324abd44 | ||
|   | 65a0c3b40a | ||
|   | 2449baac5b | ||
|   | 0ab03e1856 | ||
|   | 9a3f85106c | ||
|   | 42c495d8ab | ||
|   | 58def585f1 | ||
|   | 047034d831 | ||
|   | bdb2f24a81 | ||
|   | 636870a075 | ||
|   | 8981174302 | ||
|   | dd5f05334b | ||
|   | 929b626b51 | ||
|   | 7d5b2ec81e | ||
|   | f0e2d36c34 | ||
|   | 94f171d757 | ||
|   | 04e06db430 | ||
|   | d74b215169 | ||
|   | 404ea94dd2 | ||
|   | 4afe7c6b46 | ||
|   | 60b20c4d01 | ||
|   | 58658c6b1a | ||
|   | ec444317b3 | ||
|   | 8b4aee1afa | ||
|   | 51abd74304 | ||
|   | b93b465f09 | ||
|   | 5aad68ec62 | ||
|   | 74fbc937a1 | ||
|   | 7e35f544eb | ||
|   | ed1c3cffc1 | ||
|   | c4983a9f9b | ||
|   | 5b43262e7a | ||
|   | dad4408679 | ||
|   | a78c4d12b4 | ||
|   | 74664a9df8 | ||
|   | fce5281a03 | ||
|   | de0bd2f486 | ||
|   | 079fb5d108 | ||
|   | 1c7435a32b | ||
|   | 1afd374cf6 | ||
|   | 3adf549915 | ||
|   | e94d669eca | ||
|   | 5fac0b4689 | ||
|   | 832d323a6e | ||
|   | 03f1dbd5b2 | ||
|   | c0a216f109 | ||
|   | ad67b13270 | ||
|   | 5d420c08c6 | ||
|   | 3d8235c670 | ||
|   | 5a209f81d1 | ||
|   | d17d8e2805 | ||
|   | ca7636e7bc | ||
|   | 532cfc10d0 | ||
|   | 72d68c4377 | ||
|   | 00f5964aa4 | ||
|   | bd1ad5fa56 | ||
|   | f2e22e7445 | ||
|   | fcf345abab | ||
|   | 24ad43d3e4 | ||
|   | 9a7cdf42e1 | ||
|   | c36b652d5b | ||
|   | 553070fc23 | ||
|   | 5d69f7e24f | ||
|   | bc0e2bada0 | ||
|   | 80b6cc1d94 | ||
|   | bce3273e64 | ||
|   | 3abf61152a | ||
|   | 14923d4cd6 | ||
|   | 6fdc4bf900 | ||
|   | d21e1f75b5 | ||
|   | 84a0b37fcc | ||
|   | f135a0f09e | ||
|   | 0f24c99456 | ||
|   | 9eae0ab947 | ||
|   | 3bf1f79c79 | ||
|   | b21074c871 | ||
|   | d7893d9a32 | ||
|   | 9cbdda330c | ||
|   | 42a9bfd099 | ||
|   | eb15bd01ca | ||
|   | 9e98aebeb3 | ||
|   | 1361cbc826 | ||
|   | 679d0808a9 | ||
|   | 6fe9a548ad | ||
|   | 2d6d2430d2 | ||
|   | a445538e81 | ||
|   | 50d38ffbd8 | ||
|   | 93bcdfd9c9 | ||
|   | 5be3b101a5 | 
| @@ -28,7 +28,7 @@ cd packages/web | |||||||
| rm -rf .env | rm -rf .env | ||||||
| echo " | echo " | ||||||
| PORT=$WEB_PORT | PORT=$WEB_PORT | ||||||
| REACT_APP_GRAPHQL_URL=http://localhost:$BACKEND_PORT/graphql | REACT_APP_BACKEND_URL=http://localhost:$BACKEND_PORT | ||||||
| " >> .env | " >> .env | ||||||
| cd $CURRENT_DIR | cd $CURRENT_DIR | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								.eslintrc.js
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								.eslintrc.js
									
									
									
									
									
								
							| @@ -1,18 +0,0 @@ | |||||||
| module.exports = { |  | ||||||
|   root: true, |  | ||||||
|   parser: '@typescript-eslint/parser', |  | ||||||
|   plugins: ['@typescript-eslint'], |  | ||||||
|   extends: [ |  | ||||||
|     'eslint:recommended', |  | ||||||
|     'plugin:@typescript-eslint/recommended', |  | ||||||
|     'prettier', |  | ||||||
|   ], |  | ||||||
|   overrides: [ |  | ||||||
|     { |  | ||||||
|       files: ['**/*.test.ts', '**/test/**/*.ts'], |  | ||||||
|       rules: { |  | ||||||
|         '@typescript-eslint/ban-ts-comment': ['off'], |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|   ], |  | ||||||
| }; |  | ||||||
							
								
								
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -22,7 +22,7 @@ jobs: | |||||||
|       - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." |       - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." | ||||||
|       - run: echo "🖥️ The workflow is now ready to test your code on the runner." |       - run: echo "🖥️ The workflow is now ready to test your code on the runner." | ||||||
|       - run: yarn --frozen-lockfile |       - run: yarn --frozen-lockfile | ||||||
|       - run: yarn lint |       - run: cd packages/backend && yarn lint | ||||||
|       - run: echo "🍏 This job's status is ${{ job.status }}." |       - run: echo "🍏 This job's status is ${{ job.status }}." | ||||||
|   start-backend-server: |   start-backend-server: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								.github/workflows/docs-change.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.github/workflows/docs-change.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | name: Automatisch Docs Change | ||||||
|  | on: | ||||||
|  |   pull_request: | ||||||
|  |     paths: | ||||||
|  |       - 'packages/docs/**' | ||||||
|  | jobs: | ||||||
|  |   label: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - name: Checkout repository | ||||||
|  |         uses: actions/checkout@v3 | ||||||
|  |       - name: Label PR | ||||||
|  |         uses: actions/github-script@v6 | ||||||
|  |         with: | ||||||
|  |           script: | | ||||||
|  |             const { pull_request } = context.payload; | ||||||
|  |  | ||||||
|  |             const label = 'documentation-change'; | ||||||
|  |             const hasLabel = pull_request.labels.some(({ name }) => name === label); | ||||||
|  |  | ||||||
|  |             if (!hasLabel) { | ||||||
|  |               await github.rest.issues.addLabels({ | ||||||
|  |                 owner: context.repo.owner, | ||||||
|  |                 repo: context.repo.repo, | ||||||
|  |                 issue_number: pull_request.number, | ||||||
|  |                 labels: [label], | ||||||
|  |               }); | ||||||
|  |  | ||||||
|  |               console.log(`Label "${label}" added to PR #${pull_request.number}`); | ||||||
|  |             } else { | ||||||
|  |               console.log(`Label "${label}" already exists on PR #${pull_request.number}`); | ||||||
|  |             } | ||||||
							
								
								
									
										5
									
								
								.github/workflows/playwright.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/playwright.yml
									
									
									
									
										vendored
									
									
								
							| @@ -62,8 +62,9 @@ jobs: | |||||||
|         run: yarn && yarn lerna bootstrap |         run: yarn && yarn lerna bootstrap | ||||||
|       - name: Install Playwright Browsers |       - name: Install Playwright Browsers | ||||||
|         run: yarn playwright install --with-deps |         run: yarn playwright install --with-deps | ||||||
|       - name: Build Automatisch |       - name: Build Automatisch web | ||||||
|         run: yarn lerna run --scope=@*/{web,cli} build |         working-directory: ./packages/web | ||||||
|  |         run: yarn build | ||||||
|         env: |         env: | ||||||
|           # Keep this until we clean up warnings in build processes |           # Keep this until we clean up warnings in build processes | ||||||
|           CI: false |           CI: false | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ | |||||||
|     "start": "lerna run --stream --parallel --scope=@*/{web,backend} dev", |     "start": "lerna run --stream --parallel --scope=@*/{web,backend} dev", | ||||||
|     "start:web": "lerna run --stream --scope=@*/web dev", |     "start:web": "lerna run --stream --scope=@*/web dev", | ||||||
|     "start:backend": "lerna run --stream --scope=@*/backend dev", |     "start:backend": "lerna run --stream --scope=@*/backend dev", | ||||||
|     "lint": "lerna run --no-bail --stream --parallel --scope=@*/{web,backend} lint", |  | ||||||
|     "build:docs": "cd ./packages/docs && yarn install && yarn build" |     "build:docs": "cd ./packages/docs && yarn install && yarn build" | ||||||
|   }, |   }, | ||||||
|   "workspaces": { |   "workspaces": { | ||||||
| @@ -21,8 +20,6 @@ | |||||||
|     ] |     ] | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@typescript-eslint/eslint-plugin": "^5.9.1", |  | ||||||
|     "@typescript-eslint/parser": "^5.9.1", |  | ||||||
|     "eslint": "^8.13.0", |     "eslint": "^8.13.0", | ||||||
|     "eslint-config-prettier": "^8.3.0", |     "eslint-config-prettier": "^8.3.0", | ||||||
|     "eslint-plugin-prettier": "^4.0.0", |     "eslint-plugin-prettier": "^4.0.0", | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|     "start:worker": "node src/worker.js", |     "start:worker": "node src/worker.js", | ||||||
|     "pretest": "APP_ENV=test node ./test/setup/prepare-test-env.js", |     "pretest": "APP_ENV=test node ./test/setup/prepare-test-env.js", | ||||||
|     "test": "APP_ENV=test vitest run", |     "test": "APP_ENV=test vitest run", | ||||||
|     "lint": "eslint . --ignore-path ../../.eslintignore", |     "lint": "eslint .", | ||||||
|     "db:create": "node ./bin/database/create.js", |     "db:create": "node ./bin/database/create.js", | ||||||
|     "db:seed:user": "node ./bin/database/seed-user.js", |     "db:seed:user": "node ./bin/database/seed-user.js", | ||||||
|     "db:drop": "node ./bin/database/drop.js", |     "db:drop": "node ./bin/database/drop.js", | ||||||
| @@ -38,6 +38,7 @@ | |||||||
|     "debug": "~2.6.9", |     "debug": "~2.6.9", | ||||||
|     "dotenv": "^10.0.0", |     "dotenv": "^10.0.0", | ||||||
|     "express": "~4.18.2", |     "express": "~4.18.2", | ||||||
|  |     "express-async-handler": "^1.2.0", | ||||||
|     "express-basic-auth": "^1.2.1", |     "express-basic-auth": "^1.2.1", | ||||||
|     "express-graphql": "^0.12.0", |     "express-graphql": "^0.12.0", | ||||||
|     "fast-xml-parser": "^4.0.11", |     "fast-xml-parser": "^4.0.11", | ||||||
|   | |||||||
| @@ -1,32 +0,0 @@ | |||||||
| <svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" id="b" data-name="Layer 2" width="327.714" height="120" viewBox="0 0 327.714 120"> |  | ||||||
|   <defs> |  | ||||||
|     <style> |  | ||||||
|       .d { |  | ||||||
|         fill: #039649; |  | ||||||
|       } |  | ||||||
|     </style> |  | ||||||
|   </defs> |  | ||||||
|   <g id="c" data-name="Layer 1"> |  | ||||||
|     <g> |  | ||||||
|       <path class="d" d="m52.39,120c-1.801,0-3.6-.6-5.101-1.5-2.1-1.5-3.6-4.2-3.6-6.9v-24.6L1.09,12.901C-.41,9.9-.41,6.601,1.391,4.2,2.891,1.5,5.89,0,8.89,0h78c2.999,0,5.698,1.5,7.199,4.2,1.801,2.702,1.801,6.3.301,9.002l-5.101,9h10.201c2.999,0,5.698,1.5,7.199,4.2,1.801,2.7,1.801,6.3.301,9l-29.701,51.3v18.599c0,3.899-2.399,6.9-5.999,8.099l-16.2,6.3c-.6.301-1.799.301-2.7.301M8.89,8.4q-.301.301-.301.602l42.301,73.798c1.199,2.1,1.199,3.299,1.199,4.501v23.998s.301.301.6,0l16.2-6.3h.6v-17.999c0-1.199,0-3.299,1.201-4.8l29.4-50.999c0-.301,0-.6-.301-.6l-53.699-.301,14.399,24.901,10.201-17.7c1.199-2.1,3.6-2.7,5.7-1.5,2.1,1.199,2.7,3.6,1.498,5.7l-11.998,20.699c-2.702,4.2-8.701,3.901-11.102.301l-17.4-30.9c-1.199-1.799-1.199-4.2,0-6.3,1.201-2.1,3.301-3.6,5.7-3.6h36.6l7.499-12.899s0-.301-.299-.602H8.89Z"/> |  | ||||||
|       <g> |  | ||||||
|         <path d="m181.725,48.737c0,3.089-.54,5.684-1.618,7.788-1.081,2.104-2.548,3.79-4.407,5.062-1.858,1.27-4.016,2.179-6.476,2.726-2.459.547-5.069.819-7.828.819h-24.591V6.521h23.034c2.676,0,5.198.24,7.561.718,2.364.479,4.427,1.311,6.189,2.5,1.762,1.189,3.149,2.787,4.16,4.795,1.011,2.008,1.517,4.53,1.517,7.562,0,3.17-.786,5.813-2.357,7.93-1.57,2.119-3.913,3.628-7.028,4.53,4.017.819,6.995,2.371,8.935,4.652s2.91,5.458,2.91,9.529Zm-33.813-17.624h10.533c1.612,0,3.033-.136,4.263-.41,1.23-.272,2.268-.738,3.115-1.393.846-.656,1.489-1.55,1.927-2.684.436-1.134.655-2.562.655-4.284,0-1.612-.267-2.923-.799-3.934-.532-1.01-1.25-1.809-2.152-2.398-.902-.587-1.967-.99-3.197-1.209s-2.542-.328-3.934-.328h-10.411v16.64Zm0,26.067h12.09c1.64,0,3.115-.169,4.427-.512,1.311-.342,2.424-.894,3.341-1.66.915-.764,1.612-1.748,2.091-2.951.478-1.202.716-2.663.716-4.385,0-1.802-.294-3.285-.881-4.447-.588-1.161-1.394-2.083-2.419-2.766s-2.227-1.154-3.606-1.414c-1.381-.26-2.863-.39-4.447-.39h-11.312v18.525Z"/> |  | ||||||
|         <path d="m202.257,9.555c0,.847-.151,1.633-.451,2.356-.3.724-.716,1.34-1.25,1.844-.532.507-1.167.902-1.905,1.189s-1.53.431-2.377.431c-.819,0-1.598-.144-2.336-.431s-1.388-.69-1.947-1.209c-.56-.519-.998-1.133-1.311-1.844-.315-.711-.471-1.489-.471-2.336s.156-1.626.471-2.336c.314-.711.744-1.325,1.291-1.845.546-.519,1.187-.922,1.925-1.209s1.53-.431,2.377-.431c.821,0,1.598.144,2.336.431s1.373.69,1.907,1.209c.532.52.955,1.134,1.27,1.845.314.71.471,1.489.471,2.336Zm-.778,12.705v42.871h-10.246V22.261h10.246Z"/> |  | ||||||
|         <path d="m254.186,61.935c0,3.361-.587,6.236-1.762,8.627-1.174,2.391-2.78,4.352-4.815,5.882-2.036,1.529-4.407,2.65-7.111,3.361-2.706.71-5.589,1.065-8.648,1.065-2.023,0-4.037-.157-6.045-.471-2.009-.314-3.908-.861-5.697-1.64-1.79-.778-3.395-1.837-4.816-3.175-1.421-1.34-2.555-3.021-3.402-5.042l8.074-3.525c.519,1.202,1.202,2.193,2.049,2.971.847.779,1.797,1.408,2.848,1.885,1.051.479,2.172.814,3.361,1.005s2.398.287,3.628.287c1.885,0,3.572-.232,5.062-.696,1.489-.466,2.752-1.169,3.79-2.111,1.039-.943,1.838-2.132,2.399-3.566.559-1.434.839-3.107.839-5.02v-6.393c-.71,1.229-1.592,2.336-2.643,3.319-1.053.983-2.207,1.824-3.464,2.52s-2.582,1.237-3.976,1.62c-1.393.383-2.814.574-4.263.574-3.033,0-5.737-.56-8.114-1.681-2.377-1.119-4.379-2.636-6.005-4.55-1.625-1.912-2.862-4.139-3.709-6.68-.847-2.542-1.27-5.233-1.27-8.074,0-2.814.451-5.491,1.353-8.033.901-2.542,2.192-4.775,3.873-6.702,1.68-1.927,3.709-3.464,6.086-4.611,2.376-1.147,5.054-1.721,8.033-1.721,2.923,0,5.621.594,8.094,1.782,2.472,1.189,4.473,3.082,6.004,5.677v-6.557h10.246v39.674Zm-33.198-19.017c0,1.666.252,3.265.758,4.795s1.237,2.876,2.193,4.037c.957,1.162,2.137,2.084,3.545,2.767s3.013,1.025,4.816,1.025c2.021,0,3.784-.355,5.287-1.066,1.502-.711,2.746-1.673,3.729-2.89.985-1.215,1.722-2.643,2.213-4.283.492-1.64.738-3.374.738-5.206,0-1.802-.239-3.49-.716-5.062-.479-1.57-1.203-2.93-2.173-4.077s-2.179-2.056-3.626-2.726c-1.449-.67-3.157-1.005-5.123-1.005-2.049,0-3.812.363-5.287,1.086-1.476.724-2.684,1.709-3.628,2.951-.943,1.243-1.633,2.699-2.069,4.365-.438,1.666-.656,3.429-.656,5.287Z"/> |  | ||||||
|         <path d="m277.096,9.555c0,.847-.151,1.633-.451,2.356-.3.724-.716,1.34-1.25,1.844-.532.507-1.167.902-1.905,1.189s-1.53.431-2.377.431c-.819,0-1.598-.144-2.336-.431s-1.388-.69-1.947-1.209c-.56-.519-.998-1.133-1.311-1.844-.315-.711-.471-1.489-.471-2.336s.156-1.626.471-2.336c.314-.711.744-1.325,1.291-1.845.546-.519,1.187-.922,1.925-1.209s1.53-.431,2.377-.431c.821,0,1.598.144,2.336.431s1.373.69,1.907,1.209c.532.52.955,1.134,1.27,1.845.314.71.471,1.489.471,2.336Zm-.778,12.705v42.871h-10.246V22.261h10.246Z"/> |  | ||||||
|         <path d="m308.451,29.801c-1.585,0-2.999.24-4.243.718-1.243.479-2.288,1.162-3.135,2.049-.847.889-1.496,1.961-1.947,3.217-.451,1.258-.676,2.651-.676,4.181v25.165h-10.246V22.261h10.246v6.803c.683-1.338,1.537-2.492,2.562-3.462,1.025-.97,2.165-1.769,3.422-2.399,1.257-.627,2.59-1.091,3.997-1.393,1.406-.3,2.82-.451,4.241-.451,2.623,0,4.884.431,6.783,1.291s3.464,2.049,4.694,3.565c1.229,1.517,2.131,3.307,2.704,5.37s.861,4.311.861,6.742v26.805h-10.328v-25.822c0-3.005-.711-5.341-2.132-7.008-1.421-1.666-3.688-2.5-6.803-2.5Z"/> |  | ||||||
|       </g> |  | ||||||
|       <g> |  | ||||||
|         <path d="m152.871,102.12c0,1.138-.168,2.221-.507,3.25-.337,1.028-.828,1.935-1.471,2.72s-1.436,1.41-2.379,1.874-2.021.696-3.234.696c-.579,0-1.155-.061-1.723-.183-.569-.121-1.107-.306-1.613-.553-.505-.247-.974-.561-1.407-.941s-.801-.822-1.107-1.328v2.72h-2.625v-24.446h2.625v10.689c.273-.464.632-.882,1.076-1.257.442-.374.93-.696,1.463-.964.531-.27,1.082-.477,1.652-.626.569-.146,1.122-.22,1.66-.22,1.244,0,2.34.227,3.289.679.95.454,1.743,1.068,2.38,1.843s1.117,1.685,1.44,2.728c.321,1.044.482,2.151.482,3.321Zm-13.536.126c0,.917.123,1.756.371,2.515.249.759.614,1.415,1.1,1.968.485.553,1.079.984,1.787,1.289.706.306,1.517.459,2.435.459.991,0,1.813-.178,2.467-.53.653-.354,1.178-.828,1.573-1.424.395-.595.674-1.283.838-2.063.163-.78.245-1.603.245-2.467,0-.801-.104-1.576-.308-2.325-.206-.748-.52-1.415-.941-1.999-.422-.586-.958-1.055-1.605-1.407-.648-.354-1.415-.53-2.3-.53-.981,0-1.827.174-2.538.521-.711.349-1.3.818-1.764,1.409-.464.59-.806,1.28-1.028,2.071s-.332,1.629-.332,2.514Z"/> |  | ||||||
|         <path d="m169.949,93.834l-9.141,22.297h-2.656l3.131-7.464-6.388-14.833h2.814l4.965,11.86,4.586-11.86h2.689Z"/> |  | ||||||
|         <path d="m194.11,108.034v2.34h-16.414v-1.139l13.189-19.228h-12.05v-2.246h15.465v1.139l-12.84,19.134h12.65Z"/> |  | ||||||
|         <path d="m213.229,102.215c0,1.202-.206,2.319-.617,3.352-.411,1.034-.986,1.927-1.723,2.681-.739.753-1.611,1.344-2.618,1.77-1.007.428-2.106.641-3.296.641-1.256,0-2.388-.222-3.4-.665s-1.874-1.053-2.585-1.834-1.257-1.697-1.637-2.752c-.38-1.053-.57-2.192-.57-3.416,0-1.191.201-2.3.601-3.329.4-1.028.964-1.92,1.692-2.68.727-.759,1.594-1.354,2.601-1.787s2.116-.648,3.329-.648c1.254,0,2.391.22,3.408.663s1.881,1.055,2.593,1.835,1.26,1.697,1.644,2.751c.385,1.055.578,2.192.578,3.416Zm-13.726.031c0,.886.117,1.708.349,2.467s.579,1.418,1.043,1.977c.464.558,1.038.995,1.723,1.311.685.317,1.481.476,2.388.476.938,0,1.753-.175,2.443-.522.691-.349,1.263-.816,1.717-1.407.452-.591.79-1.275,1.012-2.056.22-.78.332-1.602.332-2.466,0-.844-.117-1.646-.349-2.404-.232-.759-.579-1.429-1.043-2.008s-1.038-1.038-1.723-1.376c-.685-.337-1.481-.505-2.388-.505-.971,0-1.802.174-2.498.521-.696.349-1.265.82-1.708,1.416-.443.595-.77,1.288-.981,2.078-.21.792-.316,1.624-.316,2.498Z"/> |  | ||||||
|         <path d="m224.423,95.827c-.696,0-1.323.105-1.881.316s-1.034.512-1.423.902c-.391.39-.691.864-.902,1.423s-.316,1.186-.316,1.881v10.026h-2.625v-24.446h2.625v10.531c.253-.516.564-.956.933-1.32s.777-.663,1.226-.902c.447-.237.933-.411,1.454-.521.522-.111,1.057-.166,1.605-.166,1.033,0,1.937.171,2.712.513.775.343,1.423.818,1.945,1.424.522.605.915,1.326,1.178,2.157.263.833.395,1.745.395,2.735v9.994h-2.625v-10.184c0-1.423-.36-2.506-1.083-3.25-.722-.742-1.795-1.114-3.217-1.114Z"/> |  | ||||||
|         <path d="m251.685,102.215c0,1.202-.206,2.319-.617,3.352-.411,1.034-.986,1.927-1.723,2.681-.739.753-1.611,1.344-2.618,1.77-1.007.428-2.106.641-3.296.641-1.256,0-2.388-.222-3.4-.665s-1.874-1.053-2.585-1.834-1.257-1.697-1.637-2.752c-.38-1.053-.57-2.192-.57-3.416,0-1.191.201-2.3.601-3.329.4-1.028.964-1.92,1.692-2.68.727-.759,1.594-1.354,2.601-1.787s2.116-.648,3.329-.648c1.254,0,2.391.22,3.408.663s1.881,1.055,2.593,1.835,1.26,1.697,1.644,2.751c.385,1.055.578,2.192.578,3.416Zm-13.726.031c0,.886.117,1.708.349,2.467s.579,1.418,1.043,1.977c.464.558,1.038.995,1.723,1.311.685.317,1.481.476,2.388.476.938,0,1.753-.175,2.443-.522.691-.349,1.263-.816,1.717-1.407.452-.591.79-1.275,1.012-2.056.22-.78.332-1.602.332-2.466,0-.844-.117-1.646-.349-2.404-.232-.759-.579-1.429-1.043-2.008s-1.038-1.038-1.723-1.376c-.685-.337-1.481-.505-2.388-.505-.971,0-1.802.174-2.498.521-.696.349-1.265.82-1.708,1.416-.443.595-.77,1.288-.981,2.078-.21.792-.316,1.624-.316,2.498Z"/> |  | ||||||
|         <path d="m281.097,103.923c-.38.959-.884,1.85-1.511,2.672-.627.823-1.346,1.534-2.157,2.135-.812.601-1.703,1.073-2.673,1.415-.969.342-1.976.514-3.02.514-1.76,0-3.312-.295-4.656-.886-1.345-.59-2.472-1.407-3.385-2.45-.912-1.044-1.603-2.279-2.072-3.709-.469-1.428-.704-2.98-.704-4.657,0-1.033.114-2.037.341-3.013.227-.974.553-1.889.98-2.743.428-.854.95-1.637,1.565-2.348.617-.711,1.318-1.32,2.104-1.827.785-.505,1.652-.901,2.601-1.186.949-.284,1.961-.426,3.036-.426,1.012,0,2.003.148,2.973.442.971.295,1.866.718,2.689,1.266.822.548,1.55,1.209,2.182,1.984s1.127,1.642,1.486,2.602l-2.246.98c-.295-.768-.673-1.468-1.13-2.095-.459-.627-.989-1.162-1.59-1.604-.601-.443-1.265-.785-1.992-1.029-.728-.242-1.508-.363-2.341-.363-1.359,0-2.532.268-3.518.806s-1.797,1.254-2.435,2.151c-.638.895-1.107,1.919-1.407,3.067-.301,1.149-.451,2.335-.451,3.558,0,1.244.166,2.424.498,3.543.333,1.117.833,2.098,1.503,2.94.669.844,1.507,1.513,2.514,2.008,1.007.496,2.18.744,3.518.744.801,0,1.576-.143,2.325-.428.749-.284,1.44-.671,2.072-1.162.632-.49,1.191-1.064,1.675-1.723.485-.658.864-1.357,1.139-2.095l2.088.917Z"/> |  | ||||||
|         <path d="m287.991,100.539v9.835h-2.751v-22.613h7.431c1.044,0,2.042.111,2.997.332.954.222,1.795.586,2.522,1.092.728.505,1.307,1.168,1.74,1.984.431.818.648,1.822.648,3.013,0,.885-.137,1.673-.411,2.364-.275.691-.66,1.289-1.155,1.795-.496.507-1.086.925-1.771,1.257-.685.333-1.44.583-2.261.752l6.926,10.026h-3.068l-6.736-9.835h-4.112Zm0-2.183h4.871c.727,0,1.393-.075,1.999-.228s1.131-.402,1.574-.744c.442-.342.785-.785,1.028-1.328s.363-1.21.363-2.001c0-.801-.126-1.466-.378-1.992-.254-.527-.601-.943-1.044-1.25-.443-.305-.967-.521-1.573-.648s-1.262-.189-1.968-.189h-4.871v8.38Z"/> |  | ||||||
|         <path d="m308.231,91.335v19.039h-2.529v-22.613h3.826l7.242,17.932,7.148-17.932h3.795v22.613h-2.752v-19.039l-7.653,19.039h-1.17l-7.907-19.039Z"/> |  | ||||||
|       </g> |  | ||||||
|     </g> |  | ||||||
|   </g> |  | ||||||
| </svg> |  | ||||||
| Before Width: | Height: | Size: 10 KiB | 
| @@ -1,25 +0,0 @@ | |||||||
| import { URLSearchParams } from 'url'; |  | ||||||
| import authScope from '../common/auth-scope.js'; |  | ||||||
|  |  | ||||||
| export default async function generateAuthUrl($) { |  | ||||||
|   const oauthRedirectUrlField = $.app.auth.fields.find( |  | ||||||
|     (field) => field.key == 'oAuthRedirectUrl' |  | ||||||
|   ); |  | ||||||
|   const redirectUri = oauthRedirectUrlField.value; |  | ||||||
|   const searchParams = new URLSearchParams({ |  | ||||||
|     scope: authScope.join(','), |  | ||||||
|     client_id: $.auth.data.clientId, |  | ||||||
|     response_type: 'code', |  | ||||||
|     access_type: 'offline', |  | ||||||
|     redirect_uri: redirectUri, |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   const domain = |  | ||||||
|     $.auth.data.region !== 'cn' ? 'account.zoho.com' : 'accounts.zoho.com.cn'; |  | ||||||
|  |  | ||||||
|   const url = `https://${domain}/oauth/v2/auth?${searchParams.toString()}`; |  | ||||||
|  |  | ||||||
|   await $.auth.set({ |  | ||||||
|     url, |  | ||||||
|   }); |  | ||||||
| } |  | ||||||
| @@ -1,66 +0,0 @@ | |||||||
| import generateAuthUrl from './generate-auth-url.js'; |  | ||||||
| import verifyCredentials from './verify-credentials.js'; |  | ||||||
| import refreshToken from './refresh-token.js'; |  | ||||||
| import isStillVerified from './is-still-verified.js'; |  | ||||||
|  |  | ||||||
| export default { |  | ||||||
|   fields: [ |  | ||||||
|     { |  | ||||||
|       key: 'oAuthRedirectUrl', |  | ||||||
|       label: 'OAuth Redirect URL', |  | ||||||
|       type: 'string', |  | ||||||
|       required: true, |  | ||||||
|       readOnly: true, |  | ||||||
|       value: '{WEB_APP_URL}/app/bigin-by-zoho-crm/connections/add', |  | ||||||
|       placeholder: null, |  | ||||||
|       description: |  | ||||||
|         'When asked to input a redirect URL in Bigin By Zoho CRM, enter the URL above.', |  | ||||||
|       clickToCopy: true, |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       key: 'region', |  | ||||||
|       label: 'Region', |  | ||||||
|       type: 'dropdown', |  | ||||||
|       required: true, |  | ||||||
|       readOnly: false, |  | ||||||
|       value: null, |  | ||||||
|       placeholder: null, |  | ||||||
|       description: '', |  | ||||||
|       options: [ |  | ||||||
|         { label: 'United States', value: 'us' }, |  | ||||||
|         { label: 'European Union', value: 'eu' }, |  | ||||||
|         { label: 'Australia', value: 'au' }, |  | ||||||
|         { label: 'India', value: 'in' }, |  | ||||||
|         { label: 'China', value: 'cn' }, |  | ||||||
|       ], |  | ||||||
|       clickToCopy: false, |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       key: 'clientId', |  | ||||||
|       label: 'Client ID', |  | ||||||
|       type: 'string', |  | ||||||
|       required: true, |  | ||||||
|       readOnly: false, |  | ||||||
|       value: null, |  | ||||||
|       placeholder: null, |  | ||||||
|       description: null, |  | ||||||
|       clickToCopy: false, |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       key: 'clientSecret', |  | ||||||
|       label: 'Client Secret', |  | ||||||
|       type: 'string', |  | ||||||
|       required: true, |  | ||||||
|       readOnly: false, |  | ||||||
|       value: null, |  | ||||||
|       placeholder: null, |  | ||||||
|       description: null, |  | ||||||
|       clickToCopy: false, |  | ||||||
|     }, |  | ||||||
|   ], |  | ||||||
|  |  | ||||||
|   generateAuthUrl, |  | ||||||
|   verifyCredentials, |  | ||||||
|   isStillVerified, |  | ||||||
|   refreshToken, |  | ||||||
| }; |  | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| import getCurrentOrganization from '../common/get-current-organization.js'; |  | ||||||
|  |  | ||||||
| const isStillVerified = async ($) => { |  | ||||||
|   const org = await getCurrentOrganization($); |  | ||||||
|   return !!org.id; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default isStillVerified; |  | ||||||
| @@ -1,34 +0,0 @@ | |||||||
| import { URLSearchParams } from 'node:url'; |  | ||||||
|  |  | ||||||
| import authScope from '../common/auth-scope.js'; |  | ||||||
| import { regionUrlMap } from '../common/region-url-map.js'; |  | ||||||
|  |  | ||||||
| const refreshToken = async ($) => { |  | ||||||
|   const location = $.auth.data.location; |  | ||||||
|   const params = new URLSearchParams({ |  | ||||||
|     client_id: $.auth.data.clientId, |  | ||||||
|     client_secret: $.auth.data.clientSecret, |  | ||||||
|     refresh_token: $.auth.data.refreshToken, |  | ||||||
|     grant_type: 'refresh_token', |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   const { data } = await $.http.post( |  | ||||||
|     `${regionUrlMap[location]}/oauth/v2/token`, |  | ||||||
|     params.toString(), |  | ||||||
|     { |  | ||||||
|       additionalProperties: { |  | ||||||
|         skipAddingBaseUrl: true, |  | ||||||
|       }, |  | ||||||
|     } |  | ||||||
|   ); |  | ||||||
|  |  | ||||||
|   await $.auth.set({ |  | ||||||
|     accessToken: data.access_token, |  | ||||||
|     apiDomain: data.api_domain, |  | ||||||
|     scope: authScope.join(','), |  | ||||||
|     tokenType: data.token_type, |  | ||||||
|     expiresIn: data.expires_in, |  | ||||||
|   }); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default refreshToken; |  | ||||||
| @@ -1,46 +0,0 @@ | |||||||
| import { URLSearchParams } from 'node:url'; |  | ||||||
| import { regionUrlMap } from '../common/region-url-map.js'; |  | ||||||
| import getCurrentOrganization from '../common/get-current-organization.js'; |  | ||||||
|  |  | ||||||
| const verifyCredentials = async ($) => { |  | ||||||
|   const oauthRedirectUrlField = $.app.auth.fields.find( |  | ||||||
|     (field) => field.key == 'oAuthRedirectUrl' |  | ||||||
|   ); |  | ||||||
|   const redirectUri = oauthRedirectUrlField.value; |  | ||||||
|   const location = $.auth.data.location; |  | ||||||
|   const params = new URLSearchParams({ |  | ||||||
|     client_id: $.auth.data.clientId, |  | ||||||
|     client_secret: $.auth.data.clientSecret, |  | ||||||
|     code: $.auth.data.code, |  | ||||||
|     redirect_uri: redirectUri, |  | ||||||
|     grant_type: 'authorization_code', |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   const { data } = await $.http.post( |  | ||||||
|     `${regionUrlMap[location]}/oauth/v2/token`, |  | ||||||
|     params.toString() |  | ||||||
|   ); |  | ||||||
|  |  | ||||||
|   await $.auth.set({ |  | ||||||
|     accessToken: data.access_token, |  | ||||||
|     tokenType: data.token_type, |  | ||||||
|     apiDomain: data.api_domain, |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   const organization = await getCurrentOrganization($); |  | ||||||
|  |  | ||||||
|   const screenName = [organization.company_name, organization.primary_email] |  | ||||||
|     .filter(Boolean) |  | ||||||
|     .join(' @ '); |  | ||||||
|  |  | ||||||
|   await $.auth.set({ |  | ||||||
|     clientId: $.auth.data.clientId, |  | ||||||
|     clientSecret: $.auth.data.clientSecret, |  | ||||||
|     scope: $.auth.data.scope, |  | ||||||
|     expiresIn: data.expires_in, |  | ||||||
|     refreshToken: data.refresh_token, |  | ||||||
|     screenName, |  | ||||||
|   }); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default verifyCredentials; |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| const addAuthHeader = ($, requestConfig) => { |  | ||||||
|   if ($.auth.data?.accessToken) { |  | ||||||
|     requestConfig.headers.Authorization = `Zoho-oauthtoken ${$.auth.data.accessToken}`; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return requestConfig; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default addAuthHeader; |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| const authScope = [ |  | ||||||
|   'ZohoBigin.notifications.ALL', |  | ||||||
|   'ZohoBigin.users.ALL', |  | ||||||
|   'ZohoBigin.modules.ALL', |  | ||||||
|   'ZohoBigin.org.READ', |  | ||||||
|   'ZohoBigin.settings.ALL', |  | ||||||
|   'ZohoBigin.modules.ALL', |  | ||||||
| ]; |  | ||||||
|  |  | ||||||
| export default authScope; |  | ||||||
| @@ -1,6 +0,0 @@ | |||||||
| const getCurrentOrganization = async ($) => { |  | ||||||
|   const response = await $.http.get('/bigin/v2/org'); |  | ||||||
|   return response.data.org[0]; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default getCurrentOrganization; |  | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| export const regionUrlMap = { |  | ||||||
|   us: 'https://accounts.zoho.com', |  | ||||||
|   au: 'https://accounts.zoho.com.au', |  | ||||||
|   eu: 'https://accounts.zoho.eu', |  | ||||||
|   in: 'https://accounts.zoho.in', |  | ||||||
|   cn: 'https://accounts.zoho.com.cn', |  | ||||||
|   jp: 'https://accounts.zoho.jp', |  | ||||||
| }; |  | ||||||
| @@ -1,14 +0,0 @@ | |||||||
| const setBaseUrl = ($, requestConfig) => { |  | ||||||
|   if (requestConfig.additionalProperties?.skipAddingBaseUrl) |  | ||||||
|     return requestConfig; |  | ||||||
|  |  | ||||||
|   const apiDomain = $.auth.data.apiDomain; |  | ||||||
|  |  | ||||||
|   if (apiDomain) { |  | ||||||
|     requestConfig.baseURL = apiDomain; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return requestConfig; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default setBaseUrl; |  | ||||||
| @@ -1,3 +0,0 @@ | |||||||
| import listOrganizations from './list-organizations/index.js'; |  | ||||||
|  |  | ||||||
| export default [listOrganizations]; |  | ||||||
| @@ -1,23 +0,0 @@ | |||||||
| export default { |  | ||||||
|   name: 'List organizations', |  | ||||||
|   key: 'listOrganizations', |  | ||||||
|  |  | ||||||
|   async run($) { |  | ||||||
|     const organizations = { |  | ||||||
|       data: [], |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     const { data } = await $.http.get('/bigin/v2/org'); |  | ||||||
|  |  | ||||||
|     if (data.org) { |  | ||||||
|       for (const org of data.org) { |  | ||||||
|         organizations.data.push({ |  | ||||||
|           value: org.id, |  | ||||||
|           name: org.company_name, |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return organizations; |  | ||||||
|   }, |  | ||||||
| }; |  | ||||||
| @@ -1,21 +0,0 @@ | |||||||
| import defineApp from '../../helpers/define-app.js'; |  | ||||||
| import addAuthHeader from './common/add-auth-header.js'; |  | ||||||
| import auth from './auth/index.js'; |  | ||||||
| import setBaseUrl from './common/set-base-url.js'; |  | ||||||
| import triggers from './triggers/index.js'; |  | ||||||
| import dynamicData from './dynamic-data/index.js'; |  | ||||||
|  |  | ||||||
| export default defineApp({ |  | ||||||
|   name: 'Bigin By Zoho CRM', |  | ||||||
|   key: 'bigin-by-zoho-crm', |  | ||||||
|   baseUrl: 'https://www.bigin.com', |  | ||||||
|   apiBaseUrl: '', |  | ||||||
|   iconUrl: '{BASE_URL}/apps/bigin-by-zoho-crm/assets/favicon.svg', |  | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/bigin-by-zoho-crm/connection', |  | ||||||
|   primaryColor: '039649', |  | ||||||
|   supportsConnections: true, |  | ||||||
|   beforeRequest: [setBaseUrl, addAuthHeader], |  | ||||||
|   auth, |  | ||||||
|   triggers, |  | ||||||
|   dynamicData, |  | ||||||
| }); |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| import newCalls from './new-calls/index.js'; |  | ||||||
| import newCompanies from './new-companies/index.js'; |  | ||||||
| import newContacts from './new-contacts/index.js'; |  | ||||||
|  |  | ||||||
| export default [newCalls, newCompanies, newContacts]; |  | ||||||
| @@ -1,89 +0,0 @@ | |||||||
| import Crypto from 'crypto'; |  | ||||||
| import defineTrigger from '../../../../helpers/define-trigger.js'; |  | ||||||
|  |  | ||||||
| export default defineTrigger({ |  | ||||||
|   name: 'New calls', |  | ||||||
|   key: 'newCalls', |  | ||||||
|   type: 'webhook', |  | ||||||
|   description: 'Triggers when a new call is added.', |  | ||||||
|   arguments: [ |  | ||||||
|     { |  | ||||||
|       label: 'Organization', |  | ||||||
|       key: 'organizationId', |  | ||||||
|       type: 'dropdown', |  | ||||||
|       required: true, |  | ||||||
|       description: '', |  | ||||||
|       variables: false, |  | ||||||
|       source: { |  | ||||||
|         type: 'query', |  | ||||||
|         name: 'getDynamicData', |  | ||||||
|         arguments: [ |  | ||||||
|           { |  | ||||||
|             name: 'key', |  | ||||||
|             value: 'listOrganizations', |  | ||||||
|           }, |  | ||||||
|         ], |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|   ], |  | ||||||
|  |  | ||||||
|   async run($) { |  | ||||||
|     const dataItem = { |  | ||||||
|       raw: $.request.body, |  | ||||||
|       meta: { |  | ||||||
|         internalId: Crypto.randomUUID(), |  | ||||||
|       }, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     $.pushTriggerItem(dataItem); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   async testRun($) { |  | ||||||
|     const organizationId = $.step.parameters.organizationId; |  | ||||||
|  |  | ||||||
|     const sampleEventData = { |  | ||||||
|       ids: ['111111111111111111'], |  | ||||||
|       token: null, |  | ||||||
|       module: 'Calls', |  | ||||||
|       operation: 'insert', |  | ||||||
|       channel_id: organizationId, |  | ||||||
|       server_time: 1708426963120, |  | ||||||
|       query_params: {}, |  | ||||||
|       resource_uri: `${$.auth.data.apiDomain}/bigin/v1/Calls`, |  | ||||||
|       affected_fields: [], |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     const dataItem = { |  | ||||||
|       raw: sampleEventData, |  | ||||||
|       meta: { |  | ||||||
|         internalId: sampleEventData.channel_id, |  | ||||||
|       }, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     $.pushTriggerItem(dataItem); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   async registerHook($) { |  | ||||||
|     const organizationId = $.step.parameters.organizationId; |  | ||||||
|  |  | ||||||
|     const payload = { |  | ||||||
|       watch: [ |  | ||||||
|         { |  | ||||||
|           channel_id: organizationId, |  | ||||||
|           notify_url: $.webhookUrl, |  | ||||||
|           events: ['Calls.create'], |  | ||||||
|         }, |  | ||||||
|       ], |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     await $.http.post('/bigin/v2/actions/watch', payload); |  | ||||||
|  |  | ||||||
|     await $.flow.setRemoteWebhookId(organizationId); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   async unregisterHook($) { |  | ||||||
|     await $.http.delete( |  | ||||||
|       `/bigin/v2/actions/watch?channel_ids=${$.flow.remoteWebhookId}` |  | ||||||
|     ); |  | ||||||
|   }, |  | ||||||
| }); |  | ||||||
| @@ -1,89 +0,0 @@ | |||||||
| import Crypto from 'crypto'; |  | ||||||
| import defineTrigger from '../../../../helpers/define-trigger.js'; |  | ||||||
|  |  | ||||||
| export default defineTrigger({ |  | ||||||
|   name: 'New companies', |  | ||||||
|   key: 'newCompanies', |  | ||||||
|   type: 'webhook', |  | ||||||
|   description: 'Triggers when a new company is created.', |  | ||||||
|   arguments: [ |  | ||||||
|     { |  | ||||||
|       label: 'Organization', |  | ||||||
|       key: 'organizationId', |  | ||||||
|       type: 'dropdown', |  | ||||||
|       required: true, |  | ||||||
|       description: '', |  | ||||||
|       variables: false, |  | ||||||
|       source: { |  | ||||||
|         type: 'query', |  | ||||||
|         name: 'getDynamicData', |  | ||||||
|         arguments: [ |  | ||||||
|           { |  | ||||||
|             name: 'key', |  | ||||||
|             value: 'listOrganizations', |  | ||||||
|           }, |  | ||||||
|         ], |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|   ], |  | ||||||
|  |  | ||||||
|   async run($) { |  | ||||||
|     const dataItem = { |  | ||||||
|       raw: $.request.body, |  | ||||||
|       meta: { |  | ||||||
|         internalId: Crypto.randomUUID(), |  | ||||||
|       }, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     $.pushTriggerItem(dataItem); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   async testRun($) { |  | ||||||
|     const organizationId = $.step.parameters.organizationId; |  | ||||||
|  |  | ||||||
|     const sampleEventData = { |  | ||||||
|       ids: ['111111111111111111'], |  | ||||||
|       token: null, |  | ||||||
|       module: 'Accounts', |  | ||||||
|       operation: 'insert', |  | ||||||
|       channel_id: organizationId, |  | ||||||
|       server_time: 1708426963120, |  | ||||||
|       query_params: {}, |  | ||||||
|       resource_uri: `${$.auth.data.apiDomain}/bigin/v1/Accounts`, |  | ||||||
|       affected_fields: [], |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     const dataItem = { |  | ||||||
|       raw: sampleEventData, |  | ||||||
|       meta: { |  | ||||||
|         internalId: sampleEventData.channel_id, |  | ||||||
|       }, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     $.pushTriggerItem(dataItem); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   async registerHook($) { |  | ||||||
|     const organizationId = $.step.parameters.organizationId; |  | ||||||
|  |  | ||||||
|     const payload = { |  | ||||||
|       watch: [ |  | ||||||
|         { |  | ||||||
|           channel_id: organizationId, |  | ||||||
|           notify_url: $.webhookUrl, |  | ||||||
|           events: ['Accounts.create'], |  | ||||||
|         }, |  | ||||||
|       ], |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     await $.http.post('/bigin/v2/actions/watch', payload); |  | ||||||
|  |  | ||||||
|     await $.flow.setRemoteWebhookId(organizationId); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   async unregisterHook($) { |  | ||||||
|     await $.http.delete( |  | ||||||
|       `/bigin/v2/actions/watch?channel_ids=${$.flow.remoteWebhookId}` |  | ||||||
|     ); |  | ||||||
|   }, |  | ||||||
| }); |  | ||||||
| @@ -1,89 +0,0 @@ | |||||||
| import Crypto from 'crypto'; |  | ||||||
| import defineTrigger from '../../../../helpers/define-trigger.js'; |  | ||||||
|  |  | ||||||
| export default defineTrigger({ |  | ||||||
|   name: 'New contacts', |  | ||||||
|   key: 'newContacts', |  | ||||||
|   type: 'webhook', |  | ||||||
|   description: 'Triggers when a new contact is created.', |  | ||||||
|   arguments: [ |  | ||||||
|     { |  | ||||||
|       label: 'Organization', |  | ||||||
|       key: 'organizationId', |  | ||||||
|       type: 'dropdown', |  | ||||||
|       required: true, |  | ||||||
|       description: '', |  | ||||||
|       variables: false, |  | ||||||
|       source: { |  | ||||||
|         type: 'query', |  | ||||||
|         name: 'getDynamicData', |  | ||||||
|         arguments: [ |  | ||||||
|           { |  | ||||||
|             name: 'key', |  | ||||||
|             value: 'listOrganizations', |  | ||||||
|           }, |  | ||||||
|         ], |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|   ], |  | ||||||
|  |  | ||||||
|   async run($) { |  | ||||||
|     const dataItem = { |  | ||||||
|       raw: $.request.body, |  | ||||||
|       meta: { |  | ||||||
|         internalId: Crypto.randomUUID(), |  | ||||||
|       }, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     $.pushTriggerItem(dataItem); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   async testRun($) { |  | ||||||
|     const organizationId = $.step.parameters.organizationId; |  | ||||||
|  |  | ||||||
|     const sampleEventData = { |  | ||||||
|       ids: ['111111111111111111'], |  | ||||||
|       token: null, |  | ||||||
|       module: 'Contacts', |  | ||||||
|       operation: 'insert', |  | ||||||
|       channel_id: organizationId, |  | ||||||
|       server_time: 1708426963120, |  | ||||||
|       query_params: {}, |  | ||||||
|       resource_uri: `${$.auth.data.apiDomain}/bigin/v1/Contacts`, |  | ||||||
|       affected_fields: [], |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     const dataItem = { |  | ||||||
|       raw: sampleEventData, |  | ||||||
|       meta: { |  | ||||||
|         internalId: sampleEventData.channel_id, |  | ||||||
|       }, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     $.pushTriggerItem(dataItem); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   async registerHook($) { |  | ||||||
|     const organizationId = $.step.parameters.organizationId; |  | ||||||
|  |  | ||||||
|     const payload = { |  | ||||||
|       watch: [ |  | ||||||
|         { |  | ||||||
|           channel_id: organizationId, |  | ||||||
|           notify_url: $.webhookUrl, |  | ||||||
|           events: ['Contacts.create'], |  | ||||||
|         }, |  | ||||||
|       ], |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     await $.http.post('/bigin/v2/actions/watch', payload); |  | ||||||
|  |  | ||||||
|     await $.flow.setRemoteWebhookId(organizationId); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   async unregisterHook($) { |  | ||||||
|     await $.http.delete( |  | ||||||
|       `/bigin/v2/actions/watch?channel_ids=${$.flow.remoteWebhookId}` |  | ||||||
|     ); |  | ||||||
|   }, |  | ||||||
| }); |  | ||||||
| @@ -0,0 +1,27 @@ | |||||||
|  | import defineAction from '../../../../helpers/define-action.js'; | ||||||
|  |  | ||||||
|  | export default defineAction({ | ||||||
|  |   name: 'Get value', | ||||||
|  |   key: 'getValue', | ||||||
|  |   description: 'Get value from the persistent datastore.', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Key', | ||||||
|  |       key: 'key', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       description: 'The key of your value to get.', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const keyValuePair = await $.datastore.get({ | ||||||
|  |       key: $.step.parameters.key, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $.setActionItem({ | ||||||
|  |       raw: keyValuePair, | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
							
								
								
									
										4
									
								
								packages/backend/src/apps/datastore/actions/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								packages/backend/src/apps/datastore/actions/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | import getValue from './get-value/index.js'; | ||||||
|  | import setValue from './set-value/index.js'; | ||||||
|  |  | ||||||
|  | export default [getValue, setValue]; | ||||||
| @@ -0,0 +1,36 @@ | |||||||
|  | import defineAction from '../../../../helpers/define-action.js'; | ||||||
|  |  | ||||||
|  | export default defineAction({ | ||||||
|  |   name: 'Set value', | ||||||
|  |   key: 'setValue', | ||||||
|  |   description: 'Set value to the persistent datastore.', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Key', | ||||||
|  |       key: 'key', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       description: 'The key of your value to set.', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Value', | ||||||
|  |       key: 'value', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       description: 'The value to set.', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const keyValuePair = await $.datastore.set({ | ||||||
|  |       key: $.step.parameters.key, | ||||||
|  |       value: $.step.parameters.value, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $.setActionItem({ | ||||||
|  |       raw: keyValuePair, | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
							
								
								
									
										13
									
								
								packages/backend/src/apps/datastore/assets/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/backend/src/apps/datastore/assets/favicon.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | <?xml version="1.0"?> | ||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" id="icon"> | ||||||
|  |   <defs> | ||||||
|  |     <style>.cls-1{fill:none;}</style> | ||||||
|  |   </defs> | ||||||
|  |   <title>datastore</title> | ||||||
|  |   <circle cx="23" cy="23" r="1"/> | ||||||
|  |   <rect x="8" y="22" width="12" height="2"/> | ||||||
|  |   <circle cx="23" cy="9" r="1"/> | ||||||
|  |   <rect x="8" y="8" width="12" height="2"/> | ||||||
|  |   <path d="M26,14a2,2,0,0,0,2-2V6a2,2,0,0,0-2-2H6A2,2,0,0,0,4,6v6a2,2,0,0,0,2,2H8v4H6a2,2,0,0,0-2,2v6a2,2,0,0,0,2,2H26a2,2,0,0,0,2-2V20a2,2,0,0,0-2-2H24V14ZM6,6H26v6H6ZM26,26H6V20H26Zm-4-8H10V14H22Z"/> | ||||||
|  |   <rect id="_Transparent_Rectangle_" data-name="<Transparent Rectangle>" class="cls-1" width="32" height="32"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 704 B | 
							
								
								
									
										14
									
								
								packages/backend/src/apps/datastore/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								packages/backend/src/apps/datastore/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | import defineApp from '../../helpers/define-app.js'; | ||||||
|  | import actions from './actions/index.js'; | ||||||
|  |  | ||||||
|  | export default defineApp({ | ||||||
|  |   name: 'Datastore', | ||||||
|  |   key: 'datastore', | ||||||
|  |   iconUrl: '{BASE_URL}/apps/datastore/assets/favicon.svg', | ||||||
|  |   authDocUrl: 'https://automatisch.io/docs/apps/datastore/connection', | ||||||
|  |   supportsConnections: false, | ||||||
|  |   baseUrl: '', | ||||||
|  |   apiBaseUrl: '', | ||||||
|  |   primaryColor: '001F52', | ||||||
|  |   actions, | ||||||
|  | }); | ||||||
| @@ -1,4 +1,3 @@ | |||||||
| import FormData from 'form-data'; |  | ||||||
| import defineAction from '../../../../helpers/define-action.js'; | import defineAction from '../../../../helpers/define-action.js'; | ||||||
|  |  | ||||||
| export default defineAction({ | export default defineAction({ | ||||||
| @@ -6,45 +5,51 @@ export default defineAction({ | |||||||
|   key: 'newChat', |   key: 'newChat', | ||||||
|   description: 'Create a new chat session for Helix AI.', |   description: 'Create a new chat session for Helix AI.', | ||||||
|   arguments: [ |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Session ID', | ||||||
|  |       key: 'sessionId', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       description: | ||||||
|  |         'ID of the chat session to continue. Leave empty to start a new chat.', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'System Prompt', | ||||||
|  |       key: 'systemPrompt', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       description: | ||||||
|  |         'Optional system prompt to start the chat with. It will be used only for new chat sessions.', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|       label: 'Input', |       label: 'Input', | ||||||
|       key: 'input', |       key: 'input', | ||||||
|       type: 'string', |       type: 'string', | ||||||
|       required: true, |       required: true, | ||||||
|       description: 'Prompt to start the chat with.', |       description: 'User input to start the chat with.', | ||||||
|       variables: true, |       variables: true, | ||||||
|     }, |     }, | ||||||
|   ], |   ], | ||||||
|  |  | ||||||
|   async run($) { |   async run($) { | ||||||
|     const formData = new FormData(); |     const response = await $.http.post('/api/v1/sessions/chat', { | ||||||
|     formData.append('input', $.step.parameters.input); |       session_id: $.step.parameters.sessionId, | ||||||
|     formData.append('mode', 'inference'); |       system: $.step.parameters.systemPrompt, | ||||||
|     formData.append('type', 'text'); |       messages: [ | ||||||
|  |         { | ||||||
|     const sessionResponse = await $.http.post('/api/v1/sessions', formData, { |           role: 'user', | ||||||
|       headers: { |           content: { | ||||||
|         ...formData.getHeaders(), |             content_type: 'text', | ||||||
|       }, |             parts: [$.step.parameters.input], | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     const sessionId = sessionResponse.data.id; |     $.setActionItem({ | ||||||
|  |       raw: response.data, | ||||||
|     let chatGenerated = false; |     }); | ||||||
|  |  | ||||||
|     while (!chatGenerated) { |  | ||||||
|       const response = await $.http.get(`/api/v1/sessions/${sessionId}`); |  | ||||||
|  |  | ||||||
|       const message = |  | ||||||
|         response.data.interactions[response.data.interactions.length - 1]; |  | ||||||
|  |  | ||||||
|       if (message.creator === 'system' && message.state === 'complete') { |  | ||||||
|         $.setActionItem({ |  | ||||||
|           raw: message, |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         chatGenerated = true; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -90,7 +90,7 @@ export default defineAction({ | |||||||
|  |  | ||||||
|   async run($) { |   async run($) { | ||||||
|     const method = $.step.parameters.method; |     const method = $.step.parameters.method; | ||||||
|     const data = $.step.parameters.data; |     const data = $.step.parameters.data || null; | ||||||
|     const url = $.step.parameters.url; |     const url = $.step.parameters.url; | ||||||
|     const headers = $.step.parameters.headers; |     const headers = $.step.parameters.headers; | ||||||
|  |  | ||||||
| @@ -108,14 +108,17 @@ export default defineAction({ | |||||||
|       return result; |       return result; | ||||||
|     }, {}); |     }, {}); | ||||||
|  |  | ||||||
|     let contentType = headersObject['content-type']; |     let expectedResponseContentType = headersObject.accept; | ||||||
|  |  | ||||||
|     // in case HEAD request is not supported by the URL |     // in case HEAD request is not supported by the URL | ||||||
|     try { |     try { | ||||||
|       const metadataResponse = await $.http.head(url, { |       const metadataResponse = await $.http.head(url, { | ||||||
|         headers: headersObject, |         headers: headersObject, | ||||||
|       }); |       }); | ||||||
|       contentType = metadataResponse.headers['content-type']; |  | ||||||
|  |       if (!expectedResponseContentType) { | ||||||
|  |         expectedResponseContentType = metadataResponse.headers['content-type']; | ||||||
|  |       } | ||||||
|  |  | ||||||
|       throwIfFileSizeExceedsLimit(metadataResponse.headers['content-length']); |       throwIfFileSizeExceedsLimit(metadataResponse.headers['content-length']); | ||||||
|       // eslint-disable-next-line no-empty |       // eslint-disable-next-line no-empty | ||||||
| @@ -128,7 +131,7 @@ export default defineAction({ | |||||||
|       headers: headersObject, |       headers: headersObject, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if (!isPossiblyTextBased(contentType)) { |     if (!isPossiblyTextBased(expectedResponseContentType)) { | ||||||
|       requestData.responseType = 'arraybuffer'; |       requestData.responseType = 'arraybuffer'; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -138,7 +141,7 @@ export default defineAction({ | |||||||
|  |  | ||||||
|     let responseData = response.data; |     let responseData = response.data; | ||||||
|  |  | ||||||
|     if (!isPossiblyTextBased(contentType)) { |     if (!isPossiblyTextBased(expectedResponseContentType)) { | ||||||
|       responseData = Buffer.from(responseData).toString('base64'); |       responseData = Buffer.from(responseData).toString('base64'); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
| import createContact from './create-contact/index.js'; | import createContact from './create-contact/index.js'; | ||||||
|  | import updateContact from './update-contact/index.js'; | ||||||
|  |  | ||||||
| export default [createContact]; | export default [createContact, updateContact]; | ||||||
|   | |||||||
| @@ -0,0 +1,96 @@ | |||||||
|  | import defineAction from '../../../../helpers/define-action.js'; | ||||||
|  |  | ||||||
|  | export default defineAction({ | ||||||
|  |   name: 'Update contact', | ||||||
|  |   key: 'updateContact', | ||||||
|  |   description: `Updates an existing contact on user's account.`, | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Contact ID', | ||||||
|  |       key: 'contactId', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Company name', | ||||||
|  |       key: 'company', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Email', | ||||||
|  |       key: 'email', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'First name', | ||||||
|  |       key: 'firstName', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Last name', | ||||||
|  |       key: 'lastName', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       description: 'Last name', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Phone', | ||||||
|  |       key: 'phone', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Website URL', | ||||||
|  |       key: 'website', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Owner ID', | ||||||
|  |       key: 'hubspotOwnerId', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const { | ||||||
|  |       contactId, | ||||||
|  |       company, | ||||||
|  |       email, | ||||||
|  |       firstName, | ||||||
|  |       lastName, | ||||||
|  |       phone, | ||||||
|  |       website, | ||||||
|  |       hubspotOwnerId, | ||||||
|  |     } = $.step.parameters; | ||||||
|  |  | ||||||
|  |     const response = await $.http.patch( | ||||||
|  |       `crm/v3/objects/contacts/${contactId}`, | ||||||
|  |       { | ||||||
|  |         properties: { | ||||||
|  |           company, | ||||||
|  |           email, | ||||||
|  |           firstname: firstName, | ||||||
|  |           lastname: lastName, | ||||||
|  |           phone, | ||||||
|  |           website, | ||||||
|  |           hubspot_owner_id: hubspotOwnerId, | ||||||
|  |         }, | ||||||
|  |       } | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     $.setActionItem({ raw: response.data }); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
| @@ -64,32 +64,17 @@ export default defineAction({ | |||||||
|       value: '1', |       value: '1', | ||||||
|       description: |       description: | ||||||
|         'The ID of the stage this deal will be added to. If omitted, the deal will be placed in the first stage of the default pipeline.', |         'The ID of the stage this deal will be added to. If omitted, the deal will be placed in the first stage of the default pipeline.', | ||||||
|       options: [ |       variables: true, | ||||||
|         { |       source: { | ||||||
|           label: 'Qualified (Pipeline)', |         type: 'query', | ||||||
|           value: 1, |         name: 'getDynamicData', | ||||||
|         }, |         arguments: [ | ||||||
|         { |           { | ||||||
|           label: 'Contact Made (Pipeline)', |             name: 'key', | ||||||
|           value: 2, |             value: 'listStages', | ||||||
|         }, |           }, | ||||||
|         { |         ], | ||||||
|           label: 'Prospect Qualified (Pipeline)', |       }, | ||||||
|           value: 3, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|           label: 'Needs Defined (Pipeline)', |  | ||||||
|           value: 4, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|           label: 'Proposal Made (Pipeline)', |  | ||||||
|           value: 5, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|           label: 'Negotiations Started (Pipeline)', |  | ||||||
|           value: 6, |  | ||||||
|         }, |  | ||||||
|       ], |  | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       label: 'Owner', |       label: 'Owner', | ||||||
|   | |||||||
| @@ -1,23 +1,25 @@ | |||||||
| import listActivityTypes from './list-activity-types/index.js'; | import listActivityTypes from './list-activity-types/index.js'; | ||||||
| import listCurrencies from './list-currencies/index.js'; | import listCurrencies from './list-currencies/index.js'; | ||||||
| import listDeals from './list-deals/index.js'; | import listDeals from './list-deals/index.js'; | ||||||
| import listLeads from './list-leads/index.js'; |  | ||||||
| import listLeadLabels from './list-lead-labels/index.js'; | import listLeadLabels from './list-lead-labels/index.js'; | ||||||
| import listOrganizations from './list-organizations/index.js'; | import listLeads from './list-leads/index.js'; | ||||||
| import listOrganizationLabelField from './list-organization-label-field/index.js'; | import listOrganizationLabelField from './list-organization-label-field/index.js'; | ||||||
|  | import listOrganizations from './list-organizations/index.js'; | ||||||
| import listPersonLabelField from './list-person-label-field/index.js'; | import listPersonLabelField from './list-person-label-field/index.js'; | ||||||
| import listPersons from './list-persons/index.js'; | import listPersons from './list-persons/index.js'; | ||||||
|  | import listStages from './list-stages/index.js'; | ||||||
| import listUsers from './list-users/index.js'; | import listUsers from './list-users/index.js'; | ||||||
|  |  | ||||||
| export default [ | export default [ | ||||||
|   listActivityTypes, |   listActivityTypes, | ||||||
|   listCurrencies, |   listCurrencies, | ||||||
|   listDeals, |   listDeals, | ||||||
|   listLeads, |  | ||||||
|   listLeadLabels, |   listLeadLabels, | ||||||
|   listOrganizations, |   listLeads, | ||||||
|   listOrganizationLabelField, |   listOrganizationLabelField, | ||||||
|  |   listOrganizations, | ||||||
|   listPersonLabelField, |   listPersonLabelField, | ||||||
|   listPersons, |   listPersons, | ||||||
|  |   listStages, | ||||||
|   listUsers, |   listUsers, | ||||||
| ]; | ]; | ||||||
|   | |||||||
| @@ -0,0 +1,23 @@ | |||||||
|  | export default { | ||||||
|  |   name: 'List stages', | ||||||
|  |   key: 'listStages', | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const stages = { | ||||||
|  |       data: [], | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const { data } = await $.http.get('/api/v1/stages'); | ||||||
|  |  | ||||||
|  |     if (data.data?.length) { | ||||||
|  |       for (const stage of data.data) { | ||||||
|  |         stages.data.push({ | ||||||
|  |           value: stage.id, | ||||||
|  |           name: stage.name, | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return stages; | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										3
									
								
								packages/backend/src/apps/webhook/actions/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/backend/src/apps/webhook/actions/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | import respondWith from './respond-with/index.js'; | ||||||
|  |  | ||||||
|  | export default [respondWith]; | ||||||
| @@ -0,0 +1,69 @@ | |||||||
|  | import defineAction from '../../../../helpers/define-action.js'; | ||||||
|  |  | ||||||
|  | export default defineAction({ | ||||||
|  |   name: 'Respond with', | ||||||
|  |   key: 'respondWith', | ||||||
|  |   description: 'Respond with defined JSON body.', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Status code', | ||||||
|  |       key: 'statusCode', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       variables: true, | ||||||
|  |       value: '200', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Headers', | ||||||
|  |       key: 'headers', | ||||||
|  |       type: 'dynamic', | ||||||
|  |       required: false, | ||||||
|  |       description: 'Add or remove headers as needed', | ||||||
|  |       fields: [ | ||||||
|  |         { | ||||||
|  |           label: 'Key', | ||||||
|  |           key: 'key', | ||||||
|  |           type: 'string', | ||||||
|  |           required: true, | ||||||
|  |           description: 'Header key', | ||||||
|  |           variables: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           label: 'Value', | ||||||
|  |           key: 'value', | ||||||
|  |           type: 'string', | ||||||
|  |           required: true, | ||||||
|  |           description: 'Header value', | ||||||
|  |           variables: true, | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Body', | ||||||
|  |       key: 'body', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       description: 'The content of the response body.', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const statusCode = parseInt($.step.parameters.statusCode, 10); | ||||||
|  |     const body = $.step.parameters.body; | ||||||
|  |     const headers = $.step.parameters.headers.reduce((result, entry) => { | ||||||
|  |       return { | ||||||
|  |         ...result, | ||||||
|  |         [entry.key]: entry.value, | ||||||
|  |       }; | ||||||
|  |     }, {}); | ||||||
|  |  | ||||||
|  |     $.setActionItem({ | ||||||
|  |       raw: { | ||||||
|  |         headers, | ||||||
|  |         body, | ||||||
|  |         statusCode, | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
| @@ -1,4 +1,5 @@ | |||||||
| import defineApp from '../../helpers/define-app.js'; | import defineApp from '../../helpers/define-app.js'; | ||||||
|  | import actions from './actions/index.js'; | ||||||
| import triggers from './triggers/index.js'; | import triggers from './triggers/index.js'; | ||||||
|  |  | ||||||
| export default defineApp({ | export default defineApp({ | ||||||
| @@ -10,5 +11,6 @@ export default defineApp({ | |||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   primaryColor: '0059F7', |   primaryColor: '0059F7', | ||||||
|  |   actions, | ||||||
|   triggers, |   triggers, | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -7,7 +7,20 @@ export default defineTrigger({ | |||||||
|   key: 'catchRawWebhook', |   key: 'catchRawWebhook', | ||||||
|   type: 'webhook', |   type: 'webhook', | ||||||
|   showWebhookUrl: true, |   showWebhookUrl: true, | ||||||
|   description: 'Triggers when the webhook receives a request.', |   description: | ||||||
|  |     'Triggers (immediately if configured) when the webhook receives a request.', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Wait until flow is done', | ||||||
|  |       key: 'workSynchronously', | ||||||
|  |       type: 'dropdown', | ||||||
|  |       required: true, | ||||||
|  |       options: [ | ||||||
|  |         { label: 'Yes', value: true }, | ||||||
|  |         { label: 'No', value: false }, | ||||||
|  |       ], | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|   async run($) { |   async run($) { | ||||||
|     const dataItem = { |     const dataItem = { | ||||||
|   | |||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | import User from '../../../../models/user.js'; | ||||||
|  | import { renderObject, renderError } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const { email, password } = request.body; | ||||||
|  |   const token = await User.authenticate(email, password); | ||||||
|  |  | ||||||
|  |   if (token) { | ||||||
|  |     return renderObject(response, { token }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   renderError(response, [{ general: ['Incorrect email or password.'] }]); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,39 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user'; | ||||||
|  |  | ||||||
|  | describe('POST /api/v1/access-tokens', () => { | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     await createUser({ | ||||||
|  |       email: 'user@automatisch.io', | ||||||
|  |       password: 'password', | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the token data with correct credentials', async () => { | ||||||
|  |     const response = await request(app) | ||||||
|  |       .post('/api/v1/access-tokens') | ||||||
|  |       .send({ | ||||||
|  |         email: 'user@automatisch.io', | ||||||
|  |         password: 'password', | ||||||
|  |       }) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     expect(response.body.data.token.length).toBeGreaterThan(0); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return error with incorrect credentials', async () => { | ||||||
|  |     const response = await request(app) | ||||||
|  |       .post('/api/v1/access-tokens') | ||||||
|  |       .send({ | ||||||
|  |         email: 'incorrect@email.com', | ||||||
|  |         password: 'incorrectpassword', | ||||||
|  |       }) | ||||||
|  |       .expect(422); | ||||||
|  |  | ||||||
|  |     expect(response.body.errors.general).toEqual([ | ||||||
|  |       'Incorrect email or password.', | ||||||
|  |     ]); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,11 @@ | |||||||
|  | import { renderObject } from '../../../../../helpers/renderer.js'; | ||||||
|  | import AppAuthClient from '../../../../../models/app-auth-client.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const appAuthClient = await AppAuthClient.query() | ||||||
|  |     .findById(request.params.appAuthClientId) | ||||||
|  |     .where({ app_key: request.params.appKey }) | ||||||
|  |     .throwIfNotFound(); | ||||||
|  |  | ||||||
|  |   renderObject(response, appAuthClient); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,55 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import Crypto from 'crypto'; | ||||||
|  | import app from '../../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createUser } from '../../../../../../test/factories/user.js'; | ||||||
|  | import { createRole } from '../../../../../../test/factories/role.js'; | ||||||
|  | import getAppAuthClientMock from '../../../../../../test/mocks/rest/api/v1/admin/apps/get-auth-client.js'; | ||||||
|  | import { createAppAuthClient } from '../../../../../../test/factories/app-auth-client.js'; | ||||||
|  | import * as license from '../../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/admin/apps/:appKey/auth-clients/:appAuthClientId', () => { | ||||||
|  |   let currentUser, adminRole, currentAppAuthClient, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     adminRole = await createRole({ key: 'admin' }); | ||||||
|  |     currentUser = await createUser({ roleId: adminRole.id }); | ||||||
|  |  | ||||||
|  |     currentAppAuthClient = await createAppAuthClient({ | ||||||
|  |       appKey: 'deepl', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return specified app auth client', async () => { | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/admin/apps/deepl/auth-clients/${currentAppAuthClient.id}`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getAppAuthClientMock(currentAppAuthClient); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for not existing app auth client ID', async () => { | ||||||
|  |     const notExistingAppAuthClientUUID = Crypto.randomUUID(); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .get( | ||||||
|  |         `/api/v1/admin/apps/deepl/auth-clients/${notExistingAppAuthClientUUID}` | ||||||
|  |       ) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return bad request response for invalid UUID', async () => { | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/admin/apps/deepl/auth-clients/invalidAppAuthClientUUID') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(400); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,10 @@ | |||||||
|  | import { renderObject } from '../../../../../helpers/renderer.js'; | ||||||
|  | import AppAuthClient from '../../../../../models/app-auth-client.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const appAuthClients = await AppAuthClient.query() | ||||||
|  |     .where({ app_key: request.params.appKey }) | ||||||
|  |     .orderBy('created_at', 'desc'); | ||||||
|  |  | ||||||
|  |   renderObject(response, appAuthClients); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,44 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createUser } from '../../../../../../test/factories/user.js'; | ||||||
|  | import { createRole } from '../../../../../../test/factories/role.js'; | ||||||
|  | import getAuthClientsMock from '../../../../../../test/mocks/rest/api/v1/admin/apps/get-auth-clients.js'; | ||||||
|  | import { createAppAuthClient } from '../../../../../../test/factories/app-auth-client.js'; | ||||||
|  | import * as license from '../../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/admin/apps/:appKey/auth-clients', () => { | ||||||
|  |   let currentUser, adminRole, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     adminRole = await createRole({ key: 'admin' }); | ||||||
|  |     currentUser = await createUser({ roleId: adminRole.id }); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return specified app auth client info', async () => { | ||||||
|  |     const appAuthClientOne = await createAppAuthClient({ | ||||||
|  |       appKey: 'deepl', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const appAuthClientTwo = await createAppAuthClient({ | ||||||
|  |       appKey: 'deepl', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/admin/apps/deepl/auth-clients') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getAuthClientsMock([ | ||||||
|  |       appAuthClientTwo, | ||||||
|  |       appAuthClientOne, | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,6 @@ | |||||||
|  | import { renderObject } from '../../../../../helpers/renderer.js'; | ||||||
|  | import permissionCatalog from '../../../../../helpers/permission-catalog.ee.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   renderObject(response, permissionCatalog); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,32 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createRole } from '../../../../../../test/factories/role.js'; | ||||||
|  | import { createUser } from '../../../../../../test/factories/user.js'; | ||||||
|  | import getPermissionsCatalogMock from '../../../../../../test/mocks/rest/api/v1/admin/permissions/get-permissions-catalog.ee.js'; | ||||||
|  | import * as license from '../../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/admin/permissions/catalog', () => { | ||||||
|  |   let role, currentUser, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     role = await createRole({ key: 'admin' }); | ||||||
|  |     currentUser = await createUser({ roleId: role.id }); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return roles', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/admin/permissions/catalog') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getPermissionsCatalogMock(); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,16 @@ | |||||||
|  | import { renderObject } from '../../../../../helpers/renderer.js'; | ||||||
|  | import Role from '../../../../../models/role.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const role = await Role.query() | ||||||
|  |     .leftJoinRelated({ | ||||||
|  |       permissions: true, | ||||||
|  |     }) | ||||||
|  |     .withGraphFetched({ | ||||||
|  |       permissions: true, | ||||||
|  |     }) | ||||||
|  |     .findById(request.params.roleId) | ||||||
|  |     .throwIfNotFound(); | ||||||
|  |  | ||||||
|  |   renderObject(response, role); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,59 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import Crypto from 'crypto'; | ||||||
|  | import app from '../../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createRole } from '../../../../../../test/factories/role.js'; | ||||||
|  | import { createUser } from '../../../../../../test/factories/user.js'; | ||||||
|  | import { createPermission } from '../../../../../../test/factories/permission.js'; | ||||||
|  | import getRoleMock from '../../../../../../test/mocks/rest/api/v1/admin/roles/get-role.ee.js'; | ||||||
|  | import * as license from '../../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/admin/roles/:roleId', () => { | ||||||
|  |   let role, currentUser, token, permissionOne, permissionTwo; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     role = await createRole({ key: 'admin' }); | ||||||
|  |     permissionOne = await createPermission({ roleId: role.id }); | ||||||
|  |     permissionTwo = await createPermission({ roleId: role.id }); | ||||||
|  |     currentUser = await createUser({ roleId: role.id }); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return role', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/admin/roles/${role.id}`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getRoleMock(role, [ | ||||||
|  |       permissionOne, | ||||||
|  |       permissionTwo, | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for not existing role UUID', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const notExistingRoleUUID = Crypto.randomUUID(); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .get(`/api/v1/admin/roles/${notExistingRoleUUID}`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return bad request response for invalid UUID', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/admin/roles/invalidRoleUUID') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(400); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | import { renderObject } from '../../../../../helpers/renderer.js'; | ||||||
|  | import Role from '../../../../../models/role.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const roles = await Role.query().orderBy('name'); | ||||||
|  |  | ||||||
|  |   renderObject(response, roles); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,33 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createRole } from '../../../../../../test/factories/role.js'; | ||||||
|  | import { createUser } from '../../../../../../test/factories/user.js'; | ||||||
|  | import getRolesMock from '../../../../../../test/mocks/rest/api/v1/admin/roles/get-roles.ee.js'; | ||||||
|  | import * as license from '../../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/admin/roles', () => { | ||||||
|  |   let roleOne, roleTwo, currentUser, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     roleOne = await createRole({ key: 'admin' }); | ||||||
|  |     roleTwo = await createRole({ key: 'user' }); | ||||||
|  |     currentUser = await createUser({ roleId: roleOne.id }); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return roles', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/admin/roles') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getRolesMock([roleOne, roleTwo]); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,14 @@ | |||||||
|  | import { renderObject } from '../../../../../helpers/renderer.js'; | ||||||
|  | import SamlAuthProvider from '../../../../../models/saml-auth-provider.ee.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const samlAuthProvider = await SamlAuthProvider.query() | ||||||
|  |     .findById(request.params.samlAuthProviderId) | ||||||
|  |     .throwIfNotFound(); | ||||||
|  |  | ||||||
|  |   const roleMappings = await samlAuthProvider | ||||||
|  |     .$relatedQuery('samlAuthProvidersRoleMappings') | ||||||
|  |     .orderBy('remote_role_name', 'asc'); | ||||||
|  |  | ||||||
|  |   renderObject(response, roleMappings); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,51 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createRole } from '../../../../../../test/factories/role.js'; | ||||||
|  | import { createUser } from '../../../../../../test/factories/user.js'; | ||||||
|  | import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js'; | ||||||
|  | import { createRoleMapping } from '../../../../../../test/factories/role-mapping.js'; | ||||||
|  | import getRoleMappingsMock from '../../../../../../test/mocks/rest/api/v1/admin/saml-auth-providers/get-role-mappings.ee.js'; | ||||||
|  | import * as license from '../../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/admin/saml-auth-providers/:samlAuthProviderId/role-mappings', () => { | ||||||
|  |   let roleMappingOne, roleMappingTwo, samlAuthProvider, currentUser, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     const role = await createRole({ key: 'admin' }); | ||||||
|  |     currentUser = await createUser({ roleId: role.id }); | ||||||
|  |  | ||||||
|  |     samlAuthProvider = await createSamlAuthProvider(); | ||||||
|  |  | ||||||
|  |     roleMappingOne = await createRoleMapping({ | ||||||
|  |       samlAuthProviderId: samlAuthProvider.id, | ||||||
|  |       remoteRoleName: 'Admin', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     roleMappingTwo = await createRoleMapping({ | ||||||
|  |       samlAuthProviderId: samlAuthProvider.id, | ||||||
|  |       remoteRoleName: 'User', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return role mappings', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get( | ||||||
|  |         `/api/v1/admin/saml-auth-providers/${samlAuthProvider.id}/role-mappings` | ||||||
|  |       ) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getRoleMappingsMock([ | ||||||
|  |       roleMappingOne, | ||||||
|  |       roleMappingTwo, | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | import { renderObject } from '../../../../../helpers/renderer.js'; | ||||||
|  | import SamlAuthProvider from '../../../../../models/saml-auth-provider.ee.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const samlAuthProvider = await SamlAuthProvider.query() | ||||||
|  |     .findById(request.params.samlAuthProviderId) | ||||||
|  |     .throwIfNotFound(); | ||||||
|  |  | ||||||
|  |   renderObject(response, samlAuthProvider, { | ||||||
|  |     serializer: 'AdminSamlAuthProvider', | ||||||
|  |   }); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,57 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import Crypto from 'crypto'; | ||||||
|  | import app from '../../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createRole } from '../../../../../../test/factories/role.js'; | ||||||
|  | import { createUser } from '../../../../../../test/factories/user.js'; | ||||||
|  | import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js'; | ||||||
|  | import getSamlAuthProviderMock from '../../../../../../test/mocks/rest/api/v1/admin/saml-auth-providers/get-saml-auth-provider.ee.js'; | ||||||
|  | import * as license from '../../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/admin/saml-auth-provider/:samlAuthProviderId', () => { | ||||||
|  |   let samlAuthProvider, currentUser, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     const role = await createRole({ key: 'admin' }); | ||||||
|  |     currentUser = await createUser({ roleId: role.id }); | ||||||
|  |     samlAuthProvider = await createSamlAuthProvider(); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return saml auth provider with specified id', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/admin/saml-auth-providers/${samlAuthProvider.id}`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getSamlAuthProviderMock(samlAuthProvider); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for not existing saml auth provider UUID', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const notExistingSamlAuthProviderUUID = Crypto.randomUUID(); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .get( | ||||||
|  |         `/api/v1/admin/saml-auth-providers/${notExistingSamlAuthProviderUUID}` | ||||||
|  |       ) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return bad request response for invalid UUID', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/admin/saml-auth-providers/invalidSamlAuthProviderUUID') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(400); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | import { renderObject } from '../../../../../helpers/renderer.js'; | ||||||
|  | import SamlAuthProvider from '../../../../../models/saml-auth-provider.ee.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const samlAuthProviders = await SamlAuthProvider.query().orderBy( | ||||||
|  |     'created_at', | ||||||
|  |     'desc' | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   renderObject(response, samlAuthProviders, { | ||||||
|  |     serializer: 'AdminSamlAuthProvider', | ||||||
|  |   }); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,39 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createRole } from '../../../../../../test/factories/role.js'; | ||||||
|  | import { createUser } from '../../../../../../test/factories/user.js'; | ||||||
|  | import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js'; | ||||||
|  | import getSamlAuthProvidersMock from '../../../../../../test/mocks/rest/api/v1/admin/saml-auth-providers/get-saml-auth-providers.ee.js'; | ||||||
|  | import * as license from '../../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/admin/saml-auth-providers', () => { | ||||||
|  |   let samlAuthProviderOne, samlAuthProviderTwo, currentUser, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     const role = await createRole({ key: 'admin' }); | ||||||
|  |     currentUser = await createUser({ roleId: role.id }); | ||||||
|  |  | ||||||
|  |     samlAuthProviderOne = await createSamlAuthProvider(); | ||||||
|  |     samlAuthProviderTwo = await createSamlAuthProvider(); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return saml auth providers', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/admin/saml-auth-providers') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getSamlAuthProvidersMock([ | ||||||
|  |       samlAuthProviderTwo, | ||||||
|  |       samlAuthProviderOne, | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -1,11 +1,8 @@ | |||||||
| import { renderObject } from '../../../../helpers/renderer.js'; | import { renderObject } from '../../../../../helpers/renderer.js'; | ||||||
| import User from '../../../../models/user.js'; | import User from '../../../../../models/user.js'; | ||||||
| 
 | 
 | ||||||
| export default async (request, response) => { | export default async (request, response) => { | ||||||
|   const user = await User.query() |   const user = await User.query() | ||||||
|     .leftJoinRelated({ |  | ||||||
|       role: true, |  | ||||||
|     }) |  | ||||||
|     .withGraphFetched({ |     .withGraphFetched({ | ||||||
|       role: true, |       role: true, | ||||||
|     }) |     }) | ||||||
| @@ -0,0 +1,55 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import Crypto from 'crypto'; | ||||||
|  | import app from '../../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id'; | ||||||
|  | import { createUser } from '../../../../../../test/factories/user'; | ||||||
|  | import { createRole } from '../../../../../../test/factories/role'; | ||||||
|  | import getUserMock from '../../../../../../test/mocks/rest/api/v1/admin/users/get-user.js'; | ||||||
|  | import * as license from '../../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/admin/users/:userId', () => { | ||||||
|  |   let currentUser, currentUserRole, anotherUser, anotherUserRole, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUserRole = await createRole({ key: 'admin' }); | ||||||
|  |     currentUser = await createUser({ roleId: currentUserRole.id }); | ||||||
|  |  | ||||||
|  |     anotherUser = await createUser(); | ||||||
|  |     anotherUserRole = await anotherUser.$relatedQuery('role'); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return specified user info', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/admin/users/${anotherUser.id}`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getUserMock(anotherUser, anotherUserRole); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for not existing user UUID', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const notExistingUserUUID = Crypto.randomUUID(); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .get(`/api/v1/admin/users/${notExistingUserUUID}`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return bad request response for invalid UUID', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/admin/users/invalidUserUUID') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(400); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -1,12 +1,9 @@ | |||||||
| import { renderObject } from '../../../../helpers/renderer.js'; | import { renderObject } from '../../../../../helpers/renderer.js'; | ||||||
| import User from '../../../../models/user.js'; | import User from '../../../../../models/user.js'; | ||||||
| import paginateRest from '../../../../helpers/pagination-rest.js'; | import paginateRest from '../../../../../helpers/pagination-rest.js'; | ||||||
| 
 | 
 | ||||||
| export default async (request, response) => { | export default async (request, response) => { | ||||||
|   const usersQuery = User.query() |   const usersQuery = User.query() | ||||||
|     .leftJoinRelated({ |  | ||||||
|       role: true, |  | ||||||
|     }) |  | ||||||
|     .withGraphFetched({ |     .withGraphFetched({ | ||||||
|       role: true, |       role: true, | ||||||
|     }) |     }) | ||||||
| @@ -1,26 +1,17 @@ | |||||||
| import { describe, it, expect, beforeEach } from 'vitest'; | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
| import request from 'supertest'; | import request from 'supertest'; | ||||||
| import app from '../../../../app'; | import app from '../../../../../app'; | ||||||
| import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id'; | import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id'; | ||||||
| import { createRole } from '../../../../../test/factories/role'; | import { createRole } from '../../../../../../test/factories/role'; | ||||||
| import { createPermission } from '../../../../../test/factories/permission'; | import { createUser } from '../../../../../../test/factories/user'; | ||||||
| import { createUser } from '../../../../../test/factories/user'; | import getUsersMock from '../../../../../../test/mocks/rest/api/v1/admin/users/get-users.js'; | ||||||
| import getUsersMock from '../../../../../test/mocks/rest/api/v1/users/get-users'; | import * as license from '../../../../../helpers/license.ee.js'; | ||||||
| 
 | 
 | ||||||
| describe('GET /api/v1/users', () => { | describe('GET /api/v1/admin/users', () => { | ||||||
|   let currentUser, currentUserRole, anotherUser, anotherUserRole, token; |   let currentUser, currentUserRole, anotherUser, anotherUserRole, token; | ||||||
| 
 | 
 | ||||||
|   beforeEach(async () => { |   beforeEach(async () => { | ||||||
|     currentUserRole = await createRole({ |     currentUserRole = await createRole({ key: 'admin' }); | ||||||
|       key: 'currentUser', |  | ||||||
|       name: 'Current user role', |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     await createPermission({ |  | ||||||
|       action: 'read', |  | ||||||
|       subject: 'User', |  | ||||||
|       roleId: currentUserRole.id, |  | ||||||
|     }); |  | ||||||
| 
 | 
 | ||||||
|     currentUser = await createUser({ |     currentUser = await createUser({ | ||||||
|       roleId: currentUserRole.id, |       roleId: currentUserRole.id, | ||||||
| @@ -41,8 +32,10 @@ describe('GET /api/v1/users', () => { | |||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   it('should return users data', async () => { |   it('should return users data', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  | 
 | ||||||
|     const response = await request(app) |     const response = await request(app) | ||||||
|       .get('/api/v1/users') |       .get('/api/v1/admin/users') | ||||||
|       .set('Authorization', token) |       .set('Authorization', token) | ||||||
|       .expect(200); |       .expect(200); | ||||||
| 
 | 
 | ||||||
| @@ -0,0 +1,11 @@ | |||||||
|  | import App from '../../../../models/app.js'; | ||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const substeps = await App.findActionSubsteps( | ||||||
|  |     request.params.appKey, | ||||||
|  |     request.params.actionKey | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   renderObject(response, substeps); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,52 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import App from '../../../../models/app'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user'; | ||||||
|  | import getActionSubstepsMock from '../../../../../test/mocks/rest/api/v1/apps/get-action-substeps.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps/:appKey/actions/:actionKey/substeps', () => { | ||||||
|  |   let currentUser, exampleApp, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |     exampleApp = await App.findOneByKey('github'); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the app auth info', async () => { | ||||||
|  |     const actions = await App.findActionsByKey('github'); | ||||||
|  |     const exampleAction = actions.find( | ||||||
|  |       (action) => action.key === 'createIssue' | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     const endpointUrl = `/api/v1/apps/${exampleApp.key}/actions/${exampleAction.key}/substeps`; | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(endpointUrl) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getActionSubstepsMock(exampleAction.substeps); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for invalid app key', async () => { | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/apps/invalid-app-key/actions/invalid-actions-key/substeps') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return empty array for invalid action key', async () => { | ||||||
|  |     const endpointUrl = `/api/v1/apps/${exampleApp.key}/actions/invalid-action-key/substeps`; | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(endpointUrl) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     expect(response.body.data).toEqual([]); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | import App from '../../../../models/app.js'; | ||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const actions = await App.findActionsByKey(request.params.appKey); | ||||||
|  |  | ||||||
|  |   renderObject(response, actions, { serializer: 'Action' }); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,35 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import App from '../../../../models/app'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user'; | ||||||
|  | import getActionsMock from '../../../../../test/mocks/rest/api/v1/apps/get-actions.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps/:appKey/actions', () => { | ||||||
|  |   let currentUser, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the app actions', async () => { | ||||||
|  |     const exampleApp = await App.findOneByKey('github'); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/apps/${exampleApp.key}/actions`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getActionsMock(exampleApp.actions); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for invalid app key', async () => { | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/apps/invalid-app-key/actions') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										8
									
								
								packages/backend/src/controllers/api/v1/apps/get-app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/backend/src/controllers/api/v1/apps/get-app.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | import App from '../../../../models/app.js'; | ||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const app = await App.findOneByKey(request.params.appKey); | ||||||
|  |  | ||||||
|  |   renderObject(response, app, { serializer: 'App' }); | ||||||
|  | }; | ||||||
							
								
								
									
										35
									
								
								packages/backend/src/controllers/api/v1/apps/get-app.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								packages/backend/src/controllers/api/v1/apps/get-app.test.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import App from '../../../../models/app'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user'; | ||||||
|  | import getAppMock from '../../../../../test/mocks/rest/api/v1/apps/get-app.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps/:appKey', () => { | ||||||
|  |   let currentUser, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the app info', async () => { | ||||||
|  |     const exampleApp = await App.findOneByKey('github'); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/apps/${exampleApp.key}`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getAppMock(exampleApp); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for invalid app key', async () => { | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/apps/invalid-app-key') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										16
									
								
								packages/backend/src/controllers/api/v1/apps/get-apps.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								packages/backend/src/controllers/api/v1/apps/get-apps.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | import App from '../../../../models/app.js'; | ||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   let apps = await App.findAll(request.query.name); | ||||||
|  |  | ||||||
|  |   if (request.query.onlyWithTriggers) { | ||||||
|  |     apps = apps.filter((app) => app.triggers?.length); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (request.query.onlyWithActions) { | ||||||
|  |     apps = apps.filter((app) => app.actions?.length); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   renderObject(response, apps, { serializer: 'App' }); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,63 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import App from '../../../../models/app'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user'; | ||||||
|  | import getAppsMock from '../../../../../test/mocks/rest/api/v1/apps/get-apps.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps', () => { | ||||||
|  |   let currentUser, apps, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |     apps = await App.findAll(); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return all apps', async () => { | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/apps') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getAppsMock(apps); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return all apps filtered by name', async () => { | ||||||
|  |     const appsWithNameGit = apps.filter((app) => app.name.includes('Git')); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/apps?name=Git') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getAppsMock(appsWithNameGit); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return only the apps with triggers', async () => { | ||||||
|  |     const appsWithTriggers = apps.filter((app) => app.triggers?.length > 0); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/apps?onlyWithTriggers=true') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getAppsMock(appsWithTriggers); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return only the apps with actions', async () => { | ||||||
|  |     const appsWithActions = apps.filter((app) => app.actions?.length > 0); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/apps?onlyWithActions=true') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getAppsMock(appsWithActions); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,11 @@ | |||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  | import AppAuthClient from '../../../../models/app-auth-client.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const appAuthClient = await AppAuthClient.query() | ||||||
|  |     .findById(request.params.appAuthClientId) | ||||||
|  |     .where({ app_key: request.params.appKey, active: true }) | ||||||
|  |     .throwIfNotFound(); | ||||||
|  |  | ||||||
|  |   renderObject(response, appAuthClient); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,50 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import Crypto from 'crypto'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user.js'; | ||||||
|  | import getAppAuthClientMock from '../../../../../test/mocks/rest/api/v1/apps/get-auth-client.js'; | ||||||
|  | import { createAppAuthClient } from '../../../../../test/factories/app-auth-client.js'; | ||||||
|  | import * as license from '../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps/:appKey/auth-clients/:appAuthClientId', () => { | ||||||
|  |   let currentUser, currentAppAuthClient, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     currentAppAuthClient = await createAppAuthClient({ | ||||||
|  |       appKey: 'deepl', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return specified app auth client', async () => { | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/apps/deepl/auth-clients/${currentAppAuthClient.id}`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getAppAuthClientMock(currentAppAuthClient); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for not existing app auth client ID', async () => { | ||||||
|  |     const notExistingAppAuthClientUUID = Crypto.randomUUID(); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .get(`/api/v1/apps/deepl/auth-clients/${notExistingAppAuthClientUUID}`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return bad request response for invalid UUID', async () => { | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/apps/deepl/auth-clients/invalidAppAuthClientUUID') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(400); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,10 @@ | |||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  | import AppAuthClient from '../../../../models/app-auth-client.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const appAuthClients = await AppAuthClient.query() | ||||||
|  |     .where({ app_key: request.params.appKey, active: true }) | ||||||
|  |     .orderBy('created_at', 'desc'); | ||||||
|  |  | ||||||
|  |   renderObject(response, appAuthClients); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,42 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user.js'; | ||||||
|  | import getAuthClientsMock from '../../../../../test/mocks/rest/api/v1/apps/get-auth-clients.js'; | ||||||
|  | import { createAppAuthClient } from '../../../../../test/factories/app-auth-client.js'; | ||||||
|  | import * as license from '../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps/:appKey/auth-clients', () => { | ||||||
|  |   let currentUser, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return specified app auth client info', async () => { | ||||||
|  |     const appAuthClientOne = await createAppAuthClient({ | ||||||
|  |       appKey: 'deepl', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const appAuthClientTwo = await createAppAuthClient({ | ||||||
|  |       appKey: 'deepl', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/apps/deepl/auth-clients') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getAuthClientsMock([ | ||||||
|  |       appAuthClientTwo, | ||||||
|  |       appAuthClientOne, | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										8
									
								
								packages/backend/src/controllers/api/v1/apps/get-auth.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/backend/src/controllers/api/v1/apps/get-auth.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | import App from '../../../../models/app.js'; | ||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const auth = await App.findAuthByKey(request.params.appKey); | ||||||
|  |  | ||||||
|  |   renderObject(response, auth, { serializer: 'Auth' }); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,35 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import App from '../../../../models/app'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user'; | ||||||
|  | import getAuthMock from '../../../../../test/mocks/rest/api/v1/apps/get-auth.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps/:appKey/auth', () => { | ||||||
|  |   let currentUser, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the app auth info', async () => { | ||||||
|  |     const exampleApp = await App.findOneByKey('github'); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/apps/${exampleApp.key}/auth`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getAuthMock(exampleApp.auth); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for invalid app key', async () => { | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/apps/invalid-app-key/auth') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,15 @@ | |||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  | import AppConfig from '../../../../models/app-config.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const appConfig = await AppConfig.query() | ||||||
|  |     .withGraphFetched({ | ||||||
|  |       appAuthClients: true, | ||||||
|  |     }) | ||||||
|  |     .findOne({ | ||||||
|  |       key: request.params.appKey, | ||||||
|  |     }) | ||||||
|  |     .throwIfNotFound(); | ||||||
|  |  | ||||||
|  |   renderObject(response, appConfig); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,44 @@ | |||||||
|  | import { vi, describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user.js'; | ||||||
|  | import getAppConfigMock from '../../../../../test/mocks/rest/api/v1/apps/get-config.js'; | ||||||
|  | import { createAppConfig } from '../../../../../test/factories/app-config.js'; | ||||||
|  | import * as license from '../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps/:appKey/config', () => { | ||||||
|  |   let currentUser, appConfig, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |  | ||||||
|  |     appConfig = await createAppConfig({ | ||||||
|  |       key: 'deepl', | ||||||
|  |       allowCustomConnection: true, | ||||||
|  |       shared: true, | ||||||
|  |       disabled: false, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return specified app config info', async () => { | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/apps/${appConfig.key}/config`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getAppConfigMock(appConfig); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for not existing app key', async () => { | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/apps/not-existing-app-key/config') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,24 @@ | |||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  | import App from '../../../../models/app.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const app = await App.findOneByKey(request.params.appKey); | ||||||
|  |  | ||||||
|  |   const connections = await request.currentUser.authorizedConnections | ||||||
|  |     .clone() | ||||||
|  |     .select('connections.*') | ||||||
|  |     .withGraphFetched({ | ||||||
|  |       appConfig: true, | ||||||
|  |       appAuthClient: true, | ||||||
|  |     }) | ||||||
|  |     .fullOuterJoinRelated('steps') | ||||||
|  |     .where({ | ||||||
|  |       'connections.key': app.key, | ||||||
|  |       'connections.draft': false, | ||||||
|  |     }) | ||||||
|  |     .countDistinct('steps.flow_id as flowCount') | ||||||
|  |     .groupBy('connections.id') | ||||||
|  |     .orderBy('created_at', 'desc'); | ||||||
|  |  | ||||||
|  |   renderObject(response, connections); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,101 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user.js'; | ||||||
|  | import { createConnection } from '../../../../../test/factories/connection.js'; | ||||||
|  | import { createPermission } from '../../../../../test/factories/permission.js'; | ||||||
|  | import getConnectionsMock from '../../../../../test/mocks/rest/api/v1/apps/get-connections.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps/:appKey/connections', () => { | ||||||
|  |   let currentUser, currentUserRole, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     currentUserRole = await currentUser.$relatedQuery('role'); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the connections data of specified app for current user', async () => { | ||||||
|  |     const currentUserConnectionOne = await createConnection({ | ||||||
|  |       userId: currentUser.id, | ||||||
|  |       key: 'deepl', | ||||||
|  |       draft: false, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const currentUserConnectionTwo = await createConnection({ | ||||||
|  |       userId: currentUser.id, | ||||||
|  |       key: 'deepl', | ||||||
|  |       draft: false, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'read', | ||||||
|  |       subject: 'Connection', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: ['isCreator'], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/apps/deepl/connections') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getConnectionsMock([ | ||||||
|  |       currentUserConnectionTwo, | ||||||
|  |       currentUserConnectionOne, | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the connections data of specified app for another user', async () => { | ||||||
|  |     const anotherUser = await createUser(); | ||||||
|  |  | ||||||
|  |     const anotherUserConnectionOne = await createConnection({ | ||||||
|  |       userId: anotherUser.id, | ||||||
|  |       key: 'deepl', | ||||||
|  |       draft: false, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const anotherUserConnectionTwo = await createConnection({ | ||||||
|  |       userId: anotherUser.id, | ||||||
|  |       key: 'deepl', | ||||||
|  |       draft: false, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'read', | ||||||
|  |       subject: 'Connection', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: [], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/apps/deepl/connections') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getConnectionsMock([ | ||||||
|  |       anotherUserConnectionTwo, | ||||||
|  |       anotherUserConnectionOne, | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for invalid connection UUID', async () => { | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'update', | ||||||
|  |       subject: 'Connection', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: ['isCreator'], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/connections/invalid-connection-id/connections') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										23
									
								
								packages/backend/src/controllers/api/v1/apps/get-flows.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								packages/backend/src/controllers/api/v1/apps/get-flows.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  | import App from '../../../../models/app.js'; | ||||||
|  | import paginateRest from '../../../../helpers/pagination-rest.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const app = await App.findOneByKey(request.params.appKey); | ||||||
|  |  | ||||||
|  |   const flowsQuery = request.currentUser.authorizedFlows | ||||||
|  |     .clone() | ||||||
|  |     .joinRelated({ | ||||||
|  |       steps: true, | ||||||
|  |     }) | ||||||
|  |     .withGraphFetched({ | ||||||
|  |       steps: true, | ||||||
|  |     }) | ||||||
|  |     .where('steps.app_key', app.key) | ||||||
|  |     .orderBy('active', 'desc') | ||||||
|  |     .orderBy('updated_at', 'desc'); | ||||||
|  |  | ||||||
|  |   const flows = await paginateRest(flowsQuery, request.query.page); | ||||||
|  |  | ||||||
|  |   renderObject(response, flows); | ||||||
|  | }; | ||||||
							
								
								
									
										129
									
								
								packages/backend/src/controllers/api/v1/apps/get-flows.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								packages/backend/src/controllers/api/v1/apps/get-flows.test.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user.js'; | ||||||
|  | import { createFlow } from '../../../../../test/factories/flow.js'; | ||||||
|  | import { createStep } from '../../../../../test/factories/step.js'; | ||||||
|  | import { createPermission } from '../../../../../test/factories/permission.js'; | ||||||
|  | import getFlowsMock from '../../../../../test/mocks/rest/api/v1/flows/get-flows.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps/:appKey/flows', () => { | ||||||
|  |   let currentUser, currentUserRole, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     currentUserRole = await currentUser.$relatedQuery('role'); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the flows data of specified app for current user', async () => { | ||||||
|  |     const currentUserFlowOne = await createFlow({ userId: currentUser.id }); | ||||||
|  |  | ||||||
|  |     const triggerStepFlowOne = await createStep({ | ||||||
|  |       flowId: currentUserFlowOne.id, | ||||||
|  |       type: 'trigger', | ||||||
|  |       appKey: 'webhook', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const actionStepFlowOne = await createStep({ | ||||||
|  |       flowId: currentUserFlowOne.id, | ||||||
|  |       type: 'action', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const currentUserFlowTwo = await createFlow({ userId: currentUser.id }); | ||||||
|  |  | ||||||
|  |     await createStep({ | ||||||
|  |       flowId: currentUserFlowTwo.id, | ||||||
|  |       type: 'trigger', | ||||||
|  |       appKey: 'github', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createStep({ | ||||||
|  |       flowId: currentUserFlowTwo.id, | ||||||
|  |       type: 'action', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'read', | ||||||
|  |       subject: 'Flow', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: ['isCreator'], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/apps/webhook/flows') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getFlowsMock( | ||||||
|  |       [currentUserFlowOne], | ||||||
|  |       [triggerStepFlowOne, actionStepFlowOne] | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the flows data of specified app for another user', async () => { | ||||||
|  |     const anotherUser = await createUser(); | ||||||
|  |     const anotherUserFlowOne = await createFlow({ userId: anotherUser.id }); | ||||||
|  |  | ||||||
|  |     const triggerStepFlowOne = await createStep({ | ||||||
|  |       flowId: anotherUserFlowOne.id, | ||||||
|  |       type: 'trigger', | ||||||
|  |       appKey: 'webhook', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const actionStepFlowOne = await createStep({ | ||||||
|  |       flowId: anotherUserFlowOne.id, | ||||||
|  |       type: 'action', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const anotherUserFlowTwo = await createFlow({ userId: anotherUser.id }); | ||||||
|  |  | ||||||
|  |     await createStep({ | ||||||
|  |       flowId: anotherUserFlowTwo.id, | ||||||
|  |       type: 'trigger', | ||||||
|  |       appKey: 'github', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createStep({ | ||||||
|  |       flowId: anotherUserFlowTwo.id, | ||||||
|  |       type: 'action', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'read', | ||||||
|  |       subject: 'Flow', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: [], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/apps/webhook/flows') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getFlowsMock( | ||||||
|  |       [anotherUserFlowOne], | ||||||
|  |       [triggerStepFlowOne, actionStepFlowOne] | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for invalid app key', async () => { | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'read', | ||||||
|  |       subject: 'Flow', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: ['isCreator'], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/apps/invalid-app-key/flows') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,11 @@ | |||||||
|  | import App from '../../../../models/app.js'; | ||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const substeps = await App.findTriggerSubsteps( | ||||||
|  |     request.params.appKey, | ||||||
|  |     request.params.triggerKey | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   renderObject(response, substeps); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,52 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import App from '../../../../models/app'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user'; | ||||||
|  | import getTriggerSubstepsMock from '../../../../../test/mocks/rest/api/v1/apps/get-trigger-substeps.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps/:appKey/triggers/:triggerKey/substeps', () => { | ||||||
|  |   let currentUser, exampleApp, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |     exampleApp = await App.findOneByKey('github'); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the app auth info', async () => { | ||||||
|  |     const triggers = await App.findTriggersByKey('github'); | ||||||
|  |     const exampleTrigger = triggers.find( | ||||||
|  |       (trigger) => trigger.key === 'newIssues' | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     const endpointUrl = `/api/v1/apps/${exampleApp.key}/triggers/${exampleTrigger.key}/substeps`; | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(endpointUrl) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getTriggerSubstepsMock(exampleTrigger.substeps); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for invalid app key', async () => { | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/apps/invalid-app-key/triggers/invalid-trigger-key/substeps') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return empty array for invalid trigger key', async () => { | ||||||
|  |     const endpointUrl = `/api/v1/apps/${exampleApp.key}/triggers/invalid-trigger-key/substeps`; | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(endpointUrl) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     expect(response.body.data).toEqual([]); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | import App from '../../../../models/app.js'; | ||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const triggers = await App.findTriggersByKey(request.params.appKey); | ||||||
|  |  | ||||||
|  |   renderObject(response, triggers, { serializer: 'Trigger' }); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,35 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import App from '../../../../models/app'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user'; | ||||||
|  | import getTriggersMock from '../../../../../test/mocks/rest/api/v1/apps/get-triggers.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/apps/:appKey/triggers', () => { | ||||||
|  |   let currentUser, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the app triggers', async () => { | ||||||
|  |     const exampleApp = await App.findOneByKey('github'); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/apps/${exampleApp.key}/triggers`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = getTriggersMock(exampleApp.triggers); | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for invalid app key', async () => { | ||||||
|  |     await request(app) | ||||||
|  |       .get('/api/v1/apps/invalid-app-key/triggers') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,24 @@ | |||||||
|  | import appConfig from '../../../../config/app.js'; | ||||||
|  | import Config from '../../../../models/config.js'; | ||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const defaultConfig = { | ||||||
|  |     disableNotificationsPage: appConfig.disableNotificationsPage, | ||||||
|  |     disableFavicon: appConfig.disableFavicon, | ||||||
|  |     additionalDrawerLink: appConfig.additionalDrawerLink, | ||||||
|  |     additionalDrawerLinkText: appConfig.additionalDrawerLinkText, | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   let config = await Config.query().orderBy('key', 'asc'); | ||||||
|  |  | ||||||
|  |   config = config.reduce((computedConfig, configEntry) => { | ||||||
|  |     const { key, value } = configEntry; | ||||||
|  |  | ||||||
|  |     computedConfig[key] = value?.data; | ||||||
|  |  | ||||||
|  |     return computedConfig; | ||||||
|  |   }, defaultConfig); | ||||||
|  |  | ||||||
|  |   renderObject(response, config); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,51 @@ | |||||||
|  | import { vi, expect, describe, it } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import { createConfig } from '../../../../../test/factories/config.js'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import configMock from '../../../../../test/mocks/rest/api/v1/automatisch/config.js'; | ||||||
|  | import * as license from '../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/automatisch/config', () => { | ||||||
|  |   it('should return Automatisch config', async () => { | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const logoConfig = await createConfig({ | ||||||
|  |       key: 'logo.svgData', | ||||||
|  |       value: { data: '<svg>Sample</svg>' }, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const primaryDarkConfig = await createConfig({ | ||||||
|  |       key: 'palette.primary.dark', | ||||||
|  |       value: { data: '#001F52' }, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const primaryLightConfig = await createConfig({ | ||||||
|  |       key: 'palette.primary.light', | ||||||
|  |       value: { data: '#4286FF' }, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const primaryMainConfig = await createConfig({ | ||||||
|  |       key: 'palette.primary.main', | ||||||
|  |       value: { data: '#0059F7' }, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const titleConfig = await createConfig({ | ||||||
|  |       key: 'title', | ||||||
|  |       value: { data: 'Sample Title' }, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/automatisch/config') | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = configMock( | ||||||
|  |       logoConfig, | ||||||
|  |       primaryDarkConfig, | ||||||
|  |       primaryLightConfig, | ||||||
|  |       primaryMainConfig, | ||||||
|  |       titleConfig | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										13
									
								
								packages/backend/src/controllers/api/v1/automatisch/info.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/backend/src/controllers/api/v1/automatisch/info.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | import appConfig from '../../../../config/app.js'; | ||||||
|  | import { hasValidLicense } from '../../../../helpers/license.ee.js'; | ||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const info = { | ||||||
|  |     isCloud: appConfig.isCloud, | ||||||
|  |     isMation: appConfig.isMation, | ||||||
|  |     isEnterprise: await hasValidLicense(), | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   renderObject(response, info); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | import { vi, expect, describe, it } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import appConfig from '../../../../config/app.js'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import infoMock from '../../../../../test/mocks/rest/api/v1/automatisch/info.js'; | ||||||
|  | import * as license from '../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/automatisch/info', () => { | ||||||
|  |   it('should return Automatisch info', async () => { | ||||||
|  |     vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false); | ||||||
|  |     vi.spyOn(appConfig, 'isMation', 'get').mockReturnValue(false); | ||||||
|  |     vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/automatisch/info') | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = infoMock(); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,15 @@ | |||||||
|  | import { getLicense } from '../../../../helpers/license.ee.js'; | ||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const license = await getLicense(); | ||||||
|  |  | ||||||
|  |   const computedLicense = { | ||||||
|  |     id: license ? license.id : null, | ||||||
|  |     name: license ? license.name : null, | ||||||
|  |     expireAt: license ? license.expireAt : null, | ||||||
|  |     verified: license ? true : false, | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   renderObject(response, computedLicense); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,23 @@ | |||||||
|  | import { vi, expect, describe, it } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import licenseMock from '../../../../../test/mocks/rest/api/v1/automatisch/license.js'; | ||||||
|  | import * as license from '../../../../helpers/license.ee.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/automatisch/license', () => { | ||||||
|  |   it('should return Automatisch license info', async () => { | ||||||
|  |     vi.spyOn(license, 'getLicense').mockResolvedValue({ | ||||||
|  |       id: '123', | ||||||
|  |       name: 'license-name', | ||||||
|  |       expireAt: '2025-12-31T23:59:59Z', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get('/api/v1/automatisch/license') | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = licenseMock(); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,19 @@ | |||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  | import axios from '../../../../helpers/axios-with-proxy.js'; | ||||||
|  | import logger from '../../../../helpers/logger.js'; | ||||||
|  |  | ||||||
|  | const NOTIFICATIONS_URL = | ||||||
|  |   'https://notifications.automatisch.io/notifications.json'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   let notifications = []; | ||||||
|  |  | ||||||
|  |   try { | ||||||
|  |     const response = await axios.get(NOTIFICATIONS_URL); | ||||||
|  |     notifications = response.data; | ||||||
|  |   } catch (error) { | ||||||
|  |     logger.error('Error fetching notifications API endpoint!', error); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   renderObject(response, notifications); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | import { describe, it } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/automatisch/notifications', () => { | ||||||
|  |   it('should return Automatisch notifications', async () => { | ||||||
|  |     await request(app).get('/api/v1/automatisch/notifications').expect(200); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,14 @@ | |||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   let connection = await request.currentUser.authorizedConnections | ||||||
|  |     .clone() | ||||||
|  |     .findOne({ | ||||||
|  |       id: request.params.connectionId, | ||||||
|  |     }) | ||||||
|  |     .throwIfNotFound(); | ||||||
|  |  | ||||||
|  |   connection = await connection.testAndUpdateConnection(); | ||||||
|  |  | ||||||
|  |   renderObject(response, connection); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,123 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import Crypto from 'crypto'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user.js'; | ||||||
|  | import { createConnection } from '../../../../../test/factories/connection.js'; | ||||||
|  | import { createPermission } from '../../../../../test/factories/permission.js'; | ||||||
|  |  | ||||||
|  | describe('POST /api/v1/connections/:connectionId/test', () => { | ||||||
|  |   let currentUser, currentUserRole, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     currentUserRole = await currentUser.$relatedQuery('role'); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should update the connection as not verified for current user', async () => { | ||||||
|  |     const currentUserConnection = await createConnection({ | ||||||
|  |       userId: currentUser.id, | ||||||
|  |       key: 'deepl', | ||||||
|  |       verified: true, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'read', | ||||||
|  |       subject: 'Connection', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: ['isCreator'], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'update', | ||||||
|  |       subject: 'Connection', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: ['isCreator'], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .post(`/api/v1/connections/${currentUserConnection.id}/test`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     expect(response.body.data.verified).toEqual(false); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should update the connection as not verified for another user', async () => { | ||||||
|  |     const anotherUser = await createUser(); | ||||||
|  |  | ||||||
|  |     const anotherUserConnection = await createConnection({ | ||||||
|  |       userId: anotherUser.id, | ||||||
|  |       key: 'deepl', | ||||||
|  |       verified: true, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'read', | ||||||
|  |       subject: 'Connection', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: [], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'update', | ||||||
|  |       subject: 'Connection', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: [], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .post(`/api/v1/connections/${anotherUserConnection.id}/test`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     expect(response.body.data.verified).toEqual(false); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return not found response for not existing connection UUID', async () => { | ||||||
|  |     const notExistingConnectionUUID = Crypto.randomUUID(); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'read', | ||||||
|  |       subject: 'Connection', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: ['isCreator'], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'update', | ||||||
|  |       subject: 'Connection', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: ['isCreator'], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .post(`/api/v1/connections/${notExistingConnectionUUID}/test`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(404); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return bad request response for invalid UUID', async () => { | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'read', | ||||||
|  |       subject: 'Connection', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: ['isCreator'], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'update', | ||||||
|  |       subject: 'Connection', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: ['isCreator'], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await request(app) | ||||||
|  |       .post('/api/v1/connections/invalidConnectionUUID/test') | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(400); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -0,0 +1,20 @@ | |||||||
|  | import { renderObject } from '../../../../helpers/renderer.js'; | ||||||
|  | import paginateRest from '../../../../helpers/pagination-rest.js'; | ||||||
|  |  | ||||||
|  | export default async (request, response) => { | ||||||
|  |   const flowsQuery = request.currentUser.authorizedFlows | ||||||
|  |     .clone() | ||||||
|  |     .joinRelated({ | ||||||
|  |       steps: true, | ||||||
|  |     }) | ||||||
|  |     .withGraphFetched({ | ||||||
|  |       steps: true, | ||||||
|  |     }) | ||||||
|  |     .where('steps.connection_id', request.params.connectionId) | ||||||
|  |     .orderBy('active', 'desc') | ||||||
|  |     .orderBy('updated_at', 'desc'); | ||||||
|  |  | ||||||
|  |   const flows = await paginateRest(flowsQuery, request.query.page); | ||||||
|  |  | ||||||
|  |   renderObject(response, flows); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,128 @@ | |||||||
|  | import { describe, it, expect, beforeEach } from 'vitest'; | ||||||
|  | import request from 'supertest'; | ||||||
|  | import app from '../../../../app.js'; | ||||||
|  | import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js'; | ||||||
|  | import { createUser } from '../../../../../test/factories/user.js'; | ||||||
|  | import { createConnection } from '../../../../../test/factories/connection.js'; | ||||||
|  | import { createFlow } from '../../../../../test/factories/flow.js'; | ||||||
|  | import { createStep } from '../../../../../test/factories/step.js'; | ||||||
|  | import { createPermission } from '../../../../../test/factories/permission.js'; | ||||||
|  | import getFlowsMock from '../../../../../test/mocks/rest/api/v1/flows/get-flows.js'; | ||||||
|  |  | ||||||
|  | describe('GET /api/v1/connections/:connectionId/flows', () => { | ||||||
|  |   let currentUser, currentUserRole, token; | ||||||
|  |  | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     currentUser = await createUser(); | ||||||
|  |     currentUserRole = await currentUser.$relatedQuery('role'); | ||||||
|  |  | ||||||
|  |     token = createAuthTokenByUserId(currentUser.id); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the flows data of specified connection for current user', async () => { | ||||||
|  |     const currentUserFlowOne = await createFlow({ userId: currentUser.id }); | ||||||
|  |  | ||||||
|  |     const currentUserConnection = await createConnection({ | ||||||
|  |       userId: currentUser.id, | ||||||
|  |       key: 'webhook', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const triggerStepFlowOne = await createStep({ | ||||||
|  |       flowId: currentUserFlowOne.id, | ||||||
|  |       type: 'trigger', | ||||||
|  |       appKey: 'webhook', | ||||||
|  |       connectionId: currentUserConnection.id, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const actionStepFlowOne = await createStep({ | ||||||
|  |       flowId: currentUserFlowOne.id, | ||||||
|  |       type: 'action', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const currentUserFlowTwo = await createFlow({ userId: currentUser.id }); | ||||||
|  |  | ||||||
|  |     await createStep({ | ||||||
|  |       flowId: currentUserFlowTwo.id, | ||||||
|  |       type: 'trigger', | ||||||
|  |       appKey: 'github', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createStep({ | ||||||
|  |       flowId: currentUserFlowTwo.id, | ||||||
|  |       type: 'action', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'read', | ||||||
|  |       subject: 'Flow', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: ['isCreator'], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/connections/${currentUserConnection.id}/flows`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getFlowsMock( | ||||||
|  |       [currentUserFlowOne], | ||||||
|  |       [triggerStepFlowOne, actionStepFlowOne] | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   it('should return the flows data of specified connection for another user', async () => { | ||||||
|  |     const anotherUser = await createUser(); | ||||||
|  |     const anotherUserFlowOne = await createFlow({ userId: anotherUser.id }); | ||||||
|  |  | ||||||
|  |     const anotherUserConnection = await createConnection({ | ||||||
|  |       userId: anotherUser.id, | ||||||
|  |       key: 'webhook', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const triggerStepFlowOne = await createStep({ | ||||||
|  |       flowId: anotherUserFlowOne.id, | ||||||
|  |       type: 'trigger', | ||||||
|  |       appKey: 'webhook', | ||||||
|  |       connectionId: anotherUserConnection.id, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const actionStepFlowOne = await createStep({ | ||||||
|  |       flowId: anotherUserFlowOne.id, | ||||||
|  |       type: 'action', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const anotherUserFlowTwo = await createFlow({ userId: anotherUser.id }); | ||||||
|  |  | ||||||
|  |     await createStep({ | ||||||
|  |       flowId: anotherUserFlowTwo.id, | ||||||
|  |       type: 'trigger', | ||||||
|  |       appKey: 'github', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createStep({ | ||||||
|  |       flowId: anotherUserFlowTwo.id, | ||||||
|  |       type: 'action', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await createPermission({ | ||||||
|  |       action: 'read', | ||||||
|  |       subject: 'Flow', | ||||||
|  |       roleId: currentUserRole.id, | ||||||
|  |       conditions: [], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const response = await request(app) | ||||||
|  |       .get(`/api/v1/connections/${anotherUserConnection.id}/flows`) | ||||||
|  |       .set('Authorization', token) | ||||||
|  |       .expect(200); | ||||||
|  |  | ||||||
|  |     const expectedPayload = await getFlowsMock( | ||||||
|  |       [anotherUserFlowOne], | ||||||
|  |       [triggerStepFlowOne, actionStepFlowOne] | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     expect(response.body).toEqual(expectedPayload); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user